Skip to content

Commit

Permalink
Implement a new CalendarKit based on libical
Browse files Browse the repository at this point in the history
  • Loading branch information
hmelder committed Oct 27, 2024
1 parent d6a9ed5 commit d0663da
Show file tree
Hide file tree
Showing 20 changed files with 824 additions and 2 deletions.
9 changes: 9 additions & 0 deletions Libraries/CalendarKit/CalendarKit/CalendarKit.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/* CalendarKit - An ObjC wrapper around libical
* Copyright (C) 2024 Hugo Melder
*
* SPDX-License-Identifier: MIT
*/

#import <CalendarKit/ICALComponent.h>
#import <CalendarKit/ICALError.h>
#import <CalendarKit/ICALProperty.h>
82 changes: 82 additions & 0 deletions Libraries/CalendarKit/CalendarKit/ICALComponent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/* CalendarKit - An ObjC wrapper around libical
* Copyright (C) 2024 Hugo Melder
*
* SPDX-License-Identifier: MIT
*/

#import <Foundation/NSData.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSError.h>
#import <Foundation/NSObjCRuntime.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSString.h>

// Class reference to avoid cyclic dependency
@class ICALProperty;

NS_ASSUME_NONNULL_BEGIN

typedef NS_ENUM(NSUInteger, ICALComponentKind) {
ICALComponentKindPlaceholder,
/// Used to select all components
ICALComponentKindAny,
ICALComponentKindXROOT,
/// MIME attached data, returned by parser
ICALComponentKindXATTACH,
ICALComponentKindVEVENT,
ICALComponentKindVTODO,
ICALComponentKindVJOURNAL,
ICALComponentKindVCALENDAR,
ICALComponentKindVAGENDA,
ICALComponentKindVFREEBUSY,
ICALComponentKindVALARM,
ICALComponentKindXAUDIOALARM,
ICALComponentKindXDISPLAYALARM,
ICALComponentKindXEMAILALARM,
ICALComponentKindXPROCEDUREALARM,
ICALComponentKindVTIMEZONE,
ICALComponentKindXSTANDARD,
ICALComponentKindXDAYLIGHT,
ICALComponentKindX,
ICALComponentKindVSCHEDULE,
ICALComponentKindVQUERY,
ICALComponentKindVREPLY,
ICALComponentKindVCAR,
ICALComponentKindVCOMMAND,
ICALComponentKindXLICINVALID,
ICALComponentKindXLICMIMEPART,
ICALComponentKindVAVAILABILITY,
ICALComponentKindXAVAILABLE,
ICALComponentKindVPOLL,
ICALComponentKindVVOTER,
ICALComponentKindXVOTE,
ICALComponentKindVPATCH,
ICALComponentKindXPATCH
};

@interface ICALComponent : NSObject <NSCopying> {
void *_handle;
ICALComponent *_Nullable _root;
}

+ (instancetype)componentWithData:(NSData *)data error:(NSError **)error;

- (instancetype)initWithData:(NSData *)data error:(NSError **)error;

- (ICALComponentKind)kind;

- (NSString *_Nullable)uid;

- (NSString *_Nullable)summary;

- (id)copyWithZone:(NSZone *)zone;

- (NSInteger)numberOfChildren;
- (NSInteger)numberOfProperties;

- (void)enumerateComponentsUsingBlock:(void (^)(ICALComponent *component, BOOL *stop))block;
- (void)enumeratePropertiesUsingBlock:(void (^)(ICALProperty *property, BOOL *stop))block;

@end

NS_ASSUME_NONNULL_END
40 changes: 40 additions & 0 deletions Libraries/CalendarKit/CalendarKit/ICALError.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/* CalendarKit - An ObjC wrapper around libical
* Copyright (C) 2024 Hugo Melder
*
* SPDX-License-Identifier: MIT
*/

#import <Foundation/NSDictionary.h>
#import <Foundation/NSError.h>
#import <Foundation/NSObjCRuntime.h>
#import <Foundation/NSString.h>

NS_ASSUME_NONNULL_BEGIN

extern NSString *const ICALErrorDomain;

