Skip to content

Commit d0663da

Browse files
committed
Implement a new CalendarKit based on libical
1 parent d6a9ed5 commit d0663da

20 files changed

+824
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/* CalendarKit - An ObjC wrapper around libical
2+
* Copyright (C) 2024 Hugo Melder
3+
*
4+
* SPDX-License-Identifier: MIT
5+
*/
6+
7+
#import <CalendarKit/ICALComponent.h>
8+
#import <CalendarKit/ICALError.h>
9+
#import <CalendarKit/ICALProperty.h>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/* CalendarKit - An ObjC wrapper around libical
2+
* Copyright (C) 2024 Hugo Melder
3+
*
4+
* SPDX-License-Identifier: MIT
5+
*/
6+
7+
#import <Foundation/NSData.h>
8+
#import <Foundation/NSEnumerator.h>
9+
#import <Foundation/NSError.h>
10+
#import <Foundation/NSObjCRuntime.h>
11+
#import <Foundation/NSObject.h>
12+
#import <Foundation/NSString.h>
13+
14+
// Class reference to avoid cyclic dependency
15+
@class ICALProperty;
16+
17+
NS_ASSUME_NONNULL_BEGIN
18+
19+
typedef NS_ENUM(NSUInteger, ICALComponentKind) {
20+
ICALComponentKindPlaceholder,
21+
/// Used to select all components
22+
ICALComponentKindAny,
23+
ICALComponentKindXROOT,
24+
/// MIME attached data, returned by parser
25+
ICALComponentKindXATTACH,
26+
ICALComponentKindVEVENT,
27+
ICALComponentKindVTODO,
28+
ICALComponentKindVJOURNAL,
29+
ICALComponentKindVCALENDAR,
30+
ICALComponentKindVAGENDA,
31+
ICALComponentKindVFREEBUSY,
32+
ICALComponentKindVALARM,
33+
ICALComponentKindXAUDIOALARM,
34+
ICALComponentKindXDISPLAYALARM,
35+
ICALComponentKindXEMAILALARM,
36+
ICALComponentKindXPROCEDUREALARM,
37+
ICALComponentKindVTIMEZONE,
38+
ICALComponentKindXSTANDARD,
39+
ICALComponentKindXDAYLIGHT,
40+
ICALComponentKindX,
41+
ICALComponentKindVSCHEDULE,
42+
ICALComponentKindVQUERY,
43+
ICALComponentKindVREPLY,
44+
ICALComponentKindVCAR,
45+
ICALComponentKindVCOMMAND,
46+
ICALComponentKindXLICINVALID,
47+
ICALComponentKindXLICMIMEPART,
48+
ICALComponentKindVAVAILABILITY,
49+
ICALComponentKindXAVAILABLE,
50+
ICALComponentKindVPOLL,
51+
ICALComponentKindVVOTER,
52+
ICALComponentKindXVOTE,
53+
ICALComponentKindVPATCH,
54+
ICALComponentKindXPATCH
55+
};
56+
57+
@interface ICALComponent : NSObject <NSCopying> {
58+
void *_handle;
59+
ICALComponent *_Nullable _root;
60+
}
61+
62+
+ (instancetype)componentWithData:(NSData *)data error:(NSError **)error;
63+
64+
- (instancetype)initWithData:(NSData *)data error:(NSError **)error;
65+
66+
- (ICALComponentKind)kind;
67+
68+
- (NSString *_Nullable)uid;
69+
70+
- (NSString *_Nullable)summary;
71+
72+
- (id)copyWithZone:(NSZone *)zone;
73+
74+
- (NSInteger)numberOfChildren;
75+
- (NSInteger)numberOfProperties;
76+
77+
- (void)enumerateComponentsUsingBlock:(void (^)(ICALComponent *component, BOOL *stop))block;
78+
- (void)enumeratePropertiesUsingBlock:(void (^)(ICALProperty *property, BOOL *stop))block;
79+
80+
@end
81+
82+
NS_ASSUME_NONNULL_END
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/* CalendarKit - An ObjC wrapper around libical
2+
* Copyright (C) 2024 Hugo Melder
3+
*
4+
* SPDX-License-Identifier: MIT
5+
*/
6+
7+
#import <Foundation/NSDictionary.h>
8+
#import <Foundation/NSError.h>
9+
#import <Foundation/NSObjCRuntime.h>
10+
#import <Foundation/NSString.h>
11+
12+
NS_ASSUME_NONNULL_BEGIN
13+
14+
extern NSString *const ICALErrorDomain;
15+
16+
#define ICAL_FAST_ERROR(err, errorCode, description, ...) \
17+
do { \
18+
if (err) { \
19+
NSString *message = [NSString stringWithFormat:description, ##__VA_ARGS__]; \
20+
*err = [NSError errorWithDomain:ICALErrorDomain \
21+
code:errorCode \
22+
userInfo:@{NSLocalizedDescriptionKey : message}]; \
23+
} \
24+
} while (0)
25+
26+
typedef NS_ENUM(NSUInteger, ICALError) {
27+
ICALErrorNone = 0,
28+
ICALErrorBadArgument,
29+
ICALErrorConstruction,
30+
ICALErrorAllocation,
31+
ICALErrorMalformedData,
32+
ICALErrorParse,
33+
ICALErrorInternal,
34+
ICALErrorFile,
35+
ICALErrorUsage,
36+
ICALUnimplementedError,
37+
ICALUnknownError
38+
};
39+
40+
NS_ASSUME_NONNULL_END
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/* CalendarKit - An ObjC wrapper around libical
2+
* Copyright (C) 2024 Hugo Melder
3+
*
4+
* SPDX-License-Identifier: MIT
5+
*/
6+
7+
#import <Foundation/NSObjCRuntime.h>
8+
#import <Foundation/NSObject.h>
9+
#import <Foundation/NSString.h>
10+
11+
NS_ASSUME_NONNULL_BEGIN
12+
13+
@interface ICALParameter : NSObject {
14+
void *_handle;
15+
}
16+
17+
- (NSString *_Nullable)ianaName;
18+
- (NSString *_Nullable)ianaValue;
19+
- (NSString *)icalString;
20+
21+
@end
22+
23+
NS_ASSUME_NONNULL_END
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/* CalendarKit - An ObjC wrapper around libical
2+
* Copyright (C) 2024 Hugo Melder
3+
*
4+
* SPDX-License-Identifier: MIT
5+
*/
6+
7+
#import <Foundation/NSObjCRuntime.h>
8+
#import <Foundation/NSObject.h>
9+
#import <Foundation/NSString.h>
10+
11+
#import <CalendarKit/ICALComponent.h>
12+
#import <CalendarKit/ICALParameter.h>
13+
14+
NS_ASSUME_NONNULL_BEGIN
15+
16+
@interface ICALProperty : NSObject {
17+
// Strong reference to owner
18+
ICALComponent *_owner;
19+
// underlying libical handle. Lifetime tied to owner.
20+
void *_handle;
21+
}
22+
23+
- (NSString *)name;
24+
- (NSString *)value;
25+
26+
- (NSInteger)numberOfParameters;
27+
- (void)enumerateParametersUsingBlock:(void (^)(ICALParameter *parameter, BOOL *stop))block;
28+
29+
@end
30+
31+
NS_ASSUME_NONNULL_END
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/* CalendarKit - An ObjC wrapper around libical
2+
* Copyright (C) 2024 Hugo Melder
3+
*
4+
* SPDX-License-Identifier: MIT
5+
*/
6+
7+
#import "CalendarKit/ICALComponent.h"
8+
#import <libical/icalcomponent.h>
9+
10+
@interface ICALComponent (Private)
11+
12+
- (instancetype)initWithHandle:(icalcomponent *)handle;
13+
- (instancetype)initWithHandle:(icalcomponent *)handle root:(ICALComponent *)root;
14+
15+
- (icalcomponent *)handle;
16+
17+
@end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/* CalendarKit - An ObjC wrapper around libical
2+
* Copyright (C) 2024 Hugo Melder
3+
*
4+
* SPDX-License-Identifier: MIT
5+
*/
6+
7+
#include "libical/ical.h"
8+
#import <assert.h>
9+
#import <libical/icalcomponent.h>
10+
#import <libical/icalenums.h>
11+
#import <libical/icalerror.h>
12+
#import <libical/icalparser.h>
13+
14+
#import <Foundation/NSException.h>
15+
16+
#import "CalendarKit/ICALComponent.h"
17+
#import "CalendarKit/ICALError.h"
18+
19+
#import "ICALComponent+Private.h"
20+
#import "ICALProperty+Private.h"
21+
22+
// Make sure that ICALComponentKind is in sync with icalcomponent_kind
23+
// so that we can just cast it.
24+
static_assert(ICAL_XPATCH_COMPONENT == ICALComponentKindXPATCH,
25+
"icalcomponent_kind enumeration is not in sync!");
26+
27+
@implementation ICALComponent (Private)
28+
29+
- (instancetype)initWithHandle:(icalcomponent *)handle {
30+
self = [super init];
31+
if (self) {
32+
_handle = handle;
33+
}
34+
35+
return self;
36+
}
37+
- (instancetype)initWithHandle:(icalcomponent *)handle root:(ICALComponent *)root {
38+
self = [super init];
39+
if (self) {
40+
_handle = handle;
41+
_root = root;
42+
}
43+
44+
return self;
45+
}
46+
47+
- (icalcomponent *)handle {
48+
return _handle;
49+
}
50+
51+
@end
52+
53+
@implementation ICALComponent
54+
55+
+ (instancetype)componentWithData:(NSData *)data error:(NSError **)error {
56+
return [[ICALComponent alloc] initWithData:data error:error];
57+
}
58+
59+
- (instancetype)initWithData:(NSData *)data error:(NSError **)error {
60+
NSAssert(data, @"Data is valid");
61+
62+
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
63+
64+
icalcomponent *parsed = icalparser_parse_string([string UTF8String]);
65+
// Some error occured during parsing
66+
if (!parsed) {
67+
if (error) {
68+
ICALError errorCode = (ICALError) icalerrno;
69+
NSString *message = [NSString stringWithUTF8String:icalerror_strerror(icalerrno)];
70+
*error = [NSError errorWithDomain:ICALErrorDomain
71+
code:errorCode
72+
userInfo:@{NSLocalizedDescriptionKey : message}];
73+
}
74+
icalerror_clear_errno();
75+
return nil;
76+
}
77+
78+
return [self initWithHandle:parsed];
79+
}
80+
81+
- (ICALComponentKind)kind {
82+
return (ICALComponentKind) icalcomponent_isa(_handle);
83+
}
84+
85+
- (NSString *_Nullable)uid {
86+
const char *uid = icalcomponent_get_uid(_handle);
87+
if (!uid) {
88+
return nil;
89+
}
90+
91+
return [NSString stringWithUTF8String:uid];
92+
}
93+
94+
- (NSString *_Nullable)summary {
95+
const char *summary = icalcomponent_get_summary(_handle);
96+
if (!summary) {
97+
return nil;
98+
}
99+
100+
return [NSString stringWithUTF8String:summary];
101+
}
102+
103+
- (NSInteger)numberOfChildren {
104+
return icalcomponent_count_components(_handle, ICAL_ANY_COMPONENT);
105+
}
106+
107+
- (NSInteger)numberOfProperties {
108+
return icalcomponent_count_properties(_handle, ICAL_ANY_PROPERTY);
109+
}
110+
111+
- (void)enumerateComponentsUsingBlock:(void (^)(ICALComponent *component, BOOL *stop))block {
112+
icalcompiter iter = icalcomponent_begin_component(_handle, ICAL_ANY_COMPONENT);
113+
114+
BOOL stop = NO;
115+
116+
for (; icalcompiter_deref(&iter) != 0 && stop == NO; icalcompiter_next(&iter)) {
117+
ICALComponent *cur = [[ICALComponent alloc] initWithHandle:icalcompiter_deref(&iter)
118+
root:self];
119+
block(cur, &stop);
120+
}
121+
}
122+
123+
- (void)enumeratePropertiesUsingBlock:(void (^)(ICALProperty *property, BOOL *stop))block {
124+
icalproperty *cur = icalcomponent_get_first_property(_handle, ICAL_ANY_PROPERTY);
125+
126+
BOOL stop = NO;
127+
while (cur != 0 && stop == NO) {
128+
ICALProperty *obj = [[ICALProperty alloc] initWithHandle:cur owner:self];
129+
block(obj, &stop);
130+
cur = icalcomponent_get_next_property(_handle, ICAL_ANY_PROPERTY);
131+
}
132+
}
133+
134+
- (id)copyWithZone:(NSZone *)zone {
135+
icalcomponent *cloned = icalcomponent_new_clone(_handle);
136+
return [[ICALComponent alloc] initWithHandle:cloned];
137+
}
138+
139+
- (void)dealloc {
140+
if (!_root) {
141+
icalcomponent_free(_handle);
142+
}
143+
}
144+
145+
@end
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/* CalendarKit - An ObjC wrapper around libical
2+
* Copyright (C) 2024 Hugo Melder
3+
*
4+
* SPDX-License-Identifier: MIT
5+
*/
6+
7+
#import "CalendarKit/ICALError.h"
8+
9+
NSString *const ICALErrorDomain = @"ICALErrorDomain";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/* CalendarKit - An ObjC wrapper around libical
2+
* Copyright (C) 2024 Hugo Melder
3+
*
4+
* SPDX-License-Identifier: MIT
5+
*/
6+
7+
#import <libical/icalparameter.h>
8+
9+
#import <CalendarKit/ICALParameter.h>
10+
11+
NS_ASSUME_NONNULL_BEGIN
12+
13+
@interface ICALParameter (Private)
14+
15+
/**
16+
* Clone the underlying icalparameter and return an initialised ICALParameter
17+
* instance.
18+
*/
19+
- (instancetype)initWithHandle:(icalparameter *)handle;
20+
21+
@end
22+
23+
NS_ASSUME_NONNULL_END

0 commit comments

Comments
 (0)