├── .gitmodules
├── tomlutil.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── xcshareddata
│ └── xcschemes
│ │ └── tomlutil.xcscheme
└── project.pbxproj
├── .gitignore
├── tomlutil
├── Info.plist
├── LMPTOMLSerialization.h
├── LMP_toml_visitors.h
├── LMPTOMLSerialization.mm
└── main.m
├── toml-examples
├── wrong.toml
├── example-v0.3.0.toml
└── example-v0.4.0.toml
├── LICENSE.txt
└── README.md
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "submodules/toml11"]
2 | path = submodules/toml11
3 | url = git@github.com:ToruNiina/toml11.git
4 |
--------------------------------------------------------------------------------
/tomlutil.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/tomlutil.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | build/
3 | *.pbxuser
4 | !default.pbxuser
5 | *.mode1v3
6 | !default.mode1v3
7 | *.mode2v3
8 | !default.mode2v3
9 | *.perspectivev3
10 | !default.perspectivev3
11 | xcuserdata
12 | *.xccheckout
13 | *.moved-aside
14 | DerivedData
15 | *.hmap
16 | *.ipa
17 | *.xcuserstate
18 |
19 | #OS X Stuff
20 | .localized
21 | .DS_Store
22 | *.zip
23 |
24 | # Pods
25 | Pods/
26 |
27 | # Editors
28 | .idea
29 | *.swp
30 |
31 | # Keys
32 | *.pem
33 | *.pkey
34 | *.p12
35 |
36 |
--------------------------------------------------------------------------------
/tomlutil/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleIdentifier
6 | $(PRODUCT_BUNDLE_IDENTIFIER)
7 | CFBundleShortVersionString
8 | 2.0.0
9 | LMPToml11Version
10 | 3.7.1
11 | LSMinimumSystemVersion
12 | ${MACOSX_DEPLOYMENT_TARGET}
13 |
14 |
15 |
--------------------------------------------------------------------------------
/toml-examples/wrong.toml:
--------------------------------------------------------------------------------
1 | ################################################################################
2 | ## Comment
3 |
4 | # Speak your mind with the hash symbol. They go from the symbol to the end of
5 | # the line.
6 |
7 |
8 | ################################################################################
9 | ## Table
10 |
11 | # Tables (also known as hash tables or dictionaries) are collections of
12 | # key/value pairs. They appear in square brackets on a line by themselves.
13 |
14 | asdfpoin1!@ j= ;alskjfasdf
15 |
16 | [table]
17 |
18 | key = "value" # Yeah, you can do this.
19 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018 Dominik Wagner.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/tomlutil/LMPTOMLSerialization.h:
--------------------------------------------------------------------------------
1 | // LMPTOMLSerialization.h
2 | //
3 | // Created by dom on 10/20/18.
4 | // Copyright © 2018 Lone Monkey Productions. All rights reserved.
5 | //
6 |
7 | #import
8 |
9 | extern NSErrorDomain const LMPTOMLErrorDomain;
10 |
11 | extern NSString * const LMPTOMLErrorInfoKeyColorizedReason;
12 |
13 | extern NSString * const LMPTOMLOptionKeySourceFileURL;
14 |
15 | @interface LMPTOMLSerialization : NSObject
16 |
17 | /**
18 | Generate a Foundation Dictionary from TOML data.
19 |
20 | @param data NSData representing a TOML file
21 | @param options NSDictionary with options for the toml parsing
22 | @param error helpful information if the parsing fails
23 | @return NSDictionary representing the contents of the TOML file. Note that given dates will be represented as NSDateComponents, use +serializableObjectWithTOMLObject: to convert those to RFC3339 strings that can be used in JSON or PropertyList serializations.
24 | */
25 | + (NSDictionary *)TOMLObjectWithData:(NSData *)data options:(NSDictionary *)options error:(NSError **)error;
26 |
27 | + (NSDictionary *)TOMLObjectWithData:(NSData *)data error:(NSError **)error;
28 |
29 |
30 | /**
31 | Generates NSData representation of the TOMLObject. The representation is UTF8 and can be stored directly as a TOML file.
32 |
33 | Note that roundtripping is a lossy opreation, as all comments are stripped, the allowed number formats are reduced to canonical ones and doubles might lose or gain unwanted precision.
34 |
35 | @param tomlObject Foundation Object consisting of TOML serializable objects. In addition to plist objects this contains NSDateComponent objects with y-m-d filled, h-m-s-[nanoseconds] filled, all fields filled, or all fields + timezone filled.
36 | @param error helpful information if generation fails
37 | @return NSData representing the object.
38 | */
39 | + (NSData *)dataWithTOMLObject:(NSDictionary *)tomlObject error:(NSError **)error;
40 |
41 | /**
42 | Takes a Dictionary representing a TOML file and translates the NSDateComponents into RFC339 strings to be able to be serialized in JSON or PropertyLists
43 |
44 | @param tomlObject foundation dictionary consisting of TOML serializable objects.
45 | @return NSDictionary containing property list serializable objects.
46 | */
47 | + (NSDictionary *)serializableObjectWithTOMLObject:(NSDictionary *)tomlObject;
48 |
49 | @end
50 |
--------------------------------------------------------------------------------
/toml-examples/example-v0.3.0.toml:
--------------------------------------------------------------------------------
1 | # Comment
2 | # I am a comment. Hear me roar. Roar.
3 |
4 | # Table
5 | # Tables (also known as hash tables or dictionaries) are collections of key/value pairs.
6 | # They appear in square brackets on a line by themselves.
7 |
8 | [Table]
9 |
10 | key = "value" # Yeah, you can do this.
11 |
12 | # Nested tables are denoted by table names with dots in them. Name your tables whatever crap you please, just don't use #, ., [ or ].
13 |
14 | [dog.tater]
15 | type = "pug"
16 |
17 | # You don't need to specify all the super-tables if you don't want to. TOML knows how to do it for you.
18 |
19 | # [x] you
20 | # [x.y] don't
21 | # [x.y.z] need these
22 | [x.y.z.w] # for this to work
23 |
24 | # String
25 | # There are four ways to express strings: basic, multi-line basic, literal, and multi-line literal.
26 | # All strings must contain only valid UTF-8 characters.
27 |
28 | [String]
29 | basic = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."
30 |
31 | [String.Multiline]
32 |
33 | # The following strings are byte-for-byte equivalent:
34 | key1 = "One\nTwo"
35 | key2 = """One\nTwo"""
36 | key3 = """
37 | One
38 | Two"""
39 |
40 | [String.Multilined.Singleline]
41 |
42 | # The following strings are byte-for-byte equivalent:
43 | key1 = "The quick brown fox jumps over the lazy dog."
44 |
45 | key2 = """
46 | The quick brown \
47 |
48 |
49 | fox jumps over \
50 | the lazy dog."""
51 |
52 | key3 = """\
53 | The quick brown \
54 | fox jumps over \
55 | the lazy dog.\
56 | """
57 |
58 | [String.Literal]
59 |
60 | # What you see is what you get.
61 | winpath = 'C:\Users\nodejs\templates'
62 | winpath2 = '\\ServerX\admin$\system32\'
63 | quoted = 'Tom "Dubs" Preston-Werner'
64 | regex = '<\i\c*\s*>'
65 |
66 |
67 | [String.Literal.Multiline]
68 |
69 | regex2 = '''I [dw]on't need \d{2} apples'''
70 | lines = '''
71 | The first newline is
72 | trimmed in raw strings.
73 | All other whitespace
74 | is preserved.
75 | '''
76 |
77 | # Integer
78 | # Integers are whole numbers. Positive numbers may be prefixed with a plus sign.
79 | # Negative numbers are prefixed with a minus sign.
80 |
81 | [Integer]
82 | key1 = +99
83 | key2 = 42
84 | key3 = 0
85 | key4 = -17
86 |
87 | # Float
88 | # A float consists of an integer part (which may be prefixed with a plus or minus sign)
89 | # followed by a fractional part and/or an exponent part.
90 |
91 | [Float.fractional]
92 |
93 | # fractional
94 | key1 = +1.0
95 | key2 = 3.1415
96 | key3 = -0.01
97 |
98 | [Float.exponent]
99 |
100 | # exponent
101 | key1 = 5e+22
102 | key2 = 1e6
103 | key3 = -2E-2
104 |
105 | [Float.both]
106 |
107 | # both
108 | key = 6.626e-34
109 |
110 | # Boolean
111 | # Booleans are just the tokens you're used to. Always lowercase.
112 |
113 | [Booleans]
114 | True = true
115 | False = false
116 |
117 | # Datetime
118 | # Datetimes are RFC 3339 dates.
119 |
120 | [Datetime]
121 | key1 = 1979-05-27T07:32:00Z
122 | key2 = 1979-05-27T00:32:00-07:00
123 | key3 = 1979-05-27T00:32:00.999999-07:00
124 |
125 | # Array
126 | # Arrays are square brackets with other primitives inside. Whitespace is ignored. Elements are separated by commas. Data types may not be mixed.
127 |
128 | [Array]
129 | key1 = [ 1, 2, 3 ]
130 | key2 = [ "red", "yellow", "green" ]
131 | key3 = [ [ 1, 2 ], [3, 4, 5] ]
132 | key4 = [ [ 1, 2 ], ["a", "b", "c"] ] # this is ok
133 |
134 | #Arrays can also be multiline. So in addition to ignoring whitespace, arrays also ignore newlines between the brackets.
135 | # Terminating commas are ok before the closing bracket.
136 |
137 | key5 = [
138 | 1, 2, 3
139 | ]
140 | key6 = [
141 | 1,
142 | 2, # this is ok
143 | ]
144 |
145 | # Array of Tables
146 | # These can be expressed by using a table name in double brackets.
147 | # Each table with the same double bracketed name will be an element in the array.
148 | # The tables are inserted in the order encountered.
149 |
150 | [[products]]
151 | name = "Hammer"
152 | sku = 738594937
153 |
154 | [[products]]
155 |
156 | [[products]]
157 | name = "Nail"
158 | sku = 284758393
159 | color = "gray"
160 |
161 |
162 | # You can create nested arrays of tables as well.
163 |
164 | [[fruit]]
165 | name = "apple"
166 |
167 | [fruit.physical]
168 | color = "red"
169 | shape = "round"
170 |
171 | [[fruit.variety]]
172 | name = "red delicious"
173 |
174 | [[fruit.variety]]
175 | name = "granny smith"
176 |
177 | [[fruit]]
178 | name = "banana"
179 |
180 | [[fruit.variety]]
181 | name = "plantain"
182 |
183 |
--------------------------------------------------------------------------------
/tomlutil.xcodeproj/xcshareddata/xcschemes/tomlutil.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
66 |
67 |
70 |
71 |
74 |
75 |
78 |
79 |
82 |
83 |
86 |
87 |
88 |
89 |
95 |
97 |
103 |
104 |
105 |
106 |
108 |
109 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/toml-examples/example-v0.4.0.toml:
--------------------------------------------------------------------------------
1 | ################################################################################
2 | ## Comment
3 |
4 | # Speak your mind with the hash symbol. They go from the symbol to the end of
5 | # the line.
6 |
7 |
8 | ################################################################################
9 | ## Table
10 |
11 | # Tables (also known as hash tables or dictionaries) are collections of
12 | # key/value pairs. They appear in square brackets on a line by themselves.
13 |
14 | [table]
15 |
16 | key = "value" # Yeah, you can do this.
17 |
18 | # Nested tables are denoted by table names with dots in them. Name your tables
19 | # whatever crap you please, just don't use #, ., [ or ].
20 |
21 | [table.subtable]
22 |
23 | key = "another value"
24 |
25 | # You don't need to specify all the super-tables if you don't want to. TOML
26 | # knows how to do it for you.
27 |
28 | # [x] you
29 | # [x.y] don't
30 | # [x.y.z] need these
31 | [x.y.z.w] # for this to work
32 |
33 |
34 | ################################################################################
35 | ## Inline Table
36 |
37 | # Inline tables provide a more compact syntax for expressing tables. They are
38 | # especially useful for grouped data that can otherwise quickly become verbose.
39 | # Inline tables are enclosed in curly braces `{` and `}`. No newlines are
40 | # allowed between the curly braces unless they are valid within a value.
41 |
42 | [table.inline]
43 |
44 | name = { first = "Tom", last = "Preston-Werner" }
45 | point = { x = 1, y = 2 }
46 |
47 |
48 | ################################################################################
49 | ## String
50 |
51 | # There are four ways to express strings: basic, multi-line basic, literal, and
52 | # multi-line literal. All strings must contain only valid UTF-8 characters.
53 |
54 | [string]
55 |
56 | basic = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."
57 |
58 | [string.multiline]
59 |
60 | # The following strings are byte-for-byte equivalent:
61 | key1 = "One\nTwo"
62 | key2 = """One\nTwo"""
63 | key3 = """
64 | One
65 | Two"""
66 |
67 | [string.multiline.continued]
68 |
69 | # The following strings are byte-for-byte equivalent:
70 | key1 = "The quick brown fox jumps over the lazy dog."
71 |
72 | key2 = """
73 | The quick brown \
74 |
75 |
76 | fox jumps over \
77 | the lazy dog."""
78 |
79 | key3 = """\
80 | The quick brown \
81 | fox jumps over \
82 | the lazy dog.\
83 | """
84 |
85 | [string.literal]
86 |
87 | # What you see is what you get.
88 | winpath = 'C:\Users\nodejs\templates'
89 | winpath2 = '\\ServerX\admin$\system32\'
90 | quoted = 'Tom "Dubs" Preston-Werner'
91 | regex = '<\i\c*\s*>'
92 |
93 |
94 | [string.literal.multiline]
95 |
96 | regex2 = '''I [dw]on't need \d{2} apples'''
97 | lines = '''
98 | The first newline is
99 | trimmed in raw strings.
100 | All other whitespace
101 | is preserved.
102 | '''
103 |
104 |
105 | ################################################################################
106 | ## Integer
107 |
108 | # Integers are whole numbers. Positive numbers may be prefixed with a plus sign.
109 | # Negative numbers are prefixed with a minus sign.
110 |
111 | [integer]
112 |
113 | key1 = +99
114 | key2 = 42
115 | key3 = 0
116 | key4 = -17
117 |
118 | [integer.underscores]
119 |
120 | # For large numbers, you may use underscores to enhance readability. Each
121 | # underscore must be surrounded by at least one digit.
122 | key1 = 1_000
123 | key2 = 5_349_221
124 | key3 = 1_2_3_4_5 # valid but inadvisable
125 |
126 |
127 | ################################################################################
128 | ## Float
129 |
130 | # A float consists of an integer part (which may be prefixed with a plus or
131 | # minus sign) followed by a fractional part and/or an exponent part.
132 |
133 | [float.fractional]
134 |
135 | key1 = +1.0
136 | key2 = 3.1415
137 | key3 = -0.01
138 |
139 | [float.exponent]
140 |
141 | key1 = 5e+22
142 | key2 = 1e6
143 | key3 = -2E-2
144 |
145 | [float.both]
146 |
147 | key = 6.626e-34
148 |
149 | [float.underscores]
150 |
151 | key1 = 9_224_617.445_991_228_313
152 | key2 = 1e1_00
153 |
154 |
155 | ################################################################################
156 | ## Boolean
157 |
158 | # Booleans are just the tokens you're used to. Always lowercase.
159 |
160 | [boolean]
161 |
162 | True = true
163 | False = false
164 |
165 |
166 | ################################################################################
167 | ## Datetime
168 |
169 | # Datetimes are RFC 3339 dates.
170 |
171 | [datetime]
172 |
173 | key1 = 1979-05-27T07:32:00Z # asdf
174 | key2 = 1979-05-27T00:32:00-07:00
175 | key3 = 1979-05-27T00:32:00.999999-07:00
176 | key4 = 1979-05-27 00:32:00+07:10
177 | time = 12:32:11.123
178 | date = 1979-05-27
179 |
180 |
181 | ################################################################################
182 | ## Array
183 |
184 | # Arrays are square brackets with other primitives inside. Whitespace is
185 | # ignored. Elements are separated by commas. Data types may not be mixed.
186 |
187 | [array]
188 |
189 | key1 = [ 1, 2, 3 ]
190 | key2 = [ "red", "yellow", "green" ]
191 | key3 = [ [ 1, 2 ], [3, 4, 5] ]
192 | key4 = [ [ 1, 2 ], ["a", "b", "c"] ] # this is ok
193 |
194 | # Arrays can also be multiline. So in addition to ignoring whitespace, arrays
195 | # also ignore newlines between the brackets. Terminating commas are ok before
196 | # the closing bracket.
197 |
198 | key5 = [
199 | 1, 2, 3
200 | ]
201 | key6 = [
202 | 1,
203 | 2, # this is ok
204 | ]
205 |
206 |
207 | ################################################################################
208 | ## Array of Tables
209 |
210 | # These can be expressed by using a table name in double brackets. Each table
211 | # with the same double bracketed name will be an element in the array. The
212 | # tables are inserted in the order encountered.
213 |
214 | [[products]]
215 |
216 | name = "Hammer"
217 | sku = 738594937
218 |
219 | [[products]]
220 |
221 | [[products]]
222 |
223 | name = "Nail"
224 | sku = 284758393
225 | color = "gray"
226 |
227 |
228 | # You can create nested arrays of tables as well.
229 |
230 | [[fruit]]
231 | name = "apple"
232 |
233 | [fruit.physical]
234 | color = "red"
235 | shape = "round"
236 |
237 | [[fruit.variety]]
238 | name = "red delicious"
239 |
240 | [[fruit.variety]]
241 | name = "granny smith"
242 |
243 | [[fruit]]
244 | name = "banana"
245 |
246 | [[fruit.variety]]
247 | name = "plantain"
248 |
--------------------------------------------------------------------------------
/tomlutil/LMP_toml_visitors.h:
--------------------------------------------------------------------------------
1 | //
2 | // LMP_cpptoml_visitors.h
3 | // tomlutil
4 | //
5 | // Created by dom on 10/20/18.
6 | // Copyright © 2018 Lone Monkey Productions. All rights reserved.
7 | //
8 |
9 | #ifndef LMP_toml_visitors_h
10 | #define LMP_toml_visitors_h
11 | #include "toml.hpp"
12 | /**
13 | * A visitor for toml objects that writes to an NSMutableDictionary
14 | */
15 | class toml_nsdictionary_writer {
16 | public:
17 | toml_nsdictionary_writer() {
18 | currentDictionary_ = resultContainer_ = [NSMutableDictionary new];
19 | currentKey_ = @"TOMLRoot";
20 | }
21 |
22 | NSDictionary *dictionary() {
23 | return [resultContainer_[@"TOMLRoot"] copy];
24 | }
25 |
26 | void visit(const toml::string& v) {
27 | id value = [NSString stringWithUTF8String: static_cast(v).c_str()];
28 | writeValue(value);
29 | }
30 |
31 | void visit(const toml::local_date& v) {
32 | id value = ({
33 | NSDateComponents *result = [NSDateComponents new];
34 | result.year = v.year;
35 | result.month = v.month + 1;
36 | result.day = v.day;
37 | result;
38 | });
39 | writeValue(value);
40 | }
41 |
42 | void visit(const toml::local_time& v) {
43 | id value = ({
44 | NSDateComponents *result = [NSDateComponents new];
45 | result.hour = v.hour;
46 | result.minute = v.minute;
47 | result.second = v.second;
48 | result.nanosecond = v.millisecond * 1000000 + v.microsecond * 1000 + v.nanosecond;
49 | result;
50 | });
51 | // NSLog(@"%@", [value debugDescription]);
52 | writeValue(value);
53 | }
54 |
55 | // untested, need to be looked again to have proper roundtrip I guess.
56 | void visit(const toml::local_datetime& v) {
57 | id value = ({
58 | NSDateComponents *result = [NSDateComponents new];
59 | result.year = v.date.year;
60 | result.month = v.date.month + 1;
61 | result.day = v.date.day;
62 | result.hour = v.time.hour;
63 | result.minute = v.time.minute;
64 | result.second = v.time.second;
65 | result.nanosecond = v.time.millisecond * 1000000 + v.time.microsecond * 1000 + v.time.nanosecond;
66 | result;
67 | });
68 | writeValue(value);
69 | }
70 |
71 | void visit(const toml::offset_datetime& v) {
72 | id value = ({
73 | NSDateComponents *result = [NSDateComponents new];
74 | result.year = v.date.year;
75 | result.month = v.date.month + 1;
76 | result.day = v.date.day;
77 | result.hour = v.time.hour;
78 | result.minute = v.time.minute;
79 | result.second = v.time.second;
80 | result.nanosecond = v.time.millisecond * 1000000 + v.time.microsecond * 1000 + v.time.nanosecond;
81 | result.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:(v.offset.hour * 60 + v.offset.minute) * 60];
82 | result;
83 | });
84 | writeValue(value);
85 | }
86 |
87 |
88 | void visit(const toml::array& t) {
89 | withContainerIsDictionary( NO, ^{
90 | for (auto it = t.begin(); it != t.end(); it++) {
91 | visit(*it);
92 | }
93 | });
94 | }
95 |
96 |
97 | void visit(const toml::table& t) {
98 | withContainerIsDictionary( YES, ^{
99 | for (auto it = t.begin(); it != t.end(); it++) {
100 | currentKey_ = [NSString stringWithUTF8String: it->first.c_str()];
101 | visit(it->second);
102 | }
103 | });
104 | }
105 |
106 | void visit(const toml::value& dunno) {
107 | switch (dunno.type()) {
108 | case toml::value_t::boolean:
109 | writeValue(dunno.as_boolean() ? @YES : @NO);
110 | break;
111 | case toml::value_t::integer:
112 | writeValue(@(dunno.as_integer()));
113 | break;
114 | case toml::value_t::floating:
115 | writeValue(@(dunno.as_floating()));
116 | break;
117 | case toml::value_t::string :
118 | visit(toml::get(dunno));
119 | break;
120 | case toml::value_t::array :
121 | visit(toml::get(dunno));
122 | break;
123 | case toml::value_t::table :
124 | visit(toml::get(dunno));
125 | break;
126 | case toml::value_t::local_date :
127 | visit(toml::get(dunno));
128 | break;
129 | case toml::value_t::local_time :
130 | visit(toml::get(dunno));
131 | break;
132 | case toml::value_t::local_datetime :
133 | visit(toml::get(dunno));
134 | break;
135 | case toml::value_t::offset_datetime :
136 | visit(toml::get(dunno));
137 | break;
138 | default:
139 | // have all types now, should not happen anymore™
140 | std::cout << "- '" << dunno << "' --------- not yet implemented" << std::endl;
141 | break;
142 | }
143 | }
144 |
145 | private:
146 |
147 | void withContainerIsDictionary(BOOL isDictionary, dispatch_block_t stuff) {
148 | NSMutableArray *array = currentArray_;
149 | NSMutableDictionary *dict = currentDictionary_;
150 | NSString *key = currentKey_;
151 |
152 | if (isDictionary) {
153 | NSMutableDictionary *contextDictionary = [NSMutableDictionary new];
154 | writeValue(contextDictionary);
155 |
156 | currentDictionary_ = contextDictionary;
157 | currentArray_ = nil;
158 | } else {
159 | NSMutableArray *contextArray = [NSMutableArray new];
160 | writeValue(contextArray);
161 |
162 | currentArray_ = contextArray;
163 | }
164 |
165 | stuff();
166 |
167 | currentKey_ = key;
168 | currentArray_ = array;
169 | currentDictionary_ = dict;
170 | }
171 |
172 | void writeValue(id value) {
173 | if (currentArray_) {
174 | [currentArray_ addObject:value];
175 | } else {
176 | currentDictionary_[currentKey_] = value;
177 | }
178 | }
179 |
180 | NSMutableDictionary *resultContainer_;
181 | NSMutableDictionary *currentDictionary_;
182 | NSString *currentKey_;
183 | NSMutableArray *currentArray_;
184 | };
185 |
186 | #endif /* LMP_toml_visitors_h */
187 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ObjectiveTOML
2 |
3 | ObjectiveTOML is a clean and nice Objective-C API to read and write [TOML](https://github.com/toml-lang/toml) files. It is utilizing [toml11](https://github.com/ToruNiina/toml11) and therefore on par with its TOML compliance. At time of writing this is [TOML 1.0.0](https://toml.io/en/v1.0.0)
4 |
5 | The main target is a small command line utility `tomlutil` to convert between TOML, JSON and the xml and binary plist format on macOS.
6 |
7 | The API is aligned with `NSJSONSerialization`:
8 |
9 | ```objectivec
10 | #import "LMPTOMLSerialization.h"
11 |
12 | NSData *inputData = [NSData dataWithContentsOfURL:fileURL];
13 | NSDictionary * tomlObject =
14 | [LMPTOMLSerialization TOMLObjectWithData:inputData error:&error];
15 | // now tomlObject holds the contents of the TOML file.
16 |
17 | ```
18 |
19 | The `tomlutil` has nice parsing error reporting (if you only want this, you can use the `-lint` option:
20 |
21 | ```
22 | $> ./tomlutil -lint toml-examples/wrong.toml
23 | 🚫 Input TOML could not be parsed
24 | [error] toml::parse_key_value_pair: invalid format for key
25 | --> toml-examples/wrong.toml
26 | |
27 | 14 | asdfpoin1!@ j= ;alskjfasdf
28 | | ^--- invalid character in key
29 | |
30 | Hint: Did you forget '.' to separate dotted-key?
31 | Hint: Allowed characters for bare key are [0-9a-zA-Z_-].
32 | ```
33 |
34 | Current usage output:
35 |
36 | ```
37 | tomlutil v2.0.0 (toml11 v3.7.1)
38 |
39 | Usage: tomlutil [-f json|xml1|binary1|toml] file [outputfile]
40 |
41 | A file of '-' reads from stdin. Can read json, plists and toml. Output defaults to stdout.
42 | -f format Output format. One of json, xml1, binary1, toml. Defaults to toml.
43 | -lint Just lint with toml11, no output.
44 | ```
45 |
46 | Simple conversion of the Mojave News.app plist to TOML:
47 |
48 | ```toml
49 | $> ./tomlutil /System/Applications/News.app/Contents/Info.plist
50 | SBAppUsesLocalNotifications = true
51 | DTSDKBuild = "22D40"
52 | UILaunchStoryboardName = "LaunchScreen"
53 | CFBundleHelpBookName = "com.apple.News.help"
54 | NSSupportsSuddenTermination = true
55 | DTPlatformVersion = "13.2"
56 | UIViewGroupOpacity = false
57 | CFBundleDisplayName = "News"
58 | CFBundleName = "News"
59 | HPDHelpProjectIdentifier = "news"
60 | UIDeviceFamily = [6]
61 | CFBundlePackageType = "APPL"
62 | UIRequiredDeviceCapabilities = ["armv7"]
63 | CFBundleSignature = "????"
64 | UIMenuBarItemTitleQuit = "Quit News"
65 | CFBundleVersion = "3270.0.1"
66 | LSSupportedRegions = ["US","GB","AU","CA"]
67 | NSLocationDefaultAccuracyReduced = true
68 | UIMenuBarItemTitleHelp = "News Help"
69 | NSLocationUsageDescription = """
70 | Get top local news and weather, and locally relevant search results an\
71 | d ads.\
72 | """
73 | UIMenuBarItemTitleAbout = "About News"
74 | UIMenuBarItemTitleHide = "Hide News"
75 | NSCalendarsUsageDescription = "This will let you add events from News to your calendar."
76 | CFBundleSupportedPlatforms = ["MacOSX"]
77 | NSLocationWhenInUseUsageDescription = """
78 | Get top local news and weather, and locally relevant search r\
79 | esults and ads.\
80 | """
81 | CFBundleInfoDictionaryVersion = "6.0"
82 | LSMinimumSystemVersion = "13.2"
83 | CFBundleIdentifier = "com.apple.news"
84 | NSHumanReadableCopyright = "Copyright © 2022 Apple Inc. All rights reserved."
85 | NSPhotoLibraryAddUsageDescription = ""
86 | UIWhitePointAdaptivityStyle = "UIWhitePointAdaptivityStyleReading"
87 | NSContactsUsageDescription = ""
88 | NSSupportsAutomaticTermination = true
89 | CFBundleShortVersionString = "8.2.1"
90 | CFBundleIconFile = "AppIcon"
91 | CTIgnoreUserFonts = true
92 | UIAppFonts = []
93 | UIUserInterfaceStyle = "Automatic"
94 | DTXcodeBuild = "14A6270d"
95 | CFBundleExecutable = "News"
96 | DTCompiler = "com.apple.compilers.llvm.clang.1_0"
97 | CFBundleIconName = "AppIcon"
98 | UIStatusBarHidden = false
99 | "UISupportedInterfaceOrientations~ipad" = [
100 | "UIInterfaceOrientationPortrait",
101 | "UIInterfaceOrientationPortraitUpsideDown",
102 | "UIInterfaceOrientationLandscapeLeft",
103 | "UIInterfaceOrientationLandscapeRight",
104 | ]
105 | BuildMachineOSBuild = "20A241133"
106 | UIViewControllerBasedStatusBarAppearance = true
107 | UIBackgroundModes = ["audio","fetch","remote-notification"]
108 | NSAccentColorName = "NewsAccentColor"
109 | UIViewEdgeAntialiasing = false
110 | DTPlatformName = "macosx"
111 | SBMatchingApplicationGenres = [
112 | "News","Reference","Entertainment","Productivity","Education",
113 | "Business",
114 | ]
115 | NSUserActivityTypes = [
116 | "TagIntent","TodayIntent","com.apple.news.articleViewing",
117 | "com.apple.news.feedBrowsing","com.apple.news.feedBackCatalog",
118 | "com.apple.news.forYou","com.apple.news.history","com.apple.news.saved",
119 | "com.apple.news.magazineSections","com.apple.news.link",
120 | ]
121 | UIApplicationShortcutWidget = "com.apple.news.widget"
122 | CFBundleDevelopmentRegion = "en"
123 | UISupportedInterfaceOrientations = ["UIInterfaceOrientationPortrait"]
124 | CFBundleHelpBookFolder = "News.help"
125 | BGTaskSchedulerPermittedIdentifiers = ["com.apple.news.backgroundFetchManager"]
126 | _LSSupportsRemoval = true
127 | DTSDKName = "macosx13.2.internal"
128 | DTPlatformBuild = "22D40"
129 | NSPhotoLibraryUsageDescription = ""
130 | DTXcode = "1400"
131 | LSCounterpartIdentifiers = ["com.apple.nanonews"]
132 |
133 | [[UIApplicationShortcutItems]]
134 | UIApplicationShortcutItemTitle = "ApplicationShortcutItemForYou"
135 | UIApplicationShortcutItemIconFile = "ios_for_you_icon_large"
136 | UIApplicationShortcutItemType = "com.apple.news.openforyou"
137 |
138 | [UNUserNotificationCenter]
139 | UNSuppressUserAuthorizationPrompt = false
140 |
141 | [UIApplicationSceneManifest]
142 | UIApplicationSupportsMultipleScenes = "1"
143 |
144 | [UIApplicationSceneManifest.UISceneConfigurations]
145 |
146 | [[UIApplicationSceneManifest.UISceneConfigurations.UIWindowSceneSessionRoleApplication]]
147 | UISceneConfigurationName = "Default Configuration"
148 | UISceneDelegateClassName = "NewsUI2.SceneDelegate"
149 | UISceneClassName = "TeaUI.WindowScene"
150 |
151 |
152 | [[CFBundleURLTypes]]
153 | CFBundleURLSchemes = ["applenews","applenewss"]
154 | CFBundleURLName = "com.apple.NewsCustomScheme"
155 | CFBundleTypeRole = "Editor"
156 | ```
157 |
158 | Full header:
159 |
160 | ```objectivec
161 | @interface LMPTOMLSerialization : NSObject
162 |
163 | /**
164 | Generate a Foundation Dictionary from TOML data.
165 |
166 | @param data NSData representing a TOML file
167 | @param error helpful information if the parsing fails
168 | @return NSDictionary representing the contents of the TOML file. Note that given dates will be represented as NSDateComponents, use +serializationObjectWtihTOMLObject: to convert those to RFC3339 strings that can be used in JSON or PropertyList serializations.
169 | */
170 | + (NSDictionary *)TOMLObjectWithData:(NSData *)data error:(NSError **)error;
171 |
172 | /**
173 | Generates NSData representation of the TOMLObject. The representation is UTF8 and can be stored directly as a TOML file.
174 |
175 | Note that roundtripping is a lossy opreation, as all comments are stripped, the allowed number formats are reduced to canonical ones and doubles might lose or gain unwanted precision.
176 |
177 | @param tomlObject Foundation Object consisting of TOML serializable objects. In addition to plist objects this contains NSDateComponent objects with y-m-d filled, h-m-s-[nanoseconds] filled, all fields filled, or all fields + timezone filled.
178 | @param error helpful information if generation fails
179 | @return NSData representing the object.
180 | */
181 | + (NSData *)dataWithTOMLObject:(NSDictionary *)tomlObject error:(NSError **)error;
182 |
183 | /**
184 | Takes a Dictionary representing a TOML file and translates the NSDateComponents into RFC339 strings to be able to be serialized in JSON or PropertyLists
185 |
186 | @param tomlObject foundation dictionary consisting of TOML serializable objects.
187 | @return NSDictionary containing property list serializable objects.
188 | */
189 | + (NSDictionary *)serializableObjectWithTOMLObject:(NSDictionary *)tomlObject;
190 |
191 | @end
192 | ```
193 |
194 | ## Changelog
195 |
196 | * v2.0.0
197 | * switched to [toml11] (https://github.com/ToruNiina/toml11
198 |
199 | * v1.1.0
200 | * Updated to cpptoml v0.1.1
201 | * Added more arguments, help and error reporting to the `tomlutil`
202 |
203 | * v1.0.1
204 | * Fixed an issue with cpptoml with trailing whitespace and comments in dates as well as allowing for empty inline tables now.
205 |
206 | * v1.0
207 | * Reading and writing works. Dates are handeled. Conversion between json, plists and toml works as expected.
208 |
209 | * v0.1.0
210 | * Basic reading works, no support of Date format yet, or of the switches promised.
211 |
212 | ## Authors
213 |
214 | * **Dominik Wagner** - [monkeydom](https://github.com/monkeydom) - [@monkeydom@mastodon.social](https://mastodon.social/@monkeydom)
215 |
216 | ## License
217 |
218 | Distributed under the MIT License - see [LICENSE.txt](LICENSE.txt) file for details
219 |
220 | ## Acknowledgments
221 |
222 | * ObjectiveTOML relies on the excellent [cpptoml](https://github.com/skystrife/cpptoml)
223 |
224 |
--------------------------------------------------------------------------------
/tomlutil/LMPTOMLSerialization.mm:
--------------------------------------------------------------------------------
1 | // LMPTOMLSerialization.m
2 | //
3 | // Created by dom on 10/20/18.
4 | // Copyright © 2018 Lone Monkey Productions. All rights reserved.
5 |
6 | #import "LMPTOMLSerialization.h"
7 |
8 | #define TOML11_COLORIZE_ERROR_MESSAGE
9 |
10 | // #define TOML11_PRESERVE_COMMENTS_BY_DEFAULT
11 | #include "toml.hpp"
12 |
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 |
19 | #include "LMP_toml_visitors.h"
20 |
21 |
22 | NSErrorDomain const LMPTOMLErrorDomain = @"productions.monkey.lone.TOML";
23 | static NSInteger const LMPTOMLParseErrorCode = 7031;
24 | static NSInteger const LMPTOMLWriteErrorCode = 7001;
25 |
26 | NSString * const LMPTOMLErrorInfoKeyColorizedReason = @"ColoredFailureReason";
27 |
28 | extern NSString * const LMPTOMLOptionKeySourceFileURL = @"sourceFileURL";
29 |
30 |
31 |
32 | @implementation LMPTOMLSerialization
33 |
34 | + (NSDictionary *)TOMLObjectWithData:(NSData *)data error:(NSError **)error {
35 | return [self TOMLObjectWithData:data options:nil error:error];
36 | }
37 |
38 | + (NSDictionary *)TOMLObjectWithData:(NSData *)data options:(NSDictionary *)options error:(NSError **)error {
39 |
40 | try {
41 | char *bytes = (char *)data.bytes;
42 |
43 | // deprecated but seems to do what I want, the implementation previously was missing seek capabilities.
44 | // see https://stackoverflow.com/questions/13059091/creating-an-input-stream-from-constant-memory#comment115305688_13059195
45 | std::strstreambuf sbuf(bytes, data.length);
46 | std::istream stream(&sbuf);
47 |
48 | // this works as well, probably does more copying than we need
49 | // std::istringstream stream(std::string(bytes, data.length));
50 |
51 | NSURL *fileSourceURL = options[LMPTOMLOptionKeySourceFileURL];
52 |
53 | NSString *filePath = fileSourceURL ? fileSourceURL.relativePath : @"anonymous input";
54 |
55 | // parse
56 | const auto data = toml::parse(stream, filePath.UTF8String);
57 |
58 | // convert table to standard Objective-C objects
59 | toml_nsdictionary_writer dw;
60 | dw.visit(toml::get(data));
61 |
62 | // std::cout << " --- " << data << " --- \n";
63 |
64 | NSDictionary *result = dw.dictionary();
65 | return result;
66 |
67 | } catch (const toml::exception& e) {
68 | if (error) {
69 | NSString *coloredWhat = @(e.what());
70 | NSRegularExpression *regEx = [NSRegularExpression regularExpressionWithPattern:@"\033\\[\\d+m" options:0 error:nil];
71 | NSString *cleanWhat = [regEx stringByReplacingMatchesInString:coloredWhat options:0 range:NSMakeRange(0, coloredWhat.length) withTemplate:@""];
72 |
73 | *error = [NSError errorWithDomain:LMPTOMLErrorDomain
74 | code:LMPTOMLParseErrorCode
75 | userInfo:@{
76 | NSLocalizedDescriptionKey : @"Input TOML could not be parsed",
77 | NSLocalizedFailureReasonErrorKey : cleanWhat,
78 | LMPTOMLErrorInfoKeyColorizedReason : coloredWhat,
79 | }];
80 | }
81 | return nil;
82 | }
83 | }
84 |
85 | static toml::value DictionaryToTable(NSDictionary *dict) {
86 | toml::table table;
87 | for (NSString *key in dict.keyEnumerator) {
88 | auto cppKey = std::string(key.UTF8String);
89 | auto cppValue = ObjectToValue(dict[key]);
90 | table.insert(std::make_pair(cppKey, cppValue));
91 | }
92 | return toml::value(table);
93 | }
94 |
95 | static toml::value ArrayToArray(NSArray *array) {
96 | toml::array result;
97 | for (id value in array) {
98 | result.push_back(ObjectToValue(value));
99 | }
100 | return toml::value(result);
101 | }
102 |
103 | static toml::value ObjectToValue(id objectValue) {
104 | if ([objectValue isKindOfClass:[NSString class]]) {
105 | return toml::value(std::string([objectValue UTF8String]));
106 | } else if ([objectValue isKindOfClass:[NSNumber class]]) {
107 | if ((__bridge CFBooleanRef)objectValue == kCFBooleanTrue ||
108 | (__bridge CFBooleanRef)objectValue == kCFBooleanFalse) {
109 | return toml::value((__bridge CFBooleanRef)objectValue == kCFBooleanTrue);
110 | } else if (CFNumberIsFloatType((__bridge CFNumberRef)objectValue)) {
111 | return toml::value([objectValue doubleValue]);
112 | } else {
113 | return toml::value([objectValue longLongValue]);
114 | }
115 | } else if ([objectValue isKindOfClass:[NSDictionary class]]) {
116 | return DictionaryToTable(objectValue);
117 | } else if ([objectValue isKindOfClass:[NSArray class]]) {
118 | return ArrayToArray(objectValue);
119 | } else if ([objectValue isKindOfClass:[NSDateComponents class]]) {
120 | NSDateComponents *dc = objectValue;
121 |
122 | if (dc.year == NSDateComponentUndefined) {
123 | toml::local_time lt;
124 | if (dc.hour != NSDateComponentUndefined) {
125 | lt.hour = (int)dc.hour;
126 | }
127 | if (dc.minute != NSDateComponentUndefined) {
128 | lt.minute = (int)dc.minute;
129 | }
130 | if (dc.second != NSDateComponentUndefined) {
131 | lt.second = (int)dc.second;
132 | }
133 | if (dc.nanosecond != NSDateComponentUndefined) {
134 | lt.nanosecond = (int)(dc.nanosecond % 1000);
135 | long ms = dc.nanosecond / 1000;
136 | lt.microsecond = (int)(ms % 1000);
137 | lt.millisecond = (int)ms / 1000;
138 | }
139 | return toml::value(lt);
140 | } else {
141 | toml::local_date ld((int16_t)dc.year, (toml::month_t)(dc.month - 1), (uint8_t)dc.day);
142 | if (dc.second == NSDateComponentUndefined) {
143 | return toml::value(ld);
144 | }
145 | else {
146 | toml::local_time lt;
147 | if (dc.hour != NSDateComponentUndefined) {
148 | lt.hour = (int)dc.hour;
149 | }
150 | if (dc.minute != NSDateComponentUndefined) {
151 | lt.minute = (int)dc.minute;
152 | }
153 | if (dc.second != NSDateComponentUndefined) {
154 | lt.second = (int)dc.second;
155 | }
156 | if (dc.nanosecond != NSDateComponentUndefined) {
157 | lt.nanosecond = (int)(dc.nanosecond % 1000);
158 | long ms = dc.nanosecond / 1000;
159 | lt.microsecond = (int)(ms % 1000);
160 | lt.millisecond = (int)ms / 1000;
161 | }
162 |
163 | toml::local_datetime ldt(ld, lt);
164 | if (!dc.timeZone) {
165 | return toml::value(ldt);
166 | } else {
167 | int minutesFromGMT = (int)[dc.timeZone secondsFromGMT] / 60;
168 | toml::offset_datetime dt(ldt, toml::time_offset(minutesFromGMT / 60, minutesFromGMT % 60));
169 | return toml::value(dt);
170 | }
171 | }
172 | }
173 |
174 | } else {
175 | return toml::value("--- unkonwn ---");
176 | }
177 | }
178 |
179 | + (NSData *)dataWithTOMLObject:(NSDictionary *)tomlObject error:(NSError **)error {
180 | try {
181 | // __auto_type dict = @{@"test1" : @1, @"testBool" : @YES, @"testString" : @"some string", @"test3.14" : @(M_PI),
182 | // @"subtable" : @{ @"two" : @"zwo"},
183 | // @"some array" : @[@1, @2, @3],
184 | // @"some other array" : @[@"oans", @"zwoa"],
185 | // };
186 |
187 | auto root = DictionaryToTable(tomlObject);
188 | std::stringstream s("");
189 | // setw makes inline tables if they fit
190 | // std::setprecision(7) e.g. would also set the float precision. we might want that for the future
191 | s << std::setw(100) << root << std::endl;
192 | std::string str = s.str();
193 | NSData *result = [NSData dataWithBytes:str.data() length:str.length()];
194 | return result;
195 | } catch (const toml::exception& e) {
196 | if (error) {
197 | NSString *coloredWhat = @(e.what());
198 | NSRegularExpression *regEx = [NSRegularExpression regularExpressionWithPattern:@"\033\\[\\d+m" options:0 error:nil];
199 | NSString *cleanWhat = [regEx stringByReplacingMatchesInString:coloredWhat options:0 range:NSMakeRange(0, coloredWhat.length) withTemplate:@""];
200 |
201 | *error = [NSError errorWithDomain:LMPTOMLErrorDomain
202 | code:LMPTOMLWriteErrorCode
203 | userInfo:@{
204 | NSLocalizedDescriptionKey : @"Input objects could not be converted to TOML",
205 | NSLocalizedFailureReasonErrorKey : cleanWhat,
206 | LMPTOMLErrorInfoKeyColorizedReason : coloredWhat,
207 | }];
208 | }
209 | return nil;
210 | }
211 | }
212 |
213 | static NSString *TOMLTimeStringFromComponents(NSDateComponents *dc) {
214 | NSMutableString *result = [NSMutableString new];
215 | if (dc.year != NSDateComponentUndefined) {
216 | [result appendFormat:@"%04d-%02d-%02d", (int)dc.year, (int)dc.month, (int)dc.day];
217 | }
218 | if (dc.minute != NSDateComponentUndefined) {
219 | if (result.length > 0) {
220 | [result appendString:@"T"];
221 | }
222 | [result appendFormat:@"%02d:%02d:%02d", (int)dc.hour, (int)dc.minute, (int)dc.second];
223 | if (dc.nanosecond != NSDateComponentUndefined && dc.nanosecond > 0) {
224 | [result appendFormat:@".%06d", (int)(dc.nanosecond / 1000)];
225 | }
226 | if (dc.timeZone) {
227 | int minuteOffset = (int)dc.timeZone.secondsFromGMT / 60;
228 | if (minuteOffset == 0) {
229 | [result appendString:@"Z"];
230 | } else {
231 | [result appendString:minuteOffset > 0 ? @"+" : @"-"];
232 | [result appendFormat:@"%02d:%02d", ABS(minuteOffset) / 60, ABS(minuteOffset) % 60];
233 | }
234 | }
235 | }
236 | return result;
237 | }
238 |
239 | static NSArray *serializableArray(NSArray *array) {
240 | NSMutableArray *result = [array mutableCopy];
241 | NSUInteger index = result.count;
242 | while (index-- != 0) {
243 | id value = result[index];
244 | if ([value isKindOfClass:[NSArray class]]) {
245 | result[index] = serializableArray(value);
246 | } else if ([value isKindOfClass:[NSDictionary class]]) {
247 | result[index] = [LMPTOMLSerialization serializableObjectWithTOMLObject:value];
248 | } else if ([value isKindOfClass:[NSDateComponents class]]) {
249 | result[index] = TOMLTimeStringFromComponents(value);
250 | }
251 | }
252 | return result;
253 | }
254 |
255 | + (NSDictionary *)serializableObjectWithTOMLObject:(NSDictionary *)tomlObject {
256 | NSMutableDictionary *result = [tomlObject mutableCopy];
257 | for (NSString *key in result.allKeys) {
258 | id value = result[key];
259 | if ([value isKindOfClass:[NSDictionary class]]) {
260 | result[key] = [self serializableObjectWithTOMLObject:value];
261 | } else if ([value isKindOfClass:[NSArray class]]) {
262 | result[key] = serializableArray(value);
263 | } else if ([value isKindOfClass:[NSDateComponents class]]) {
264 | result[key] = TOMLTimeStringFromComponents(value);
265 | }
266 | }
267 | return result;
268 | }
269 |
270 | @end
271 |
--------------------------------------------------------------------------------
/tomlutil/main.m:
--------------------------------------------------------------------------------
1 | // main.m
2 | // tomlutil
3 | //
4 | // Created by dom on 10/20/18.
5 | // Copyright © 2018 Lone Monkey Productions. All rights reserved.
6 |
7 | @import Foundation;
8 | #import "LMPTOMLSerialization.h"
9 |
10 | typedef CF_ENUM(int, LMPFileFormat) {
11 | FileFormatUnknown = 0,
12 | FileFormatPlistBinary = kCFPropertyListBinaryFormat_v1_0,
13 | FileFormatPlistXML = kCFPropertyListXMLFormat_v1_0,
14 | // FileFormatPlistOpenStep = kCFPropertyListOpenStepFormat, cannot write anymore anyways
15 | FileFormatJSON = 801,
16 | FileFormatTOML = 901,
17 | FileFormatNone = 99991,
18 | };
19 |
20 | void showErrorAndHalt(NSError *error, NSData *inputData) {
21 | if (!error) {
22 | return;
23 | }
24 | char *bold="";
25 | char *stopBold="";
26 |
27 | BOOL supportsAnsiColor = isatty([[NSFileHandle fileHandleWithStandardError] fileDescriptor]);
28 |
29 | if (supportsAnsiColor) {
30 | NSString *term = [[NSProcessInfo processInfo] environment][@"TERM"] ;
31 | if (term && (
32 | [term rangeOfString:@"color" options:NSCaseInsensitiveSearch].location != NSNotFound ||
33 | [term rangeOfString:@"ansi" options:NSCaseInsensitiveSearch].location != NSNotFound
34 | )
35 | ){
36 | } else {
37 | supportsAnsiColor = NO;
38 | }
39 |
40 | }
41 | if (supportsAnsiColor) {
42 | bold="\033[1m";
43 | stopBold="\033[0m";
44 | }
45 | fputs(bold, stderr);
46 | fputs("🚫 ",stderr);
47 | fputs(error.localizedDescription.UTF8String, stderr);
48 | fputs(stopBold, stderr);
49 | fputs("\n", stderr);
50 |
51 | if (supportsAnsiColor && error.userInfo[LMPTOMLErrorInfoKeyColorizedReason]) {
52 | fputs([error.userInfo[LMPTOMLErrorInfoKeyColorizedReason] UTF8String], stderr);
53 | } else {
54 | fputs(error.localizedFailureReason.UTF8String, stderr);
55 | }
56 | fputs("\n", stderr);
57 |
58 | exit(EXIT_FAILURE);
59 | }
60 |
61 | static LMPFileFormat formatFromFilename(NSString *filename) {
62 | // Extensions
63 | NSString *extension = [filename.pathExtension lowercaseString];
64 | LMPFileFormat result = [@{
65 | @".toml" : @(FileFormatTOML),
66 | @".json" : @(FileFormatJSON),
67 | @".js" : @(FileFormatJSON),
68 | @".plist" : @(FileFormatPlistXML),
69 | }[extension] intValue];
70 | return result;
71 | }
72 |
73 | static NSString *version_string(void) {
74 | NSString *shortVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
75 | // NSString *bundleVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
76 | NSString *toml11Version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"LMPToml11Version"];
77 |
78 | NSString *versionLineString = [NSString stringWithFormat:@"tomlutil v%@ (toml11 v%@)", shortVersion, toml11Version];
79 | return versionLineString;
80 | }
81 |
82 | static void show_help(BOOL versionOnly) {
83 |
84 | puts(version_string().UTF8String);
85 |
86 | if (versionOnly) {
87 | return;
88 | }
89 |
90 | puts("");
91 | puts("Usage: tomlutil [-f json|xml1|binary1|toml] file [outputfile]\n\n"
92 | "A file of '-' reads from stdin. Can read json, plists and toml. Output defaults to stdout.\n"
93 | "-f format Output format. One of json, xml1, binary1, toml. Defaults to toml.\n"
94 | "-lint Just lint with toml11, no output.");
95 | }
96 |
97 | int main(int argc, const char * argv[]) {
98 | @autoreleasepool {
99 |
100 | BOOL showHelp = argc <= 1;
101 |
102 | if (!showHelp && argc == 2) {
103 | NSString *argument = [[NSProcessInfo processInfo].arguments lastObject];
104 | NSArray *helpArgs = @[@"-h",@"-help",@"--help"];
105 | if ([helpArgs containsObject:argument]) {
106 | showHelp = YES;
107 | } else if ([@[@"-v",@"-version",@"--version"] containsObject:argument]) {
108 | show_help(YES);
109 | return EXIT_SUCCESS;
110 | }
111 | }
112 |
113 | if (showHelp) {
114 | show_help(NO);
115 | return EXIT_SUCCESS;
116 | }
117 |
118 | if (argc > 1) {
119 | __block LMPFileFormat inputFormat = FileFormatUnknown;
120 | __block LMPFileFormat outputFormat = FileFormatUnknown; // default to toml
121 | __block NSString *filenameString;
122 | __block NSString *outputFilenameString;
123 |
124 | __block NSString *flag = nil;
125 |
126 | NSMutableArray *argumentsArray = [[NSProcessInfo processInfo].arguments mutableCopy];
127 | argumentsArray[0] = @"tomlutil";
128 |
129 | [[NSProcessInfo processInfo].arguments enumerateObjectsUsingBlock:^(NSString *argument, NSUInteger index, BOOL *stop) {
130 | if (index > 0) {
131 | if (flag) {
132 | if ([flag isEqualToString:@"-f"]) {
133 | if (outputFormat != FileFormatNone) {
134 | outputFormat = [@{
135 | @"toml" : @(FileFormatTOML),
136 |
137 | @"json" : @(FileFormatJSON),
138 |
139 | @"plist" : @(FileFormatPlistXML),
140 | @"xml" : @(FileFormatPlistXML),
141 | @"binary" : @(FileFormatPlistBinary),
142 | @"xml1" : @(FileFormatPlistXML),
143 | @"binary1" : @(FileFormatPlistBinary),
144 | }[argument] intValue] ?: outputFormat;
145 | }
146 | }
147 | flag = nil;
148 | } else {
149 | if ([argument hasPrefix:@"-f"]) {
150 | flag = argument;
151 | } else if ([argument isEqualToString:@"-lint"]) {
152 | outputFormat = FileFormatNone;
153 | inputFormat = FileFormatTOML;
154 | } else {
155 | if (!filenameString) {
156 | filenameString = argument;
157 | } else if (!outputFilenameString) {
158 | outputFilenameString = argument;
159 | } else {
160 | showErrorAndHalt([NSError errorWithDomain:NSPOSIXErrorDomain code:999 userInfo:@{
161 | NSLocalizedDescriptionKey : @"Too many file arguments.",
162 | NSLocalizedFailureReasonErrorKey : [argumentsArray componentsJoinedByString:@" "],
163 | }], nil);
164 | }
165 | }
166 | }
167 | }
168 | }];
169 |
170 | NSData *inputData = (!filenameString || [filenameString isEqualToString:@"-"]) ? ({
171 | [[NSFileHandle fileHandleWithStandardInput] readDataToEndOfFile];
172 | }) : ({
173 | NSURL *fileURL =[NSURL fileURLWithPath:[filenameString stringByStandardizingPath]];
174 | [NSData dataWithContentsOfURL:fileURL];
175 | });
176 |
177 |
178 | NSError *error;
179 | NSDictionary *tomlObject;
180 | NSDictionary *dictionaryObject;
181 |
182 | if (inputData.length > 0) {
183 |
184 | // determine input format
185 | if (inputFormat == FileFormatUnknown) {
186 | inputFormat = formatFromFilename(filenameString);
187 | }
188 |
189 | if (inputFormat == FileFormatUnknown) {
190 | // check first bytes
191 | char *bytes = (char *)inputData.bytes;
192 | if (bytes[0] == '<' &&
193 | bytes[1] == '?') {
194 | inputFormat = FileFormatPlistXML;
195 | } else if ([inputData length] > 6 && [[[NSString alloc] initWithBytes:bytes length:6 encoding:NSASCIIStringEncoding] isEqualToString:@"bplist"]) {
196 | inputFormat = FileFormatPlistBinary;
197 | } else if (bytes[0] == '{') {
198 | inputFormat = FileFormatJSON;
199 | }
200 | }
201 |
202 | if (inputFormat == FileFormatUnknown) {
203 | inputFormat = FileFormatTOML; // default to TOML if nothing else was found
204 | }
205 |
206 | if (inputFormat == FileFormatTOML) {
207 | tomlObject = [LMPTOMLSerialization TOMLObjectWithData:inputData options:filenameString ? @{LMPTOMLOptionKeySourceFileURL : [NSURL fileURLWithPath:filenameString]} : nil error:&error];
208 | showErrorAndHalt(error, inputData);
209 | } else if (inputFormat == FileFormatJSON) {
210 | NSJSONReadingOptions opts = NSJSONReadingFragmentsAllowed;
211 | if (@available(macOS 12.0, *)) {
212 | opts |= NSJSONReadingJSON5Allowed;
213 | }
214 | dictionaryObject = [NSJSONSerialization JSONObjectWithData:inputData options:opts error:&error];
215 | showErrorAndHalt(error, inputData);
216 | } else {
217 | dictionaryObject = [NSPropertyListSerialization propertyListWithData:inputData options:0 format:nil error:&error];
218 | showErrorAndHalt(error, inputData);
219 | }
220 | } else if (inputData) { // empty input produces empty output
221 | dictionaryObject = @{};
222 | } else {
223 | showErrorAndHalt([NSError errorWithDomain:NSPOSIXErrorDomain code:999 userInfo:@{
224 | NSLocalizedDescriptionKey : [NSString stringWithFormat:@"Failure to read input (%@)", filenameString ?: @"stdin"],
225 | NSLocalizedFailureReasonErrorKey : [argumentsArray componentsJoinedByString:@" "],
226 | }], nil);
227 | }
228 |
229 | if (outputFormat == FileFormatNone) {
230 | exit(EXIT_SUCCESS);
231 | } else if (outputFormat == FileFormatUnknown) {
232 | if (outputFilenameString) {
233 | outputFormat = formatFromFilename(outputFilenameString);
234 | }
235 | if (outputFormat == FileFormatUnknown) {
236 | outputFormat = FileFormatTOML;
237 | }
238 | }
239 |
240 | NSData *outputData;
241 | if (outputFormat == FileFormatTOML) {
242 | outputData = [LMPTOMLSerialization dataWithTOMLObject:tomlObject ?: dictionaryObject error:&error];
243 | showErrorAndHalt(error, inputData);
244 | } else {
245 | if (!dictionaryObject) {
246 | dictionaryObject = [LMPTOMLSerialization serializableObjectWithTOMLObject:tomlObject];
247 | }
248 | @try {
249 | if (outputFormat == FileFormatJSON) {
250 | NSJSONWritingOptions options = NSJSONWritingPrettyPrinted;
251 | if (@available(macOS 10.13, *)) {
252 | options |= NSJSONWritingSortedKeys;
253 | }
254 | options |= NSJSONWritingWithoutEscapingSlashes;
255 |
256 | outputData = [NSJSONSerialization dataWithJSONObject:dictionaryObject options:options error:&error];
257 | showErrorAndHalt(error, inputData);
258 | } else {
259 | outputData = [NSPropertyListSerialization dataWithPropertyList:dictionaryObject format:(NSPropertyListFormat)outputFormat options:0 error:&error];
260 | showErrorAndHalt(error, inputData);
261 | }
262 | }
263 | @catch (NSException *exception) {
264 | error = [NSError errorWithDomain:NSCocoaErrorDomain code:1234 userInfo:@{
265 | NSLocalizedDescriptionKey : [NSString stringWithFormat:@"Exception in Foundation while serializing: %@", exception.name],
266 | NSLocalizedFailureReasonErrorKey : exception.reason,
267 | }
268 | ];
269 | showErrorAndHalt(error, nil);
270 | }
271 | }
272 |
273 | if (outputFilenameString) {
274 | NSError *error;
275 | [outputData writeToURL:[NSURL fileURLWithPath:[outputFilenameString stringByStandardizingPath]] options:NSDataWritingAtomic error:&error];
276 | showErrorAndHalt(error, nil);
277 | } else {
278 | [[NSFileHandle fileHandleWithStandardOutput] writeData:outputData];
279 | }
280 | }
281 | }
282 | return EXIT_SUCCESS;
283 | }
284 |
--------------------------------------------------------------------------------
/tomlutil.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | F22CB70B217B1FD600D1565D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F22CB70A217B1FD600D1565D /* main.m */; };
11 | F22CB714217B239300D1565D /* LMPTOMLSerialization.mm in Sources */ = {isa = PBXBuildFile; fileRef = F22CB713217B239300D1565D /* LMPTOMLSerialization.mm */; };
12 | /* End PBXBuildFile section */
13 |
14 | /* Begin PBXCopyFilesBuildPhase section */
15 | F22CB705217B1FD600D1565D /* CopyFiles */ = {
16 | isa = PBXCopyFilesBuildPhase;
17 | buildActionMask = 2147483647;
18 | dstPath = /usr/share/man/man1/;
19 | dstSubfolderSpec = 0;
20 | files = (
21 | );
22 | runOnlyForDeploymentPostprocessing = 1;
23 | };
24 | /* End PBXCopyFilesBuildPhase section */
25 |
26 | /* Begin PBXFileReference section */
27 | F22CB707217B1FD600D1565D /* tomlutil */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = tomlutil; sourceTree = BUILT_PRODUCTS_DIR; };
28 | F22CB70A217B1FD600D1565D /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
29 | F22CB712217B239300D1565D /* LMPTOMLSerialization.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LMPTOMLSerialization.h; sourceTree = ""; };
30 | F22CB713217B239300D1565D /* LMPTOMLSerialization.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = LMPTOMLSerialization.mm; sourceTree = ""; };
31 | F23131842663608400AFE2BA /* toml.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = toml.hpp; path = submodules/toml11/toml.hpp; sourceTree = SOURCE_ROOT; };
32 | F2657CF126F4D26B00C279B9 /* tomlutil */ = {isa = PBXFileReference; lastKnownFileType = text; path = tomlutil; sourceTree = BUILT_PRODUCTS_DIR; };
33 | F2962202217F303B00C44751 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
34 | F2BC7534217B44D10056FAED /* LMP_toml_visitors.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LMP_toml_visitors.h; sourceTree = ""; };
35 | F2FAE3732663A3DC0080F81A /* region.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = region.hpp; sourceTree = ""; };
36 | F2FAE3742663A3DC0080F81A /* datetime.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = datetime.hpp; sourceTree = ""; };
37 | F2FAE3752663A3DC0080F81A /* serializer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = serializer.hpp; sourceTree = ""; };
38 | F2FAE3762663A3DC0080F81A /* utility.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = utility.hpp; sourceTree = ""; };
39 | F2FAE3772663A3DC0080F81A /* get.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = get.hpp; sourceTree = ""; };
40 | F2FAE3782663A3DC0080F81A /* parser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = parser.hpp; sourceTree = ""; };
41 | F2FAE3792663A3DC0080F81A /* literal.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = literal.hpp; sourceTree = ""; };
42 | F2FAE37A2663A3DC0080F81A /* macros.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = macros.hpp; sourceTree = ""; };
43 | F2FAE37B2663A3DC0080F81A /* traits.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = traits.hpp; sourceTree = ""; };
44 | F2FAE37C2663A3DC0080F81A /* comments.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = comments.hpp; sourceTree = ""; };
45 | F2FAE37D2663A3DC0080F81A /* source_location.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = source_location.hpp; sourceTree = ""; };
46 | F2FAE37E2663A3DC0080F81A /* combinator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = combinator.hpp; sourceTree = ""; };
47 | F2FAE37F2663A3DC0080F81A /* value.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = value.hpp; sourceTree = ""; };
48 | F2FAE3802663A3DC0080F81A /* from.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = from.hpp; sourceTree = ""; };
49 | F2FAE3812663A3DC0080F81A /* result.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = result.hpp; sourceTree = ""; };
50 | F2FAE3822663A3DC0080F81A /* into.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = into.hpp; sourceTree = ""; };
51 | F2FAE3832663A3DC0080F81A /* lexer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = lexer.hpp; sourceTree = ""; };
52 | F2FAE3842663A3DC0080F81A /* string.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = string.hpp; sourceTree = ""; };
53 | F2FAE3852663A3DC0080F81A /* storage.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = storage.hpp; sourceTree = ""; };
54 | F2FAE3862663A3DC0080F81A /* color.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = color.hpp; sourceTree = ""; };
55 | F2FAE3872663A3DC0080F81A /* types.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = types.hpp; sourceTree = ""; };
56 | F2FAE3882663A3DC0080F81A /* exception.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = exception.hpp; sourceTree = ""; };
57 | /* End PBXFileReference section */
58 |
59 | /* Begin PBXFrameworksBuildPhase section */
60 | F22CB704217B1FD600D1565D /* Frameworks */ = {
61 | isa = PBXFrameworksBuildPhase;
62 | buildActionMask = 2147483647;
63 | files = (
64 | );
65 | runOnlyForDeploymentPostprocessing = 0;
66 | };
67 | /* End PBXFrameworksBuildPhase section */
68 |
69 | /* Begin PBXGroup section */
70 | F22CB6FE217B1FD600D1565D = {
71 | isa = PBXGroup;
72 | children = (
73 | F22CB709217B1FD600D1565D /* tomlutil */,
74 | F22CB708217B1FD600D1565D /* Products */,
75 | );
76 | sourceTree = "";
77 | };
78 | F22CB708217B1FD600D1565D /* Products */ = {
79 | isa = PBXGroup;
80 | children = (
81 | F22CB707217B1FD600D1565D /* tomlutil */,
82 | );
83 | name = Products;
84 | sourceTree = "";
85 | };
86 | F22CB709217B1FD600D1565D /* tomlutil */ = {
87 | isa = PBXGroup;
88 | children = (
89 | F22CB70A217B1FD600D1565D /* main.m */,
90 | F22CB712217B239300D1565D /* LMPTOMLSerialization.h */,
91 | F22CB713217B239300D1565D /* LMPTOMLSerialization.mm */,
92 | F2BC7534217B44D10056FAED /* LMP_toml_visitors.h */,
93 | F23131842663608400AFE2BA /* toml.hpp */,
94 | F2FAE3722663A3DC0080F81A /* toml */,
95 | F2962202217F303B00C44751 /* Info.plist */,
96 | F2657CF026F4D24E00C279B9 /* Products */,
97 | );
98 | path = tomlutil;
99 | sourceTree = "";
100 | };
101 | F2657CF026F4D24E00C279B9 /* Products */ = {
102 | isa = PBXGroup;
103 | children = (
104 | F2657CF126F4D26B00C279B9 /* tomlutil */,
105 | );
106 | name = Products;
107 | sourceTree = "";
108 | };
109 | F2FAE3722663A3DC0080F81A /* toml */ = {
110 | isa = PBXGroup;
111 | children = (
112 | F2FAE3732663A3DC0080F81A /* region.hpp */,
113 | F2FAE3742663A3DC0080F81A /* datetime.hpp */,
114 | F2FAE3752663A3DC0080F81A /* serializer.hpp */,
115 | F2FAE3762663A3DC0080F81A /* utility.hpp */,
116 | F2FAE3772663A3DC0080F81A /* get.hpp */,
117 | F2FAE3782663A3DC0080F81A /* parser.hpp */,
118 | F2FAE3792663A3DC0080F81A /* literal.hpp */,
119 | F2FAE37A2663A3DC0080F81A /* macros.hpp */,
120 | F2FAE37B2663A3DC0080F81A /* traits.hpp */,
121 | F2FAE37C2663A3DC0080F81A /* comments.hpp */,
122 | F2FAE37D2663A3DC0080F81A /* source_location.hpp */,
123 | F2FAE37E2663A3DC0080F81A /* combinator.hpp */,
124 | F2FAE37F2663A3DC0080F81A /* value.hpp */,
125 | F2FAE3802663A3DC0080F81A /* from.hpp */,
126 | F2FAE3812663A3DC0080F81A /* result.hpp */,
127 | F2FAE3822663A3DC0080F81A /* into.hpp */,
128 | F2FAE3832663A3DC0080F81A /* lexer.hpp */,
129 | F2FAE3842663A3DC0080F81A /* string.hpp */,
130 | F2FAE3852663A3DC0080F81A /* storage.hpp */,
131 | F2FAE3862663A3DC0080F81A /* color.hpp */,
132 | F2FAE3872663A3DC0080F81A /* types.hpp */,
133 | F2FAE3882663A3DC0080F81A /* exception.hpp */,
134 | );
135 | name = toml;
136 | path = submodules/toml11/toml;
137 | sourceTree = SOURCE_ROOT;
138 | };
139 | /* End PBXGroup section */
140 |
141 | /* Begin PBXNativeTarget section */
142 | F22CB706217B1FD600D1565D /* tomlutil */ = {
143 | isa = PBXNativeTarget;
144 | buildConfigurationList = F22CB70E217B1FD600D1565D /* Build configuration list for PBXNativeTarget "tomlutil" */;
145 | buildPhases = (
146 | F22CB703217B1FD600D1565D /* Sources */,
147 | F22CB704217B1FD600D1565D /* Frameworks */,
148 | F22CB705217B1FD600D1565D /* CopyFiles */,
149 | );
150 | buildRules = (
151 | );
152 | dependencies = (
153 | );
154 | name = tomlutil;
155 | productName = tomlutil;
156 | productReference = F22CB707217B1FD600D1565D /* tomlutil */;
157 | productType = "com.apple.product-type.tool";
158 | };
159 | /* End PBXNativeTarget section */
160 |
161 | /* Begin PBXProject section */
162 | F22CB6FF217B1FD600D1565D /* Project object */ = {
163 | isa = PBXProject;
164 | attributes = {
165 | LastUpgradeCheck = 1300;
166 | ORGANIZATIONNAME = "Lone Monkey Productions";
167 | TargetAttributes = {
168 | F22CB706217B1FD600D1565D = {
169 | CreatedOnToolsVersion = 10.0;
170 | };
171 | };
172 | };
173 | buildConfigurationList = F22CB702217B1FD600D1565D /* Build configuration list for PBXProject "tomlutil" */;
174 | compatibilityVersion = "Xcode 9.3";
175 | developmentRegion = en;
176 | hasScannedForEncodings = 0;
177 | knownRegions = (
178 | en,
179 | );
180 | mainGroup = F22CB6FE217B1FD600D1565D;
181 | productRefGroup = F22CB708217B1FD600D1565D /* Products */;
182 | projectDirPath = "";
183 | projectRoot = "";
184 | targets = (
185 | F22CB706217B1FD600D1565D /* tomlutil */,
186 | );
187 | };
188 | /* End PBXProject section */
189 |
190 | /* Begin PBXSourcesBuildPhase section */
191 | F22CB703217B1FD600D1565D /* Sources */ = {
192 | isa = PBXSourcesBuildPhase;
193 | buildActionMask = 2147483647;
194 | files = (
195 | F22CB714217B239300D1565D /* LMPTOMLSerialization.mm in Sources */,
196 | F22CB70B217B1FD600D1565D /* main.m in Sources */,
197 | );
198 | runOnlyForDeploymentPostprocessing = 0;
199 | };
200 | /* End PBXSourcesBuildPhase section */
201 |
202 | /* Begin XCBuildConfiguration section */
203 | F22CB70C217B1FD600D1565D /* Debug */ = {
204 | isa = XCBuildConfiguration;
205 | buildSettings = {
206 | ALWAYS_SEARCH_USER_PATHS = NO;
207 | CLANG_ANALYZER_NONNULL = YES;
208 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
209 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
210 | CLANG_CXX_LIBRARY = "libc++";
211 | CLANG_ENABLE_MODULES = YES;
212 | CLANG_ENABLE_OBJC_ARC = YES;
213 | CLANG_ENABLE_OBJC_WEAK = YES;
214 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
215 | CLANG_WARN_BOOL_CONVERSION = YES;
216 | CLANG_WARN_COMMA = YES;
217 | CLANG_WARN_CONSTANT_CONVERSION = YES;
218 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
219 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
220 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
221 | CLANG_WARN_EMPTY_BODY = YES;
222 | CLANG_WARN_ENUM_CONVERSION = YES;
223 | CLANG_WARN_INFINITE_RECURSION = YES;
224 | CLANG_WARN_INT_CONVERSION = YES;
225 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
226 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
227 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
228 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
229 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
230 | CLANG_WARN_STRICT_PROTOTYPES = YES;
231 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
232 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
233 | CLANG_WARN_UNREACHABLE_CODE = YES;
234 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
235 | CODE_SIGN_IDENTITY = "-";
236 | COPY_PHASE_STRIP = NO;
237 | DEBUG_INFORMATION_FORMAT = dwarf;
238 | ENABLE_STRICT_OBJC_MSGSEND = YES;
239 | ENABLE_TESTABILITY = YES;
240 | GCC_C_LANGUAGE_STANDARD = gnu11;
241 | GCC_DYNAMIC_NO_PIC = NO;
242 | GCC_NO_COMMON_BLOCKS = YES;
243 | GCC_OPTIMIZATION_LEVEL = 0;
244 | GCC_PREPROCESSOR_DEFINITIONS = (
245 | "DEBUG=1",
246 | "$(inherited)",
247 | );
248 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
249 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
250 | GCC_WARN_UNDECLARED_SELECTOR = YES;
251 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
252 | GCC_WARN_UNUSED_FUNCTION = YES;
253 | GCC_WARN_UNUSED_VARIABLE = YES;
254 | MACOSX_DEPLOYMENT_TARGET = 10.15;
255 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
256 | MTL_FAST_MATH = YES;
257 | ONLY_ACTIVE_ARCH = YES;
258 | SDKROOT = macosx;
259 | };
260 | name = Debug;
261 | };
262 | F22CB70D217B1FD600D1565D /* Release */ = {
263 | isa = XCBuildConfiguration;
264 | buildSettings = {
265 | ALWAYS_SEARCH_USER_PATHS = NO;
266 | CLANG_ANALYZER_NONNULL = YES;
267 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
268 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
269 | CLANG_CXX_LIBRARY = "libc++";
270 | CLANG_ENABLE_MODULES = YES;
271 | CLANG_ENABLE_OBJC_ARC = YES;
272 | CLANG_ENABLE_OBJC_WEAK = YES;
273 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
274 | CLANG_WARN_BOOL_CONVERSION = YES;
275 | CLANG_WARN_COMMA = YES;
276 | CLANG_WARN_CONSTANT_CONVERSION = YES;
277 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
278 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
279 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
280 | CLANG_WARN_EMPTY_BODY = YES;
281 | CLANG_WARN_ENUM_CONVERSION = YES;
282 | CLANG_WARN_INFINITE_RECURSION = YES;
283 | CLANG_WARN_INT_CONVERSION = YES;
284 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
285 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
286 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
287 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
288 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
289 | CLANG_WARN_STRICT_PROTOTYPES = YES;
290 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
291 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
292 | CLANG_WARN_UNREACHABLE_CODE = YES;
293 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
294 | CODE_SIGN_IDENTITY = "-";
295 | COPY_PHASE_STRIP = NO;
296 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
297 | ENABLE_NS_ASSERTIONS = NO;
298 | ENABLE_STRICT_OBJC_MSGSEND = YES;
299 | GCC_C_LANGUAGE_STANDARD = gnu11;
300 | GCC_NO_COMMON_BLOCKS = YES;
301 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
302 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
303 | GCC_WARN_UNDECLARED_SELECTOR = YES;
304 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
305 | GCC_WARN_UNUSED_FUNCTION = YES;
306 | GCC_WARN_UNUSED_VARIABLE = YES;
307 | MACOSX_DEPLOYMENT_TARGET = 10.15;
308 | MTL_ENABLE_DEBUG_INFO = NO;
309 | MTL_FAST_MATH = YES;
310 | SDKROOT = macosx;
311 | };
312 | name = Release;
313 | };
314 | F22CB70F217B1FD600D1565D /* Debug */ = {
315 | isa = XCBuildConfiguration;
316 | buildSettings = {
317 | CODE_SIGN_STYLE = Automatic;
318 | CREATE_INFOPLIST_SECTION_IN_BINARY = YES;
319 | INFOPLIST_FILE = tomlutil/Info.plist;
320 | PRODUCT_BUNDLE_IDENTIFIER = productions.monkey.lone.tomlutil;
321 | PRODUCT_NAME = "$(TARGET_NAME)";
322 | };
323 | name = Debug;
324 | };
325 | F22CB710217B1FD600D1565D /* Release */ = {
326 | isa = XCBuildConfiguration;
327 | buildSettings = {
328 | CODE_SIGN_STYLE = Automatic;
329 | CREATE_INFOPLIST_SECTION_IN_BINARY = YES;
330 | INFOPLIST_FILE = tomlutil/Info.plist;
331 | PRODUCT_BUNDLE_IDENTIFIER = productions.monkey.lone.tomlutil;
332 | PRODUCT_NAME = "$(TARGET_NAME)";
333 | };
334 | name = Release;
335 | };
336 | /* End XCBuildConfiguration section */
337 |
338 | /* Begin XCConfigurationList section */
339 | F22CB702217B1FD600D1565D /* Build configuration list for PBXProject "tomlutil" */ = {
340 | isa = XCConfigurationList;
341 | buildConfigurations = (
342 | F22CB70C217B1FD600D1565D /* Debug */,
343 | F22CB70D217B1FD600D1565D /* Release */,
344 | );
345 | defaultConfigurationIsVisible = 0;
346 | defaultConfigurationName = Release;
347 | };
348 | F22CB70E217B1FD600D1565D /* Build configuration list for PBXNativeTarget "tomlutil" */ = {
349 | isa = XCConfigurationList;
350 | buildConfigurations = (
351 | F22CB70F217B1FD600D1565D /* Debug */,
352 | F22CB710217B1FD600D1565D /* Release */,
353 | );
354 | defaultConfigurationIsVisible = 0;
355 | defaultConfigurationName = Release;
356 | };
357 | /* End XCConfigurationList section */
358 | };
359 | rootObject = F22CB6FF217B1FD600D1565D /* Project object */;
360 | }
361 |
--------------------------------------------------------------------------------