#define ICAL_FAST_ERROR(err, errorCode, description, ...) \
do { \
if (err) { \
NSString *message = [NSString stringWithFormat:description, ##__VA_ARGS__]; \
*err = [NSError errorWithDomain:ICALErrorDomain \
code:errorCode \
userInfo:@{NSLocalizedDescriptionKey : message}]; \
} \
} while (0)

typedef NS_ENUM(NSUInteger, ICALError) {
ICALErrorNone = 0,
ICALErrorBadArgument,
ICALErrorConstruction,
ICALErrorAllocation,
ICALErrorMalformedData,
ICALErrorParse,
ICALErrorInternal,
ICALErrorFile,
ICALErrorUsage,
ICALUnimplementedError,
ICALUnknownError
};

NS_ASSUME_NONNULL_END
23 changes: 23 additions & 0 deletions Libraries/CalendarKit/CalendarKit/ICALParameter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* CalendarKit - An ObjC wrapper around libical
* Copyright (C) 2024 Hugo Melder
*
* SPDX-License-Identifier: MIT
*/

#import <Foundation/NSObjCRuntime.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSString.h>

NS_ASSUME_NONNULL_BEGIN

@interface ICALParameter : NSObject {
void *_handle;
}

- (NSString *_Nullable)ianaName;
- (NSString *_Nullable)ianaValue;
- (NSString *)icalString;

@end

NS_ASSUME_NONNULL_END
31 changes: 31 additions & 0 deletions Libraries/CalendarKit/CalendarKit/ICALProperty.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* CalendarKit - An ObjC wrapper around libical
* Copyright (C) 2024 Hugo Melder
*
* SPDX-License-Identifier: MIT
*/

#import <Foundation/NSObjCRuntime.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSString.h>

#import <CalendarKit/ICALComponent.h>
#import <CalendarKit/ICALParameter.h>

NS_ASSUME_NONNULL_BEGIN

@interface ICALProperty : NSObject {
// Strong reference to owner
ICALComponent *_owner;
// underlying libical handle. Lifetime tied to owner.
void *_handle;
}

- (NSString *)name;
- (NSString *)value;

- (NSInteger)numberOfParameters;
- (void)enumerateParametersUsingBlock:(void (^)(ICALParameter *parameter, BOOL *stop))block;

@end

NS_ASSUME_NONNULL_END
17 changes: 17 additions & 0 deletions Libraries/CalendarKit/Source/ICALComponent+Private.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/* CalendarKit - An ObjC wrapper around libical
* Copyright (C) 2024 Hugo Melder
*
* SPDX-License-Identifier: MIT
*/

#import "CalendarKit/ICALComponent.h"
#import <libical/icalcomponent.h>

@interface ICALComponent (Private)

- (instancetype)initWithHandle:(icalcomponent *)handle;
- (instancetype)initWithHandle:(icalcomponent *)handle root:(ICALComponent *)root;

- (icalcomponent *)handle;

@end
145 changes: 145 additions & 0 deletions Libraries/CalendarKit/Source/ICALComponent.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/* CalendarKit - An ObjC wrapper around libical
* Copyright (C) 2024 Hugo Melder
*
* SPDX-License-Identifier: MIT
*/

#include "libical/ical.h"
#import <assert.h>
#import <libical/icalcomponent.h>
#import <libical/icalenums.h>
#import <libical/icalerror.h>
#import <libical/icalparser.h>

#import <Foundation/NSException.h>

#import "CalendarKit/ICALComponent.h"
#import "CalendarKit/ICALError.h"

#import "ICALComponent+Private.h"
#import "ICALProperty+Private.h"

// Make sure that ICALComponentKind is in sync with icalcomponent_kind
// so that we can just cast it.
static_assert(ICAL_XPATCH_COMPONENT == ICALComponentKindXPATCH,
"icalcomponent_kind enumeration is not in sync!");

@implementation ICALComponent (Private)

- (instancetype)initWithHandle:(icalcomponent *)handle {
self = [super init];
if (self) {
_handle = handle;
}

return self;
}
- (instancetype)initWithHandle:(icalcomponent *)handle root:(ICALComponent *)root {
self = [super init];
if (self) {
_handle = handle;
_root = root;
}

return self;
}

- (icalcomponent *)handle {
return _handle;
}

@end

@implementation ICALComponent

+ (instancetype)componentWithData:(NSData *)data error:(NSError **)error {
return [[ICALComponent alloc] initWithData:data error:error];
}

- (instancetype)initWithData:(NSData *)data error:(NSError **)error {
NSAssert(data, @"Data is valid");

NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

icalcomponent *parsed = icalparser_parse_string([string UTF8String]);
// Some error occured during parsing
if (!parsed) {
if (error) {
ICALError errorCode = (ICALError) icalerrno;
NSString *message = [NSString stringWithUTF8String:icalerror_strerror(icalerrno)];
*error = [NSError errorWithDomain:ICALErrorDomain
code:errorCode
userInfo:@{NSLocalizedDescriptionKey : message}];
}
icalerror_clear_errno();
return nil;
}

return [self initWithHandle:parsed];
}

- (ICALComponentKind)kind {
return (ICALComponentKind) icalcomponent_isa(_handle);
}

- (NSString *_Nullable)uid {
const char *uid = icalcomponent_get_uid(_handle);
if (!uid) {
return nil;
}

return [NSString stringWithUTF8String:uid];
}

- (NSString *_Nullable)summary {
const char *summary = icalcomponent_get_summary(_handle);
if (!summary) {
return nil;
}

return [NSString stringWithUTF8String:summary];
}

- (NSInteger)numberOfChildren {
return icalcomponent_count_components(_handle, ICAL_ANY_COMPONENT);
}

- (NSInteger)numberOfProperties {
return icalcomponent_count_properties(_handle, ICAL_ANY_PROPERTY);
}

- (void)enumerateComponentsUsingBlock:(void (^)(ICALComponent *component, BOOL *stop))block {
icalcompiter iter = icalcomponent_begin_component(_handle, ICAL_ANY_COMPONENT);

BOOL stop = NO;

for (; icalcompiter_deref(&iter) != 0 && stop == NO; icalcompiter_next(&iter)) {
ICALComponent *cur = [[ICALComponent alloc] initWithHandle:icalcompiter_deref(&iter)
root:self];
block(cur, &stop);
}
}

- (void)enumeratePropertiesUsingBlock:(void (^)(ICALProperty *property, BOOL *stop))block {
icalproperty *cur = icalcomponent_get_first_property(_handle, ICAL_ANY_PROPERTY);

BOOL stop = NO;
while (cur != 0 && stop == NO) {
ICALProperty *obj = [[ICALProperty alloc] initWithHandle:cur owner:self];
block(obj, &stop);
cur = icalcomponent_get_next_property(_handle, ICAL_ANY_PROPERTY);
}
}

- (id)copyWithZone:(NSZone *)zone {
icalcomponent *cloned = icalcomponent_new_clone(_handle);
return [[ICALComponent alloc] initWithHandle:cloned];
}

- (void)dealloc {
if (!_root) {
icalcomponent_free(_handle);
}
}

@end
9 changes: 9 additions & 0 deletions Libraries/CalendarKit/Source/ICALError.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/* CalendarKit - An ObjC wrapper around libical
* Copyright (C) 2024 Hugo Melder
*
* SPDX-License-Identifier: MIT
*/

#import "CalendarKit/ICALError.h"

NSString *const ICALErrorDomain = @"ICALErrorDomain";
23 changes: 23 additions & 0 deletions Libraries/CalendarKit/Source/ICALParameter+Private.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* CalendarKit - An ObjC wrapper around libical
* Copyright (C) 2024 Hugo Melder
*
* SPDX-License-Identifier: MIT
*/

#import <libical/icalparameter.h>

#import <CalendarKit/ICALParameter.h>

NS_ASSUME_NONNULL_BEGIN

@interface ICALParameter (Private)

/**
* Clone the underlying icalparameter and return an initialised ICALParameter
* instance.
*/
- (instancetype)initWithHandle:(icalparameter *)handle;

@end

NS_ASSUME_NONNULL_END
Loading

0 comments on commit d0663da

Please sign in to comment.