Compare commits
245 Commits
Author | SHA1 | Date |
---|---|---|
![]() |
81d2874c51 | |
![]() |
8a3b2328a5 | |
![]() |
708202871d | |
![]() |
2fd6693009 | |
![]() |
f8a90ce8bc | |
![]() |
ee138a8572 | |
![]() |
e8e82ac6fb | |
![]() |
f68c752e4a | |
![]() |
b12e3df560 | |
![]() |
67c5209ae6 | |
![]() |
1a2aebf797 | |
![]() |
58fee7bcab | |
![]() |
2797e65776 | |
![]() |
022af3f3e1 | |
![]() |
b95c94934f | |
![]() |
7ea1f25307 | |
![]() |
0f9d4bfd27 | |
![]() |
b11cfb960f | |
![]() |
126841fae4 | |
![]() |
ee50dd334d | |
![]() |
266f27e047 | |
![]() |
b5514ec6f3 | |
![]() |
8d3e55cfcb | |
![]() |
f99b3f4ba8 | |
![]() |
eb004b3773 | |
![]() |
a22693a7d4 | |
![]() |
849e82c73c | |
![]() |
33dbff0577 | |
![]() |
af907b46f7 | |
![]() |
0de3fcdb9e | |
![]() |
4da0ec833e | |
![]() |
9eef621761 | |
![]() |
644f0a1cbd | |
![]() |
6e1db1513d | |
![]() |
c88aca7850 | |
![]() |
dbf6697a9f | |
![]() |
dd0effcc80 | |
![]() |
38ec4535ae | |
![]() |
521b6c15da | |
![]() |
2df12cddca | |
![]() |
c18ee80f02 | |
![]() |
7658b390fd | |
![]() |
48f051fb7d | |
![]() |
7d10f1f7f6 | |
![]() |
61d2ce8a00 | |
![]() |
2af710083e | |
![]() |
4dcc07721b | |
![]() |
bf2e166146 | |
![]() |
fdd4e34621 | |
![]() |
3990cb04f4 | |
![]() |
5a86d95312 | |
![]() |
f96fb1c632 | |
![]() |
8158330910 | |
![]() |
9b35f60a09 | |
![]() |
13f9e75ef2 | |
![]() |
aebee51287 | |
![]() |
fb2e641345 | |
![]() |
1e63a5438d | |
![]() |
ff764ff8a9 | |
![]() |
9352ed330c | |
![]() |
798e696202 | |
![]() |
02ae6c51f1 | |
![]() |
2218559821 | |
![]() |
25768e174a | |
![]() |
d124b1a99a | |
![]() |
1e83c6e3b9 | |
![]() |
329bf23405 | |
![]() |
b01cbb358d | |
![]() |
40a8f59f88 | |
![]() |
e12c86f170 | |
![]() |
826ce290a2 | |
![]() |
66945c0891 | |
![]() |
028bd2f5a8 | |
![]() |
6a189246ba | |
![]() |
0669601280 | |
![]() |
68a7e0469a | |
![]() |
15540460ab | |
![]() |
cce830e35c | |
![]() |
9739fc74c6 | |
![]() |
487816c0d1 | |
![]() |
5556448ccc | |
![]() |
032be4276e | |
![]() |
22fd4ee97c | |
![]() |
1c40a3b1ec | |
![]() |
f6690b15a4 | |
![]() |
749e7a5648 | |
![]() |
59dc282e80 | |
![]() |
261a9a06c1 | |
![]() |
c1c78d75ca | |
![]() |
7b20172a9c | |
![]() |
d47c168117 | |
![]() |
3e51573eb7 | |
![]() |
5d6e01ff90 | |
![]() |
2d89f13524 | |
![]() |
244c2aacd2 | |
![]() |
6e01e88697 | |
![]() |
9ecef44708 | |
![]() |
a2a9e3f6e6 | |
![]() |
8e2319ba1d | |
![]() |
cfa034a96c | |
![]() |
17b079f2d5 | |
![]() |
6cf6ab0ae9 | |
![]() |
f1aa9b4016 | |
![]() |
b8e1285b6f | |
![]() |
4c4a5202fa | |
![]() |
e268fa3f3a | |
![]() |
9f0cbddbfc | |
![]() |
54a9d94ae4 | |
![]() |
2410820dc4 | |
![]() |
a3e770943d | |
![]() |
c318c8414d | |
![]() |
f534685017 | |
![]() |
f24b0674c2 | |
![]() |
ce44b39fa5 | |
![]() |
69fe1b65c7 | |
![]() |
0030f5dffd | |
![]() |
21ba14ace9 | |
![]() |
c14ae1dc92 | |
![]() |
17964baee5 | |
![]() |
c1fb896564 | |
![]() |
1f4719e8e2 | |
![]() |
be82f7144c | |
![]() |
5ff2177938 | |
![]() |
88d88c6fae | |
![]() |
34451b9823 | |
![]() |
000c011aa9 | |
![]() |
5c9bbc0db7 | |
![]() |
1ca5d1a558 | |
![]() |
3639a75c33 | |
![]() |
015caedd64 | |
![]() |
0ef7b0eb0e | |
![]() |
7ad80457ab | |
![]() |
cab93830fe | |
![]() |
c3402699e7 | |
![]() |
2a284b6d3d | |
![]() |
71ab28eadd | |
![]() |
7d56f466a9 | |
![]() |
03c2d8bce3 | |
![]() |
d9e0b1d369 | |
![]() |
b0ac142fde | |
![]() |
683e286ac7 | |
![]() |
d9213a7c62 | |
![]() |
cf430d4ed2 | |
![]() |
eb38710590 | |
![]() |
1d72634a03 | |
![]() |
f46887bdbb | |
![]() |
1918e5163d | |
![]() |
376640007e | |
![]() |
3bf2eab076 | |
![]() |
7b9caaf9a8 | |
![]() |
2f7accff1a | |
![]() |
13c29f98ed | |
![]() |
e7405829b1 | |
![]() |
117640b9e9 | |
![]() |
d65943c6d0 | |
![]() |
9a061c28c3 | |
![]() |
d3d9fab63f | |
![]() |
5568da5328 | |
![]() |
aa5fd6ac03 | |
![]() |
7b3d6e73cd | |
![]() |
853233e4a7 | |
![]() |
6428660c99 | |
![]() |
0971f82956 | |
![]() |
9dccfdf92b | |
![]() |
2b7a25a378 | |
![]() |
24a63ffc49 | |
![]() |
cc2437e177 | |
![]() |
b6733645a7 | |
![]() |
485738a95c | |
![]() |
c6cdbd1bd8 | |
![]() |
143a9b9740 | |
![]() |
df1f384079 | |
![]() |
f93b83c26b | |
![]() |
73e8b6ec01 | |
![]() |
8fcb59b43c | |
![]() |
bdefb44a91 | |
![]() |
f0f0ea84de | |
![]() |
597272db9b | |
![]() |
28c54df853 | |
![]() |
be8d1fda41 | |
![]() |
0b0fcd8cbb | |
![]() |
40e34fc990 | |
![]() |
ac6d663703 | |
![]() |
fde92925fc | |
![]() |
3834bbbb4f | |
![]() |
345264273c | |
![]() |
3d502204f6 | |
![]() |
3bd51605e7 | |
![]() |
44b4857396 | |
![]() |
f1ef6a7270 | |
![]() |
479913f0f0 | |
![]() |
acc4be2a35 | |
![]() |
078c5be1c6 | |
![]() |
406c4032a5 | |
![]() |
22c582b14c | |
![]() |
0715a1db99 | |
![]() |
4f29117767 | |
![]() |
8ad3238b16 | |
![]() |
19e3ee98fa | |
![]() |
1ca77e6ae7 | |
![]() |
b2658d4d04 | |
![]() |
1c2961abff | |
![]() |
f136093918 | |
![]() |
cdeb78d3c0 | |
![]() |
96ddb0d017 | |
![]() |
5e84f5e8d4 | |
![]() |
e010536e1e | |
![]() |
4c9bb24cb4 | |
![]() |
0acd6ddd20 | |
![]() |
5c3228e218 | |
![]() |
fe12a10546 | |
![]() |
7cd1d968dc | |
![]() |
445882ac94 | |
![]() |
2703bb9385 | |
![]() |
85e1ece74c | |
![]() |
0dfc025b79 | |
![]() |
d1f55063e3 | |
![]() |
b0f8c83698 | |
![]() |
29d0f2b091 | |
![]() |
4063a440e5 | |
![]() |
26b4bd153a | |
![]() |
0718eab0bb | |
![]() |
90d9ca16a3 | |
![]() |
ccadfd499e | |
![]() |
f9df2e5c9d | |
![]() |
1f2efdfd0f | |
![]() |
58f9399e57 | |
![]() |
db64ab561f | |
![]() |
69ea597f6d | |
![]() |
27fbe635b9 | |
![]() |
b29dfe4e94 | |
![]() |
b7e7815d8a | |
![]() |
36543bcdb5 | |
![]() |
47f4cad5aa | |
![]() |
d5e00a2832 | |
![]() |
484074ade5 | |
![]() |
7cb02fdc43 | |
![]() |
bec2704832 | |
![]() |
e3842e3db7 | |
![]() |
0f1bbf9f89 | |
![]() |
8dd5eb1fb3 | |
![]() |
c8dd3b1aa3 | |
![]() |
83623f140b | |
![]() |
5a1e66c379 | |
![]() |
b65249c15d |
|
@ -0,0 +1,11 @@
|
||||||
|
disabled_rules:
|
||||||
|
- force_cast
|
||||||
|
- force_try
|
||||||
|
- variable_name
|
||||||
|
included:
|
||||||
|
- Sources
|
||||||
|
line_length: 300
|
||||||
|
file_length: 1500
|
||||||
|
cyclomatic_complexity:
|
||||||
|
warning: 15
|
||||||
|
error: 20
|
14
.travis.yml
14
.travis.yml
|
@ -1,12 +1,8 @@
|
||||||
language: objective-c
|
language: swift
|
||||||
osx_image: xcode7.3
|
osx_image: xcode11.3
|
||||||
env:
|
|
||||||
- LC_CTYPE=en_US.UTF-8
|
|
||||||
git:
|
|
||||||
submodules: false
|
|
||||||
before_install:
|
|
||||||
- git submodule update --init --recursive
|
|
||||||
script:
|
script:
|
||||||
- set -o pipefail && xcodebuild test -scheme CSV-OSX
|
- set -o pipefail && xcodebuild test -scheme CSV-macOS
|
||||||
|
after_success:
|
||||||
|
- bash <(curl -s https://codecov.io/bash)
|
||||||
notifications:
|
notifications:
|
||||||
email: false
|
email: false
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
Pod::Spec.new do |s|
|
Pod::Spec.new do |spec|
|
||||||
s.name = 'CSV.swift'
|
spec.name = 'CSV.swift'
|
||||||
s.version = '0.1.0'
|
spec.version = '2.4.3'
|
||||||
s.license = 'MIT'
|
spec.license = { :type => 'MIT' }
|
||||||
s.summary = 'CSV reading library written in Swift.'
|
spec.homepage = 'https://github.com/yaslab/CSV.swift'
|
||||||
s.homepage = 'https://github.com/yaslab/CSV.swift'
|
spec.authors = { 'Yasuhiro Hatta' => 'hatta.yasuhiro@gmail.com' }
|
||||||
s.authors = { 'Yasuhiro Hatta' => 'hatta.yasuhiro@gmail.com' }
|
spec.summary = 'CSV reading and writing library written in Swift.'
|
||||||
s.source = { :git => 'https://github.com/yaslab/CSV.swift.git', :tag => s.version }
|
spec.source = { :git => 'https://github.com/yaslab/CSV.swift.git', :tag => spec.version }
|
||||||
|
spec.source_files = 'Sources/CSV/*.swift'
|
||||||
|
|
||||||
s.osx.deployment_target = '10.9'
|
spec.ios.deployment_target = '8.0'
|
||||||
s.ios.deployment_target = '8.0'
|
spec.tvos.deployment_target = '9.0'
|
||||||
s.tvos.deployment_target = '9.0'
|
spec.watchos.deployment_target = '2.0'
|
||||||
s.watchos.deployment_target = '2.0'
|
spec.osx.deployment_target = '10.9'
|
||||||
|
|
||||||
s.module_name = 'CSV'
|
spec.module_name = 'CSV'
|
||||||
s.source_files = 'Sources/*.swift'
|
spec.swift_version = '5.0'
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,31 +7,72 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
0E54021B1ED9DDF40019C3ED /* CSVWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E54021A1ED9DDF40019C3ED /* CSVWriterTests.swift */; };
|
||||||
|
0E54021C1ED9DDF40019C3ED /* CSVWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E54021A1ED9DDF40019C3ED /* CSVWriterTests.swift */; };
|
||||||
|
0E54021D1ED9DDF40019C3ED /* CSVWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E54021A1ED9DDF40019C3ED /* CSVWriterTests.swift */; };
|
||||||
0E7E8C8C1D0BC7BB0057A1C1 /* CSV.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E7E8C811D0BC7BB0057A1C1 /* CSV.framework */; };
|
0E7E8C8C1D0BC7BB0057A1C1 /* CSV.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E7E8C811D0BC7BB0057A1C1 /* CSV.framework */; };
|
||||||
0E7E8CA01D0BC7F10057A1C1 /* ByteOrder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9C1D0BC7F10057A1C1 /* ByteOrder.swift */; };
|
|
||||||
0E7E8CA11D0BC7F10057A1C1 /* CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */; };
|
|
||||||
0E7E8CA21D0BC7F10057A1C1 /* CSVError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9E1D0BC7F10057A1C1 /* CSVError.swift */; };
|
|
||||||
0E7E8CA31D0BC7F10057A1C1 /* CSVVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
0E7E8CA31D0BC7F10057A1C1 /* CSVVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
0E7E8CA91D0BC8050057A1C1 /* CSVReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8CA61D0BC8050057A1C1 /* CSVReaderTests.swift */; };
|
|
||||||
0E7E8CAA1D0BC8050057A1C1 /* CSVTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8CA71D0BC8050057A1C1 /* CSVTests.swift */; };
|
|
||||||
0E7E8CBD1D0BC9D70057A1C1 /* ByteOrder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9C1D0BC7F10057A1C1 /* ByteOrder.swift */; };
|
|
||||||
0E7E8CBE1D0BC9D70057A1C1 /* CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */; };
|
|
||||||
0E7E8CBF1D0BC9D70057A1C1 /* CSVError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9E1D0BC7F10057A1C1 /* CSVError.swift */; };
|
|
||||||
0E7E8CC01D0BC9D70057A1C1 /* CSVVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
0E7E8CC01D0BC9D70057A1C1 /* CSVVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
0E7E8CD01D0BCA2A0057A1C1 /* CSV.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E7E8CC61D0BCA2A0057A1C1 /* CSV.framework */; };
|
0E7E8CD01D0BCA2A0057A1C1 /* CSV.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E7E8CC61D0BCA2A0057A1C1 /* CSV.framework */; };
|
||||||
0E7E8CDD1D0BCA840057A1C1 /* CSVReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8CA61D0BC8050057A1C1 /* CSVReaderTests.swift */; };
|
|
||||||
0E7E8CDE1D0BCA840057A1C1 /* CSVTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8CA71D0BC8050057A1C1 /* CSVTests.swift */; };
|
|
||||||
0E7E8CDF1D0BCA8E0057A1C1 /* ByteOrder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9C1D0BC7F10057A1C1 /* ByteOrder.swift */; };
|
|
||||||
0E7E8CE01D0BCA8E0057A1C1 /* CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */; };
|
|
||||||
0E7E8CE11D0BCA8E0057A1C1 /* CSVError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9E1D0BC7F10057A1C1 /* CSVError.swift */; };
|
|
||||||
0E7E8CE21D0BCA8E0057A1C1 /* CSVVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
0E7E8CE21D0BCA8E0057A1C1 /* CSVVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
0E7E8CF21D0BCD0B0057A1C1 /* CSV.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E7E8CE81D0BCD0B0057A1C1 /* CSV.framework */; };
|
0E7E8CF21D0BCD0B0057A1C1 /* CSV.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E7E8CE81D0BCD0B0057A1C1 /* CSV.framework */; };
|
||||||
0E7E8CFF1D0BCDCF0057A1C1 /* ByteOrder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9C1D0BC7F10057A1C1 /* ByteOrder.swift */; };
|
|
||||||
0E7E8D001D0BCDCF0057A1C1 /* CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */; };
|
|
||||||
0E7E8D011D0BCDCF0057A1C1 /* CSVError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9E1D0BC7F10057A1C1 /* CSVError.swift */; };
|
|
||||||
0E7E8D021D0BCDCF0057A1C1 /* CSVVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
0E7E8D021D0BCDCF0057A1C1 /* CSVVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
0E7E8D031D0BCDDD0057A1C1 /* CSVReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8CA61D0BC8050057A1C1 /* CSVReaderTests.swift */; };
|
0E7F657B1EF6437E00E1E1A0 /* Version1Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7F657A1EF6437E00E1E1A0 /* Version1Tests.swift */; };
|
||||||
0E7E8D041D0BCDDD0057A1C1 /* CSVTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8CA71D0BC8050057A1C1 /* CSVTests.swift */; };
|
0E7F657C1EF6437E00E1E1A0 /* Version1Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7F657A1EF6437E00E1E1A0 /* Version1Tests.swift */; };
|
||||||
|
0E7F657D1EF6437E00E1E1A0 /* Version1Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7F657A1EF6437E00E1E1A0 /* Version1Tests.swift */; };
|
||||||
|
0E87607B219D992900C6C7FA /* BinaryReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E87607A219D992900C6C7FA /* BinaryReaderTests.swift */; };
|
||||||
|
0E87607C219D992900C6C7FA /* BinaryReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E87607A219D992900C6C7FA /* BinaryReaderTests.swift */; };
|
||||||
|
0E87607D219D992900C6C7FA /* BinaryReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E87607A219D992900C6C7FA /* BinaryReaderTests.swift */; };
|
||||||
|
0E87607F219FF43B00C6C7FA /* CSVRowDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E87607E219FF43B00C6C7FA /* CSVRowDecoder.swift */; };
|
||||||
|
0E876080219FF43B00C6C7FA /* CSVRowDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E87607E219FF43B00C6C7FA /* CSVRowDecoder.swift */; };
|
||||||
|
0E876081219FF43B00C6C7FA /* CSVRowDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E87607E219FF43B00C6C7FA /* CSVRowDecoder.swift */; };
|
||||||
|
0E876082219FF43B00C6C7FA /* CSVRowDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E87607E219FF43B00C6C7FA /* CSVRowDecoder.swift */; };
|
||||||
|
0E959C62208B611F005F8D01 /* CSVError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C5B208B611F005F8D01 /* CSVError.swift */; };
|
||||||
|
0E959C63208B611F005F8D01 /* CSVError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C5B208B611F005F8D01 /* CSVError.swift */; };
|
||||||
|
0E959C64208B611F005F8D01 /* CSVError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C5B208B611F005F8D01 /* CSVError.swift */; };
|
||||||
|
0E959C65208B611F005F8D01 /* CSVError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C5B208B611F005F8D01 /* CSVError.swift */; };
|
||||||
|
0E959C66208B611F005F8D01 /* CSVWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C5C208B611F005F8D01 /* CSVWriter.swift */; };
|
||||||
|
0E959C67208B611F005F8D01 /* CSVWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C5C208B611F005F8D01 /* CSVWriter.swift */; };
|
||||||
|
0E959C68208B611F005F8D01 /* CSVWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C5C208B611F005F8D01 /* CSVWriter.swift */; };
|
||||||
|
0E959C69208B611F005F8D01 /* CSVWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C5C208B611F005F8D01 /* CSVWriter.swift */; };
|
||||||
|
0E959C6A208B611F005F8D01 /* Legacy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C5D208B611F005F8D01 /* Legacy.swift */; };
|
||||||
|
0E959C6B208B611F005F8D01 /* Legacy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C5D208B611F005F8D01 /* Legacy.swift */; };
|
||||||
|
0E959C6C208B611F005F8D01 /* Legacy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C5D208B611F005F8D01 /* Legacy.swift */; };
|
||||||
|
0E959C6D208B611F005F8D01 /* Legacy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C5D208B611F005F8D01 /* Legacy.swift */; };
|
||||||
|
0E959C6E208B611F005F8D01 /* BinaryReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C5E208B611F005F8D01 /* BinaryReader.swift */; };
|
||||||
|
0E959C6F208B611F005F8D01 /* BinaryReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C5E208B611F005F8D01 /* BinaryReader.swift */; };
|
||||||
|
0E959C70208B611F005F8D01 /* BinaryReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C5E208B611F005F8D01 /* BinaryReader.swift */; };
|
||||||
|
0E959C71208B611F005F8D01 /* BinaryReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C5E208B611F005F8D01 /* BinaryReader.swift */; };
|
||||||
|
0E959C72208B611F005F8D01 /* UnicodeIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C5F208B611F005F8D01 /* UnicodeIterator.swift */; };
|
||||||
|
0E959C73208B611F005F8D01 /* UnicodeIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C5F208B611F005F8D01 /* UnicodeIterator.swift */; };
|
||||||
|
0E959C74208B611F005F8D01 /* UnicodeIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C5F208B611F005F8D01 /* UnicodeIterator.swift */; };
|
||||||
|
0E959C75208B611F005F8D01 /* UnicodeIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C5F208B611F005F8D01 /* UnicodeIterator.swift */; };
|
||||||
|
0E959C76208B611F005F8D01 /* Endian.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C60208B611F005F8D01 /* Endian.swift */; };
|
||||||
|
0E959C77208B611F005F8D01 /* Endian.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C60208B611F005F8D01 /* Endian.swift */; };
|
||||||
|
0E959C78208B611F005F8D01 /* Endian.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C60208B611F005F8D01 /* Endian.swift */; };
|
||||||
|
0E959C79208B611F005F8D01 /* Endian.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C60208B611F005F8D01 /* Endian.swift */; };
|
||||||
|
0E959C7A208B611F005F8D01 /* CSVReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C61208B611F005F8D01 /* CSVReader.swift */; };
|
||||||
|
0E959C7B208B611F005F8D01 /* CSVReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C61208B611F005F8D01 /* CSVReader.swift */; };
|
||||||
|
0E959C7C208B611F005F8D01 /* CSVReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C61208B611F005F8D01 /* CSVReader.swift */; };
|
||||||
|
0E959C7D208B611F005F8D01 /* CSVReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E959C61208B611F005F8D01 /* CSVReader.swift */; };
|
||||||
|
0EDF8ED71DDB73520068056A /* CSVReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ECD1DDB73370068056A /* CSVReaderTests.swift */; };
|
||||||
|
0EDF8ED81DDB73520068056A /* LineBreakTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ECE1DDB73370068056A /* LineBreakTests.swift */; };
|
||||||
|
0EDF8ED91DDB73520068056A /* ReadmeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ECF1DDB73370068056A /* ReadmeTests.swift */; };
|
||||||
|
0EDF8EDA1DDB73520068056A /* TrimFieldsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ED01DDB73370068056A /* TrimFieldsTests.swift */; };
|
||||||
|
0EDF8EDB1DDB73520068056A /* UnicodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ED11DDB73370068056A /* UnicodeTests.swift */; };
|
||||||
|
0EDF8EDC1DDB73520068056A /* CSVReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ECD1DDB73370068056A /* CSVReaderTests.swift */; };
|
||||||
|
0EDF8EDD1DDB73520068056A /* LineBreakTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ECE1DDB73370068056A /* LineBreakTests.swift */; };
|
||||||
|
0EDF8EDE1DDB73520068056A /* ReadmeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ECF1DDB73370068056A /* ReadmeTests.swift */; };
|
||||||
|
0EDF8EDF1DDB73520068056A /* TrimFieldsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ED01DDB73370068056A /* TrimFieldsTests.swift */; };
|
||||||
|
0EDF8EE01DDB73520068056A /* UnicodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ED11DDB73370068056A /* UnicodeTests.swift */; };
|
||||||
|
0EDF8EE11DDB73530068056A /* CSVReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ECD1DDB73370068056A /* CSVReaderTests.swift */; };
|
||||||
|
0EDF8EE21DDB73530068056A /* LineBreakTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ECE1DDB73370068056A /* LineBreakTests.swift */; };
|
||||||
|
0EDF8EE31DDB73530068056A /* ReadmeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ECF1DDB73370068056A /* ReadmeTests.swift */; };
|
||||||
|
0EDF8EE41DDB73530068056A /* TrimFieldsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ED01DDB73370068056A /* TrimFieldsTests.swift */; };
|
||||||
|
0EDF8EE51DDB73530068056A /* UnicodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ED11DDB73370068056A /* UnicodeTests.swift */; };
|
||||||
|
3C89219E21484154004AA78A /* CSVRowDecoderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C89219D21484153004AA78A /* CSVRowDecoderTests.swift */; };
|
||||||
|
3C89219F21484154004AA78A /* CSVRowDecoderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C89219D21484153004AA78A /* CSVRowDecoderTests.swift */; };
|
||||||
|
3C8921A021484154004AA78A /* CSVRowDecoderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C89219D21484153004AA78A /* CSVRowDecoderTests.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
@ -40,14 +81,14 @@
|
||||||
containerPortal = 0E7E8C781D0BC7BB0057A1C1 /* Project object */;
|
containerPortal = 0E7E8C781D0BC7BB0057A1C1 /* Project object */;
|
||||||
proxyType = 1;
|
proxyType = 1;
|
||||||
remoteGlobalIDString = 0E7E8C801D0BC7BB0057A1C1;
|
remoteGlobalIDString = 0E7E8C801D0BC7BB0057A1C1;
|
||||||
remoteInfo = CSV;
|
remoteInfo = "CSV-iOS";
|
||||||
};
|
};
|
||||||
0E7E8CD11D0BCA2A0057A1C1 /* PBXContainerItemProxy */ = {
|
0E7E8CD11D0BCA2A0057A1C1 /* PBXContainerItemProxy */ = {
|
||||||
isa = PBXContainerItemProxy;
|
isa = PBXContainerItemProxy;
|
||||||
containerPortal = 0E7E8C781D0BC7BB0057A1C1 /* Project object */;
|
containerPortal = 0E7E8C781D0BC7BB0057A1C1 /* Project object */;
|
||||||
proxyType = 1;
|
proxyType = 1;
|
||||||
remoteGlobalIDString = 0E7E8CC51D0BCA2A0057A1C1;
|
remoteGlobalIDString = 0E7E8CC51D0BCA2A0057A1C1;
|
||||||
remoteInfo = "CSV-OSX";
|
remoteInfo = "CSV-macOS";
|
||||||
};
|
};
|
||||||
0E7E8CF31D0BCD0B0057A1C1 /* PBXContainerItemProxy */ = {
|
0E7E8CF31D0BCD0B0057A1C1 /* PBXContainerItemProxy */ = {
|
||||||
isa = PBXContainerItemProxy;
|
isa = PBXContainerItemProxy;
|
||||||
|
@ -59,21 +100,33 @@
|
||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
0E54021A1ED9DDF40019C3ED /* CSVWriterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSVWriterTests.swift; sourceTree = "<group>"; };
|
||||||
0E7E8C811D0BC7BB0057A1C1 /* CSV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CSV.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
0E7E8C811D0BC7BB0057A1C1 /* CSV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CSV.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
0E7E8C8B1D0BC7BB0057A1C1 /* CSVTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "CSVTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
0E7E8C8B1D0BC7BB0057A1C1 /* CSVTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "CSVTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
0E7E8C9C1D0BC7F10057A1C1 /* ByteOrder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ByteOrder.swift; sourceTree = "<group>"; };
|
|
||||||
0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSV.swift; sourceTree = "<group>"; };
|
|
||||||
0E7E8C9E1D0BC7F10057A1C1 /* CSVError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSVError.swift; sourceTree = "<group>"; };
|
|
||||||
0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSVVersion.h; sourceTree = "<group>"; };
|
0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSVVersion.h; sourceTree = "<group>"; };
|
||||||
0E7E8CA61D0BC8050057A1C1 /* CSVReaderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSVReaderTests.swift; sourceTree = "<group>"; };
|
|
||||||
0E7E8CA71D0BC8050057A1C1 /* CSVTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSVTests.swift; sourceTree = "<group>"; };
|
|
||||||
0E7E8CAC1D0BC8610057A1C1 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
0E7E8CAC1D0BC8610057A1C1 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
0E7E8CAE1D0BC8690057A1C1 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
0E7E8CAE1D0BC8690057A1C1 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
0E7E8CB51D0BC98B0057A1C1 /* CSV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CSV.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
0E7E8CB51D0BC98B0057A1C1 /* CSV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CSV.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
0E7E8CC61D0BCA2A0057A1C1 /* CSV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CSV.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
0E7E8CC61D0BCA2A0057A1C1 /* CSV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CSV.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
0E7E8CCF1D0BCA2A0057A1C1 /* CSVTests-OSX.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "CSVTests-OSX.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
0E7E8CCF1D0BCA2A0057A1C1 /* CSVTests-macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "CSVTests-macOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
0E7E8CE81D0BCD0B0057A1C1 /* CSV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CSV.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
0E7E8CE81D0BCD0B0057A1C1 /* CSV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CSV.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
0E7E8CF11D0BCD0B0057A1C1 /* CSVTests-tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "CSVTests-tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
0E7E8CF11D0BCD0B0057A1C1 /* CSVTests-tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "CSVTests-tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
0E7F657A1EF6437E00E1E1A0 /* Version1Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Version1Tests.swift; sourceTree = "<group>"; };
|
||||||
|
0E87607A219D992900C6C7FA /* BinaryReaderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BinaryReaderTests.swift; sourceTree = "<group>"; };
|
||||||
|
0E87607E219FF43B00C6C7FA /* CSVRowDecoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CSVRowDecoder.swift; sourceTree = "<group>"; };
|
||||||
|
0E959C5B208B611F005F8D01 /* CSVError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSVError.swift; sourceTree = "<group>"; };
|
||||||
|
0E959C5C208B611F005F8D01 /* CSVWriter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSVWriter.swift; sourceTree = "<group>"; };
|
||||||
|
0E959C5D208B611F005F8D01 /* Legacy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Legacy.swift; sourceTree = "<group>"; };
|
||||||
|
0E959C5E208B611F005F8D01 /* BinaryReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BinaryReader.swift; sourceTree = "<group>"; };
|
||||||
|
0E959C5F208B611F005F8D01 /* UnicodeIterator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnicodeIterator.swift; sourceTree = "<group>"; };
|
||||||
|
0E959C60208B611F005F8D01 /* Endian.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Endian.swift; sourceTree = "<group>"; };
|
||||||
|
0E959C61208B611F005F8D01 /* CSVReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSVReader.swift; sourceTree = "<group>"; };
|
||||||
|
0EDF8ECD1DDB73370068056A /* CSVReaderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSVReaderTests.swift; sourceTree = "<group>"; };
|
||||||
|
0EDF8ECE1DDB73370068056A /* LineBreakTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineBreakTests.swift; sourceTree = "<group>"; };
|
||||||
|
0EDF8ECF1DDB73370068056A /* ReadmeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadmeTests.swift; sourceTree = "<group>"; };
|
||||||
|
0EDF8ED01DDB73370068056A /* TrimFieldsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrimFieldsTests.swift; sourceTree = "<group>"; };
|
||||||
|
0EDF8ED11DDB73370068056A /* UnicodeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnicodeTests.swift; sourceTree = "<group>"; };
|
||||||
|
3C89219D21484153004AA78A /* CSVRowDecoderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CSVRowDecoderTests.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
@ -140,6 +193,7 @@
|
||||||
0E7E8C821D0BC7BB0057A1C1 /* Products */,
|
0E7E8C821D0BC7BB0057A1C1 /* Products */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
usesTabs = 0;
|
||||||
};
|
};
|
||||||
0E7E8C821D0BC7BB0057A1C1 /* Products */ = {
|
0E7E8C821D0BC7BB0057A1C1 /* Products */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
|
@ -148,7 +202,7 @@
|
||||||
0E7E8C8B1D0BC7BB0057A1C1 /* CSVTests-iOS.xctest */,
|
0E7E8C8B1D0BC7BB0057A1C1 /* CSVTests-iOS.xctest */,
|
||||||
0E7E8CB51D0BC98B0057A1C1 /* CSV.framework */,
|
0E7E8CB51D0BC98B0057A1C1 /* CSV.framework */,
|
||||||
0E7E8CC61D0BCA2A0057A1C1 /* CSV.framework */,
|
0E7E8CC61D0BCA2A0057A1C1 /* CSV.framework */,
|
||||||
0E7E8CCF1D0BCA2A0057A1C1 /* CSVTests-OSX.xctest */,
|
0E7E8CCF1D0BCA2A0057A1C1 /* CSVTests-macOS.xctest */,
|
||||||
0E7E8CE81D0BCD0B0057A1C1 /* CSV.framework */,
|
0E7E8CE81D0BCD0B0057A1C1 /* CSV.framework */,
|
||||||
0E7E8CF11D0BCD0B0057A1C1 /* CSVTests-tvOS.xctest */,
|
0E7E8CF11D0BCD0B0057A1C1 /* CSVTests-tvOS.xctest */,
|
||||||
);
|
);
|
||||||
|
@ -158,9 +212,7 @@
|
||||||
0E7E8C9B1D0BC7F10057A1C1 /* Sources */ = {
|
0E7E8C9B1D0BC7F10057A1C1 /* Sources */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
0E7E8C9C1D0BC7F10057A1C1 /* ByteOrder.swift */,
|
0E959C5A208B611F005F8D01 /* CSV */,
|
||||||
0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */,
|
|
||||||
0E7E8C9E1D0BC7F10057A1C1 /* CSVError.swift */,
|
|
||||||
0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */,
|
0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */,
|
||||||
0E7E8CAC1D0BC8610057A1C1 /* Info.plist */,
|
0E7E8CAC1D0BC8610057A1C1 /* Info.plist */,
|
||||||
);
|
);
|
||||||
|
@ -170,21 +222,43 @@
|
||||||
0E7E8CA41D0BC8050057A1C1 /* Tests */ = {
|
0E7E8CA41D0BC8050057A1C1 /* Tests */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
0E7E8CA51D0BC8050057A1C1 /* CSV */,
|
0EA37AC81DD8C0B900F5B274 /* CSVTests */,
|
||||||
0E7E8CAE1D0BC8690057A1C1 /* Info.plist */,
|
0E7E8CAE1D0BC8690057A1C1 /* Info.plist */,
|
||||||
);
|
);
|
||||||
path = Tests;
|
path = Tests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
0E7E8CA51D0BC8050057A1C1 /* CSV */ = {
|
0E959C5A208B611F005F8D01 /* CSV */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
0E7E8CA61D0BC8050057A1C1 /* CSVReaderTests.swift */,
|
0E959C5E208B611F005F8D01 /* BinaryReader.swift */,
|
||||||
0E7E8CA71D0BC8050057A1C1 /* CSVTests.swift */,
|
0E959C5D208B611F005F8D01 /* Legacy.swift */,
|
||||||
|
0E959C5B208B611F005F8D01 /* CSVError.swift */,
|
||||||
|
0E959C61208B611F005F8D01 /* CSVReader.swift */,
|
||||||
|
0E87607E219FF43B00C6C7FA /* CSVRowDecoder.swift */,
|
||||||
|
0E959C5C208B611F005F8D01 /* CSVWriter.swift */,
|
||||||
|
0E959C60208B611F005F8D01 /* Endian.swift */,
|
||||||
|
0E959C5F208B611F005F8D01 /* UnicodeIterator.swift */,
|
||||||
);
|
);
|
||||||
path = CSV;
|
path = CSV;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
0EA37AC81DD8C0B900F5B274 /* CSVTests */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
0E87607A219D992900C6C7FA /* BinaryReaderTests.swift */,
|
||||||
|
0EDF8ECD1DDB73370068056A /* CSVReaderTests.swift */,
|
||||||
|
3C89219D21484153004AA78A /* CSVRowDecoderTests.swift */,
|
||||||
|
0E54021A1ED9DDF40019C3ED /* CSVWriterTests.swift */,
|
||||||
|
0EDF8ECE1DDB73370068056A /* LineBreakTests.swift */,
|
||||||
|
0EDF8ECF1DDB73370068056A /* ReadmeTests.swift */,
|
||||||
|
0EDF8ED01DDB73370068056A /* TrimFieldsTests.swift */,
|
||||||
|
0EDF8ED11DDB73370068056A /* UnicodeTests.swift */,
|
||||||
|
0E7F657A1EF6437E00E1E1A0 /* Version1Tests.swift */,
|
||||||
|
);
|
||||||
|
path = CSVTests;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXHeadersBuildPhase section */
|
/* Begin PBXHeadersBuildPhase section */
|
||||||
|
@ -255,7 +329,7 @@
|
||||||
0E7E8C8E1D0BC7BB0057A1C1 /* PBXTargetDependency */,
|
0E7E8C8E1D0BC7BB0057A1C1 /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
name = "CSVTests-iOS";
|
name = "CSVTests-iOS";
|
||||||
productName = CSVTests;
|
productName = "CSVTests-iOS";
|
||||||
productReference = 0E7E8C8B1D0BC7BB0057A1C1 /* CSVTests-iOS.xctest */;
|
productReference = 0E7E8C8B1D0BC7BB0057A1C1 /* CSVTests-iOS.xctest */;
|
||||||
productType = "com.apple.product-type.bundle.unit-test";
|
productType = "com.apple.product-type.bundle.unit-test";
|
||||||
};
|
};
|
||||||
|
@ -277,10 +351,11 @@
|
||||||
productReference = 0E7E8CB51D0BC98B0057A1C1 /* CSV.framework */;
|
productReference = 0E7E8CB51D0BC98B0057A1C1 /* CSV.framework */;
|
||||||
productType = "com.apple.product-type.framework";
|
productType = "com.apple.product-type.framework";
|
||||||
};
|
};
|
||||||
0E7E8CC51D0BCA2A0057A1C1 /* CSV-OSX */ = {
|
0E7E8CC51D0BCA2A0057A1C1 /* CSV-macOS */ = {
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 0E7E8CD71D0BCA2A0057A1C1 /* Build configuration list for PBXNativeTarget "CSV-OSX" */;
|
buildConfigurationList = 0E7E8CD71D0BCA2A0057A1C1 /* Build configuration list for PBXNativeTarget "CSV-macOS" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
|
0E47EEBF1DBBC05700EBF783 /* Run Script () */,
|
||||||
0E7E8CC11D0BCA2A0057A1C1 /* Sources */,
|
0E7E8CC11D0BCA2A0057A1C1 /* Sources */,
|
||||||
0E7E8CC21D0BCA2A0057A1C1 /* Frameworks */,
|
0E7E8CC21D0BCA2A0057A1C1 /* Frameworks */,
|
||||||
0E7E8CC31D0BCA2A0057A1C1 /* Headers */,
|
0E7E8CC31D0BCA2A0057A1C1 /* Headers */,
|
||||||
|
@ -290,14 +365,14 @@
|
||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
);
|
);
|
||||||
name = "CSV-OSX";
|
name = "CSV-macOS";
|
||||||
productName = "CSV-OSX";
|
productName = "CSV-macOS";
|
||||||
productReference = 0E7E8CC61D0BCA2A0057A1C1 /* CSV.framework */;
|
productReference = 0E7E8CC61D0BCA2A0057A1C1 /* CSV.framework */;
|
||||||
productType = "com.apple.product-type.framework";
|
productType = "com.apple.product-type.framework";
|
||||||
};
|
};
|
||||||
0E7E8CCE1D0BCA2A0057A1C1 /* CSVTests-OSX */ = {
|
0E7E8CCE1D0BCA2A0057A1C1 /* CSVTests-macOS */ = {
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 0E7E8CDA1D0BCA2A0057A1C1 /* Build configuration list for PBXNativeTarget "CSVTests-OSX" */;
|
buildConfigurationList = 0E7E8CDA1D0BCA2A0057A1C1 /* Build configuration list for PBXNativeTarget "CSVTests-macOS" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
0E7E8CCB1D0BCA2A0057A1C1 /* Sources */,
|
0E7E8CCB1D0BCA2A0057A1C1 /* Sources */,
|
||||||
0E7E8CCC1D0BCA2A0057A1C1 /* Frameworks */,
|
0E7E8CCC1D0BCA2A0057A1C1 /* Frameworks */,
|
||||||
|
@ -308,9 +383,9 @@
|
||||||
dependencies = (
|
dependencies = (
|
||||||
0E7E8CD21D0BCA2A0057A1C1 /* PBXTargetDependency */,
|
0E7E8CD21D0BCA2A0057A1C1 /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
name = "CSVTests-OSX";
|
name = "CSVTests-macOS";
|
||||||
productName = "CSV-OSXTests";
|
productName = "CSVTests-macOS";
|
||||||
productReference = 0E7E8CCF1D0BCA2A0057A1C1 /* CSVTests-OSX.xctest */;
|
productReference = 0E7E8CCF1D0BCA2A0057A1C1 /* CSVTests-macOS.xctest */;
|
||||||
productType = "com.apple.product-type.bundle.unit-test";
|
productType = "com.apple.product-type.bundle.unit-test";
|
||||||
};
|
};
|
||||||
0E7E8CE71D0BCD0B0057A1C1 /* CSV-tvOS */ = {
|
0E7E8CE71D0BCD0B0057A1C1 /* CSV-tvOS */ = {
|
||||||
|
@ -345,7 +420,7 @@
|
||||||
0E7E8CF41D0BCD0B0057A1C1 /* PBXTargetDependency */,
|
0E7E8CF41D0BCD0B0057A1C1 /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
name = "CSVTests-tvOS";
|
name = "CSVTests-tvOS";
|
||||||
productName = "CSV-tvOSTests";
|
productName = "CSVTests-tvOS";
|
||||||
productReference = 0E7E8CF11D0BCD0B0057A1C1 /* CSVTests-tvOS.xctest */;
|
productReference = 0E7E8CF11D0BCD0B0057A1C1 /* CSVTests-tvOS.xctest */;
|
||||||
productType = "com.apple.product-type.bundle.unit-test";
|
productType = "com.apple.product-type.bundle.unit-test";
|
||||||
};
|
};
|
||||||
|
@ -355,50 +430,58 @@
|
||||||
0E7E8C781D0BC7BB0057A1C1 /* Project object */ = {
|
0E7E8C781D0BC7BB0057A1C1 /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastSwiftUpdateCheck = 0730;
|
LastSwiftUpdateCheck = 1010;
|
||||||
LastUpgradeCheck = 0730;
|
LastUpgradeCheck = 1020;
|
||||||
ORGANIZATIONNAME = yaslab;
|
ORGANIZATIONNAME = yaslab;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
0E7E8C801D0BC7BB0057A1C1 = {
|
0E7E8C801D0BC7BB0057A1C1 = {
|
||||||
CreatedOnToolsVersion = 7.3.1;
|
CreatedOnToolsVersion = 7.3.1;
|
||||||
|
LastSwiftMigration = 1020;
|
||||||
};
|
};
|
||||||
0E7E8C8A1D0BC7BB0057A1C1 = {
|
0E7E8C8A1D0BC7BB0057A1C1 = {
|
||||||
CreatedOnToolsVersion = 7.3.1;
|
CreatedOnToolsVersion = 7.3.1;
|
||||||
|
LastSwiftMigration = 1020;
|
||||||
};
|
};
|
||||||
0E7E8CB41D0BC98B0057A1C1 = {
|
0E7E8CB41D0BC98B0057A1C1 = {
|
||||||
CreatedOnToolsVersion = 7.3.1;
|
CreatedOnToolsVersion = 7.3.1;
|
||||||
|
LastSwiftMigration = 0800;
|
||||||
};
|
};
|
||||||
0E7E8CC51D0BCA2A0057A1C1 = {
|
0E7E8CC51D0BCA2A0057A1C1 = {
|
||||||
CreatedOnToolsVersion = 7.3.1;
|
CreatedOnToolsVersion = 7.3.1;
|
||||||
|
LastSwiftMigration = 1020;
|
||||||
};
|
};
|
||||||
0E7E8CCE1D0BCA2A0057A1C1 = {
|
0E7E8CCE1D0BCA2A0057A1C1 = {
|
||||||
CreatedOnToolsVersion = 7.3.1;
|
CreatedOnToolsVersion = 7.3.1;
|
||||||
|
LastSwiftMigration = 1020;
|
||||||
};
|
};
|
||||||
0E7E8CE71D0BCD0B0057A1C1 = {
|
0E7E8CE71D0BCD0B0057A1C1 = {
|
||||||
CreatedOnToolsVersion = 7.3.1;
|
CreatedOnToolsVersion = 7.3.1;
|
||||||
|
LastSwiftMigration = 0800;
|
||||||
};
|
};
|
||||||
0E7E8CF01D0BCD0B0057A1C1 = {
|
0E7E8CF01D0BCD0B0057A1C1 = {
|
||||||
CreatedOnToolsVersion = 7.3.1;
|
CreatedOnToolsVersion = 7.3.1;
|
||||||
|
LastSwiftMigration = 0930;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = 0E7E8C7B1D0BC7BB0057A1C1 /* Build configuration list for PBXProject "CSV" */;
|
buildConfigurationList = 0E7E8C7B1D0BC7BB0057A1C1 /* Build configuration list for PBXProject "CSV" */;
|
||||||
compatibilityVersion = "Xcode 3.2";
|
compatibilityVersion = "Xcode 3.2";
|
||||||
developmentRegion = English;
|
developmentRegion = en;
|
||||||
hasScannedForEncodings = 0;
|
hasScannedForEncodings = 0;
|
||||||
knownRegions = (
|
knownRegions = (
|
||||||
en,
|
en,
|
||||||
|
Base,
|
||||||
);
|
);
|
||||||
mainGroup = 0E7E8C771D0BC7BB0057A1C1;
|
mainGroup = 0E7E8C771D0BC7BB0057A1C1;
|
||||||
productRefGroup = 0E7E8C821D0BC7BB0057A1C1 /* Products */;
|
productRefGroup = 0E7E8C821D0BC7BB0057A1C1 /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
targets = (
|
targets = (
|
||||||
0E7E8CC51D0BCA2A0057A1C1 /* CSV-OSX */,
|
0E7E8CC51D0BCA2A0057A1C1 /* CSV-macOS */,
|
||||||
0E7E8C801D0BC7BB0057A1C1 /* CSV-iOS */,
|
0E7E8C801D0BC7BB0057A1C1 /* CSV-iOS */,
|
||||||
0E7E8CE71D0BCD0B0057A1C1 /* CSV-tvOS */,
|
0E7E8CE71D0BCD0B0057A1C1 /* CSV-tvOS */,
|
||||||
0E7E8CB41D0BC98B0057A1C1 /* CSV-watchOS */,
|
0E7E8CB41D0BC98B0057A1C1 /* CSV-watchOS */,
|
||||||
0E7E8CCE1D0BCA2A0057A1C1 /* CSVTests-OSX */,
|
0E7E8CCE1D0BCA2A0057A1C1 /* CSVTests-macOS */,
|
||||||
0E7E8C8A1D0BC7BB0057A1C1 /* CSVTests-iOS */,
|
0E7E8C8A1D0BC7BB0057A1C1 /* CSVTests-iOS */,
|
||||||
0E7E8CF01D0BCD0B0057A1C1 /* CSVTests-tvOS */,
|
0E7E8CF01D0BCD0B0057A1C1 /* CSVTests-tvOS */,
|
||||||
);
|
);
|
||||||
|
@ -457,14 +540,36 @@
|
||||||
};
|
};
|
||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
0E47EEBF1DBBC05700EBF783 /* Run Script () */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "Run Script ()";
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "if which swiftlint >/dev/null; then\n swiftlint autocorrect\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi";
|
||||||
|
};
|
||||||
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
0E7E8C7C1D0BC7BB0057A1C1 /* Sources */ = {
|
0E7E8C7C1D0BC7BB0057A1C1 /* Sources */ = {
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
0E7E8CA11D0BC7F10057A1C1 /* CSV.swift in Sources */,
|
0E959C63208B611F005F8D01 /* CSVError.swift in Sources */,
|
||||||
0E7E8CA21D0BC7F10057A1C1 /* CSVError.swift in Sources */,
|
0E876080219FF43B00C6C7FA /* CSVRowDecoder.swift in Sources */,
|
||||||
0E7E8CA01D0BC7F10057A1C1 /* ByteOrder.swift in Sources */,
|
0E959C6F208B611F005F8D01 /* BinaryReader.swift in Sources */,
|
||||||
|
0E959C77208B611F005F8D01 /* Endian.swift in Sources */,
|
||||||
|
0E959C67208B611F005F8D01 /* CSVWriter.swift in Sources */,
|
||||||
|
0E959C73208B611F005F8D01 /* UnicodeIterator.swift in Sources */,
|
||||||
|
0E959C6B208B611F005F8D01 /* Legacy.swift in Sources */,
|
||||||
|
0E959C7B208B611F005F8D01 /* CSVReader.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -472,8 +577,15 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
0E7E8CAA1D0BC8050057A1C1 /* CSVTests.swift in Sources */,
|
0EDF8EDE1DDB73520068056A /* ReadmeTests.swift in Sources */,
|
||||||
0E7E8CA91D0BC8050057A1C1 /* CSVReaderTests.swift in Sources */,
|
3C89219F21484154004AA78A /* CSVRowDecoderTests.swift in Sources */,
|
||||||
|
0EDF8EDC1DDB73520068056A /* CSVReaderTests.swift in Sources */,
|
||||||
|
0EDF8EDF1DDB73520068056A /* TrimFieldsTests.swift in Sources */,
|
||||||
|
0E7F657C1EF6437E00E1E1A0 /* Version1Tests.swift in Sources */,
|
||||||
|
0EDF8EE01DDB73520068056A /* UnicodeTests.swift in Sources */,
|
||||||
|
0E87607C219D992900C6C7FA /* BinaryReaderTests.swift in Sources */,
|
||||||
|
0EDF8EDD1DDB73520068056A /* LineBreakTests.swift in Sources */,
|
||||||
|
0E54021C1ED9DDF40019C3ED /* CSVWriterTests.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -481,9 +593,14 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
0E7E8CBE1D0BC9D70057A1C1 /* CSV.swift in Sources */,
|
0E959C65208B611F005F8D01 /* CSVError.swift in Sources */,
|
||||||
0E7E8CBF1D0BC9D70057A1C1 /* CSVError.swift in Sources */,
|
0E876082219FF43B00C6C7FA /* CSVRowDecoder.swift in Sources */,
|
||||||
0E7E8CBD1D0BC9D70057A1C1 /* ByteOrder.swift in Sources */,
|
0E959C71208B611F005F8D01 /* BinaryReader.swift in Sources */,
|
||||||
|
0E959C79208B611F005F8D01 /* Endian.swift in Sources */,
|
||||||
|
0E959C69208B611F005F8D01 /* CSVWriter.swift in Sources */,
|
||||||
|
0E959C75208B611F005F8D01 /* UnicodeIterator.swift in Sources */,
|
||||||
|
0E959C6D208B611F005F8D01 /* Legacy.swift in Sources */,
|
||||||
|
0E959C7D208B611F005F8D01 /* CSVReader.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -491,9 +608,14 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
0E7E8CE01D0BCA8E0057A1C1 /* CSV.swift in Sources */,
|
0E959C62208B611F005F8D01 /* CSVError.swift in Sources */,
|
||||||
0E7E8CE11D0BCA8E0057A1C1 /* CSVError.swift in Sources */,
|
0E87607F219FF43B00C6C7FA /* CSVRowDecoder.swift in Sources */,
|
||||||
0E7E8CDF1D0BCA8E0057A1C1 /* ByteOrder.swift in Sources */,
|
0E959C6E208B611F005F8D01 /* BinaryReader.swift in Sources */,
|
||||||
|
0E959C76208B611F005F8D01 /* Endian.swift in Sources */,
|
||||||
|
0E959C66208B611F005F8D01 /* CSVWriter.swift in Sources */,
|
||||||
|
0E959C72208B611F005F8D01 /* UnicodeIterator.swift in Sources */,
|
||||||
|
0E959C6A208B611F005F8D01 /* Legacy.swift in Sources */,
|
||||||
|
0E959C7A208B611F005F8D01 /* CSVReader.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -501,8 +623,15 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
0E7E8CDE1D0BCA840057A1C1 /* CSVTests.swift in Sources */,
|
0EDF8ED91DDB73520068056A /* ReadmeTests.swift in Sources */,
|
||||||
0E7E8CDD1D0BCA840057A1C1 /* CSVReaderTests.swift in Sources */,
|
3C89219E21484154004AA78A /* CSVRowDecoderTests.swift in Sources */,
|
||||||
|
0EDF8ED71DDB73520068056A /* CSVReaderTests.swift in Sources */,
|
||||||
|
0EDF8EDA1DDB73520068056A /* TrimFieldsTests.swift in Sources */,
|
||||||
|
0E7F657B1EF6437E00E1E1A0 /* Version1Tests.swift in Sources */,
|
||||||
|
0EDF8EDB1DDB73520068056A /* UnicodeTests.swift in Sources */,
|
||||||
|
0E87607B219D992900C6C7FA /* BinaryReaderTests.swift in Sources */,
|
||||||
|
0EDF8ED81DDB73520068056A /* LineBreakTests.swift in Sources */,
|
||||||
|
0E54021B1ED9DDF40019C3ED /* CSVWriterTests.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -510,9 +639,14 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
0E7E8D001D0BCDCF0057A1C1 /* CSV.swift in Sources */,
|
0E959C64208B611F005F8D01 /* CSVError.swift in Sources */,
|
||||||
0E7E8D011D0BCDCF0057A1C1 /* CSVError.swift in Sources */,
|
0E876081219FF43B00C6C7FA /* CSVRowDecoder.swift in Sources */,
|
||||||
0E7E8CFF1D0BCDCF0057A1C1 /* ByteOrder.swift in Sources */,
|
0E959C70208B611F005F8D01 /* BinaryReader.swift in Sources */,
|
||||||
|
0E959C78208B611F005F8D01 /* Endian.swift in Sources */,
|
||||||
|
0E959C68208B611F005F8D01 /* CSVWriter.swift in Sources */,
|
||||||
|
0E959C74208B611F005F8D01 /* UnicodeIterator.swift in Sources */,
|
||||||
|
0E959C6C208B611F005F8D01 /* Legacy.swift in Sources */,
|
||||||
|
0E959C7C208B611F005F8D01 /* CSVReader.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -520,8 +654,15 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
0E7E8D041D0BCDDD0057A1C1 /* CSVTests.swift in Sources */,
|
0EDF8EE31DDB73530068056A /* ReadmeTests.swift in Sources */,
|
||||||
0E7E8D031D0BCDDD0057A1C1 /* CSVReaderTests.swift in Sources */,
|
3C8921A021484154004AA78A /* CSVRowDecoderTests.swift in Sources */,
|
||||||
|
0EDF8EE11DDB73530068056A /* CSVReaderTests.swift in Sources */,
|
||||||
|
0EDF8EE41DDB73530068056A /* TrimFieldsTests.swift in Sources */,
|
||||||
|
0E7F657D1EF6437E00E1E1A0 /* Version1Tests.swift in Sources */,
|
||||||
|
0EDF8EE51DDB73530068056A /* UnicodeTests.swift in Sources */,
|
||||||
|
0E87607D219D992900C6C7FA /* BinaryReaderTests.swift in Sources */,
|
||||||
|
0EDF8EE21DDB73530068056A /* LineBreakTests.swift in Sources */,
|
||||||
|
0E54021D1ED9DDF40019C3ED /* CSVWriterTests.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -535,7 +676,7 @@
|
||||||
};
|
};
|
||||||
0E7E8CD21D0BCA2A0057A1C1 /* PBXTargetDependency */ = {
|
0E7E8CD21D0BCA2A0057A1C1 /* PBXTargetDependency */ = {
|
||||||
isa = PBXTargetDependency;
|
isa = PBXTargetDependency;
|
||||||
target = 0E7E8CC51D0BCA2A0057A1C1 /* CSV-OSX */;
|
target = 0E7E8CC51D0BCA2A0057A1C1 /* CSV-macOS */;
|
||||||
targetProxy = 0E7E8CD11D0BCA2A0057A1C1 /* PBXContainerItemProxy */;
|
targetProxy = 0E7E8CD11D0BCA2A0057A1C1 /* PBXContainerItemProxy */;
|
||||||
};
|
};
|
||||||
0E7E8CF41D0BCD0B0057A1C1 /* PBXTargetDependency */ = {
|
0E7E8CF41D0BCD0B0057A1C1 /* PBXTargetDependency */ = {
|
||||||
|
@ -550,18 +691,29 @@
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
CLANG_WARN_EMPTY_BODY = YES;
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
CLANG_WARN_INT_CONVERSION = YES;
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
@ -584,11 +736,13 @@
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 9.3;
|
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 10.9;
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
VERSIONING_SYSTEM = "apple-generic";
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
VERSION_INFO_PREFIX = "";
|
VERSION_INFO_PREFIX = "";
|
||||||
|
@ -599,18 +753,29 @@
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
CLANG_WARN_EMPTY_BODY = YES;
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
CLANG_WARN_INT_CONVERSION = YES;
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
@ -627,9 +792,11 @@
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 9.3;
|
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 10.9;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
VALIDATE_PRODUCT = YES;
|
VALIDATE_PRODUCT = YES;
|
||||||
VERSIONING_SYSTEM = "apple-generic";
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
@ -640,6 +807,7 @@
|
||||||
0E7E8C961D0BC7BB0057A1C1 /* Debug */ = {
|
0E7E8C961D0BC7BB0057A1C1 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||||
DEFINES_MODULE = YES;
|
DEFINES_MODULE = YES;
|
||||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||||
DYLIB_CURRENT_VERSION = 1;
|
DYLIB_CURRENT_VERSION = 1;
|
||||||
|
@ -648,6 +816,7 @@
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
|
MARKETING_VERSION = 2.4.3;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV;
|
PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV;
|
||||||
PRODUCT_NAME = CSV;
|
PRODUCT_NAME = CSV;
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
|
@ -657,6 +826,7 @@
|
||||||
0E7E8C971D0BC7BB0057A1C1 /* Release */ = {
|
0E7E8C971D0BC7BB0057A1C1 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||||
DEFINES_MODULE = YES;
|
DEFINES_MODULE = YES;
|
||||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||||
DYLIB_CURRENT_VERSION = 1;
|
DYLIB_CURRENT_VERSION = 1;
|
||||||
|
@ -665,15 +835,18 @@
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
|
MARKETING_VERSION = 2.4.3;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV;
|
PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV;
|
||||||
PRODUCT_NAME = CSV;
|
PRODUCT_NAME = CSV;
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
0E7E8C991D0BC7BB0057A1C1 /* Debug */ = {
|
0E7E8C991D0BC7BB0057A1C1 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
INFOPLIST_FILE = Tests/Info.plist;
|
INFOPLIST_FILE = Tests/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-iOS";
|
PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-iOS";
|
||||||
|
@ -684,10 +857,12 @@
|
||||||
0E7E8C9A1D0BC7BB0057A1C1 /* Release */ = {
|
0E7E8C9A1D0BC7BB0057A1C1 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
INFOPLIST_FILE = Tests/Info.plist;
|
INFOPLIST_FILE = Tests/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-iOS";
|
PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-iOS";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
@ -695,6 +870,7 @@
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
|
||||||
DEFINES_MODULE = YES;
|
DEFINES_MODULE = YES;
|
||||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||||
DYLIB_CURRENT_VERSION = 1;
|
DYLIB_CURRENT_VERSION = 1;
|
||||||
|
@ -702,6 +878,7 @@
|
||||||
INFOPLIST_FILE = Sources/Info.plist;
|
INFOPLIST_FILE = Sources/Info.plist;
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
|
MARKETING_VERSION = 2.4.3;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV;
|
PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV;
|
||||||
PRODUCT_NAME = CSV;
|
PRODUCT_NAME = CSV;
|
||||||
SDKROOT = watchos;
|
SDKROOT = watchos;
|
||||||
|
@ -715,6 +892,7 @@
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
|
||||||
DEFINES_MODULE = YES;
|
DEFINES_MODULE = YES;
|
||||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||||
DYLIB_CURRENT_VERSION = 1;
|
DYLIB_CURRENT_VERSION = 1;
|
||||||
|
@ -722,10 +900,12 @@
|
||||||
INFOPLIST_FILE = Sources/Info.plist;
|
INFOPLIST_FILE = Sources/Info.plist;
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
|
MARKETING_VERSION = 2.4.3;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV;
|
PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV;
|
||||||
PRODUCT_NAME = CSV;
|
PRODUCT_NAME = CSV;
|
||||||
SDKROOT = watchos;
|
SDKROOT = watchos;
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||||
TARGETED_DEVICE_FAMILY = 4;
|
TARGETED_DEVICE_FAMILY = 4;
|
||||||
WATCHOS_DEPLOYMENT_TARGET = 2.0;
|
WATCHOS_DEPLOYMENT_TARGET = 2.0;
|
||||||
};
|
};
|
||||||
|
@ -745,6 +925,7 @@
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.9;
|
MACOSX_DEPLOYMENT_TARGET = 10.9;
|
||||||
|
MARKETING_VERSION = 2.4.3;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV;
|
PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV;
|
||||||
PRODUCT_NAME = CSV;
|
PRODUCT_NAME = CSV;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
|
@ -766,22 +947,25 @@
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.9;
|
MACOSX_DEPLOYMENT_TARGET = 10.9;
|
||||||
|
MARKETING_VERSION = 2.4.3;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV;
|
PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV;
|
||||||
PRODUCT_NAME = CSV;
|
PRODUCT_NAME = CSV;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
0E7E8CDB1D0BCA2A0057A1C1 /* Debug */ = {
|
0E7E8CDB1D0BCA2A0057A1C1 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
CODE_SIGN_IDENTITY = "-";
|
CODE_SIGN_IDENTITY = "-";
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
INFOPLIST_FILE = Tests/Info.plist;
|
INFOPLIST_FILE = Tests/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-OSX";
|
PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-macOS";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
};
|
};
|
||||||
|
@ -790,20 +974,23 @@
|
||||||
0E7E8CDC1D0BCA2A0057A1C1 /* Release */ = {
|
0E7E8CDC1D0BCA2A0057A1C1 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
CODE_SIGN_IDENTITY = "-";
|
CODE_SIGN_IDENTITY = "-";
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
INFOPLIST_FILE = Tests/Info.plist;
|
INFOPLIST_FILE = Tests/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-OSX";
|
PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-macOS";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
0E7E8CFA1D0BCD0B0057A1C1 /* Debug */ = {
|
0E7E8CFA1D0BCD0B0057A1C1 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
|
||||||
DEFINES_MODULE = YES;
|
DEFINES_MODULE = YES;
|
||||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||||
DYLIB_CURRENT_VERSION = 1;
|
DYLIB_CURRENT_VERSION = 1;
|
||||||
|
@ -811,6 +998,7 @@
|
||||||
INFOPLIST_FILE = Sources/Info.plist;
|
INFOPLIST_FILE = Sources/Info.plist;
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
|
MARKETING_VERSION = 2.4.3;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV;
|
PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV;
|
||||||
PRODUCT_NAME = CSV;
|
PRODUCT_NAME = CSV;
|
||||||
SDKROOT = appletvos;
|
SDKROOT = appletvos;
|
||||||
|
@ -823,6 +1011,7 @@
|
||||||
0E7E8CFB1D0BCD0B0057A1C1 /* Release */ = {
|
0E7E8CFB1D0BCD0B0057A1C1 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
|
||||||
DEFINES_MODULE = YES;
|
DEFINES_MODULE = YES;
|
||||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||||
DYLIB_CURRENT_VERSION = 1;
|
DYLIB_CURRENT_VERSION = 1;
|
||||||
|
@ -830,10 +1019,12 @@
|
||||||
INFOPLIST_FILE = Sources/Info.plist;
|
INFOPLIST_FILE = Sources/Info.plist;
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
|
MARKETING_VERSION = 2.4.3;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV;
|
PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV;
|
||||||
PRODUCT_NAME = CSV;
|
PRODUCT_NAME = CSV;
|
||||||
SDKROOT = appletvos;
|
SDKROOT = appletvos;
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||||
TARGETED_DEVICE_FAMILY = 3;
|
TARGETED_DEVICE_FAMILY = 3;
|
||||||
TVOS_DEPLOYMENT_TARGET = 9.0;
|
TVOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
};
|
};
|
||||||
|
@ -842,6 +1033,7 @@
|
||||||
0E7E8CFD1D0BCD0B0057A1C1 /* Debug */ = {
|
0E7E8CFD1D0BCD0B0057A1C1 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
INFOPLIST_FILE = Tests/Info.plist;
|
INFOPLIST_FILE = Tests/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-tvOS";
|
PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-tvOS";
|
||||||
|
@ -854,11 +1046,13 @@
|
||||||
0E7E8CFE1D0BCD0B0057A1C1 /* Release */ = {
|
0E7E8CFE1D0BCD0B0057A1C1 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
INFOPLIST_FILE = Tests/Info.plist;
|
INFOPLIST_FILE = Tests/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-tvOS";
|
PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-tvOS";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = appletvos;
|
SDKROOT = appletvos;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||||
TVOS_DEPLOYMENT_TARGET = 9.2;
|
TVOS_DEPLOYMENT_TARGET = 9.2;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
|
@ -902,7 +1096,7 @@
|
||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
0E7E8CD71D0BCA2A0057A1C1 /* Build configuration list for PBXNativeTarget "CSV-OSX" */ = {
|
0E7E8CD71D0BCA2A0057A1C1 /* Build configuration list for PBXNativeTarget "CSV-macOS" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
0E7E8CD81D0BCA2A0057A1C1 /* Debug */,
|
0E7E8CD81D0BCA2A0057A1C1 /* Debug */,
|
||||||
|
@ -911,7 +1105,7 @@
|
||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
0E7E8CDA1D0BCA2A0057A1C1 /* Build configuration list for PBXNativeTarget "CSVTests-OSX" */ = {
|
0E7E8CDA1D0BCA2A0057A1C1 /* Build configuration list for PBXNativeTarget "CSVTests-macOS" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
0E7E8CDB1D0BCA2A0057A1C1 /* Debug */,
|
0E7E8CDB1D0BCA2A0057A1C1 /* Debug */,
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0730"
|
LastUpgradeVersion = "1020"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
@ -26,6 +26,7 @@
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
codeCoverageEnabled = "YES"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
<Testables>
|
<Testables>
|
||||||
<TestableReference
|
<TestableReference
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0730"
|
LastUpgradeVersion = "1020"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "0E7E8CC51D0BCA2A0057A1C1"
|
BlueprintIdentifier = "0E7E8CC51D0BCA2A0057A1C1"
|
||||||
BuildableName = "CSV.framework"
|
BuildableName = "CSV.framework"
|
||||||
BlueprintName = "CSV-OSX"
|
BlueprintName = "CSV-macOS"
|
||||||
ReferencedContainer = "container:CSV.xcodeproj">
|
ReferencedContainer = "container:CSV.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildActionEntry>
|
</BuildActionEntry>
|
||||||
|
@ -26,6 +26,7 @@
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
codeCoverageEnabled = "YES"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
<Testables>
|
<Testables>
|
||||||
<TestableReference
|
<TestableReference
|
||||||
|
@ -33,8 +34,8 @@
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "0E7E8CCE1D0BCA2A0057A1C1"
|
BlueprintIdentifier = "0E7E8CCE1D0BCA2A0057A1C1"
|
||||||
BuildableName = "CSVTests-OSX.xctest"
|
BuildableName = "CSVTests-macOS.xctest"
|
||||||
BlueprintName = "CSVTests-OSX"
|
BlueprintName = "CSVTests-macOS"
|
||||||
ReferencedContainer = "container:CSV.xcodeproj">
|
ReferencedContainer = "container:CSV.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</TestableReference>
|
</TestableReference>
|
||||||
|
@ -44,7 +45,7 @@
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "0E7E8CC51D0BCA2A0057A1C1"
|
BlueprintIdentifier = "0E7E8CC51D0BCA2A0057A1C1"
|
||||||
BuildableName = "CSV.framework"
|
BuildableName = "CSV.framework"
|
||||||
BlueprintName = "CSV-OSX"
|
BlueprintName = "CSV-macOS"
|
||||||
ReferencedContainer = "container:CSV.xcodeproj">
|
ReferencedContainer = "container:CSV.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
|
@ -66,7 +67,7 @@
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "0E7E8CC51D0BCA2A0057A1C1"
|
BlueprintIdentifier = "0E7E8CC51D0BCA2A0057A1C1"
|
||||||
BuildableName = "CSV.framework"
|
BuildableName = "CSV.framework"
|
||||||
BlueprintName = "CSV-OSX"
|
BlueprintName = "CSV-macOS"
|
||||||
ReferencedContainer = "container:CSV.xcodeproj">
|
ReferencedContainer = "container:CSV.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
|
@ -84,7 +85,7 @@
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "0E7E8CC51D0BCA2A0057A1C1"
|
BlueprintIdentifier = "0E7E8CC51D0BCA2A0057A1C1"
|
||||||
BuildableName = "CSV.framework"
|
BuildableName = "CSV.framework"
|
||||||
BlueprintName = "CSV-OSX"
|
BlueprintName = "CSV-macOS"
|
||||||
ReferencedContainer = "container:CSV.xcodeproj">
|
ReferencedContainer = "container:CSV.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0730"
|
LastUpgradeVersion = "1020"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0730"
|
LastUpgradeVersion = "1020"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
|
|
@ -1,7 +1,15 @@
|
||||||
|
// swift-tools-version:5.0
|
||||||
|
|
||||||
import PackageDescription
|
import PackageDescription
|
||||||
|
|
||||||
let package = Package(
|
let package = Package(
|
||||||
name: "CSV",
|
name: "CSV.swift",
|
||||||
targets: [],
|
products: [
|
||||||
dependencies: []
|
.library(name: "CSV", targets: ["CSV"])
|
||||||
|
],
|
||||||
|
targets: [
|
||||||
|
.target(name: "CSV"),
|
||||||
|
.testTarget(name: "CSVTests", dependencies: ["CSV"])
|
||||||
|
],
|
||||||
|
swiftLanguageVersions: [.v5]
|
||||||
)
|
)
|
||||||
|
|
166
README.md
166
README.md
|
@ -1,28 +1,38 @@
|
||||||
# CSV.swift
|
# CSV.swift
|
||||||
|
|
||||||
CSV reading library written in Swift.
|
[](https://travis-ci.org/yaslab/CSV.swift)
|
||||||
|
[](https://codecov.io/gh/yaslab/CSV.swift)
|
||||||
|
[](https://www.codetriage.com/yaslab/csv.swift)
|
||||||
|
|
||||||
## Usage
|
CSV reading and writing library written in Swift.
|
||||||
|
|
||||||
### From CSV string
|
## Usage for reading CSV
|
||||||
|
|
||||||
|
### From string
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
import CSV
|
import CSV
|
||||||
|
|
||||||
let csvString = "1,\"foo\"\n2,\"bar\""
|
let csvString = "1,foo\n2,bar"
|
||||||
for row in try! CSV(string: csvString) {
|
let csv = try! CSVReader(string: csvString)
|
||||||
|
while let row = csv.next() {
|
||||||
print("\(row)")
|
print("\(row)")
|
||||||
// => ["1", "foo"]
|
|
||||||
// => ["2", "bar"]
|
|
||||||
}
|
}
|
||||||
|
// => ["1", "foo"]
|
||||||
|
// => ["2", "bar"]
|
||||||
```
|
```
|
||||||
|
|
||||||
### From file path
|
### From file
|
||||||
|
|
||||||
|
NOTE: The default character encoding is `UTF8`.
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
|
import Foundation
|
||||||
import CSV
|
import CSV
|
||||||
|
|
||||||
for row in try! CSV(path: "/path/to/file.csv") {
|
let stream = InputStream(fileAtPath: "/path/to/file.csv")!
|
||||||
|
let csv = try! CSVReader(stream: stream)
|
||||||
|
while let row = csv.next() {
|
||||||
print("\(row)")
|
print("\(row)")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -30,14 +40,34 @@ for row in try! CSV(path: "/path/to/file.csv") {
|
||||||
### Getting the header row
|
### Getting the header row
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
let csv = try! CSV(
|
import CSV
|
||||||
path: "/path/to/file.csv",
|
|
||||||
hasHeaderRow: true) // default: false
|
let csvString = "id,name\n1,foo\n2,bar"
|
||||||
|
let csv = try! CSVReader(string: csvString,
|
||||||
|
hasHeaderRow: true) // It must be true.
|
||||||
|
|
||||||
let headerRow = csv.headerRow!
|
let headerRow = csv.headerRow!
|
||||||
|
print("\(headerRow)") // => ["id", "name"]
|
||||||
|
|
||||||
for row in csv {
|
while let row = csv.next() {
|
||||||
// ...
|
print("\(row)")
|
||||||
|
}
|
||||||
|
// => ["1", "foo"]
|
||||||
|
// => ["2", "bar"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Get the field value using subscript
|
||||||
|
|
||||||
|
```swift
|
||||||
|
import CSV
|
||||||
|
|
||||||
|
let csvString = "id,name\n1,foo"
|
||||||
|
let csv = try! CSVReader(string: csvString,
|
||||||
|
hasHeaderRow: true) // It must be true.
|
||||||
|
|
||||||
|
while csv.next() != nil {
|
||||||
|
print("\(csv["id"]!)") // => "1"
|
||||||
|
print("\(csv["name"]!)") // => "foo"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -46,9 +76,92 @@ for row in csv {
|
||||||
If you use a file path, you can provide the character encoding to initializer.
|
If you use a file path, you can provide the character encoding to initializer.
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
let csv = try! CSV(
|
import Foundation
|
||||||
path: "/path/to/file.csv",
|
import CSV
|
||||||
encoding: NSUTF8StringEncoding)
|
|
||||||
|
let stream = InputStream(fileAtPath: "/path/to/file.csv")!
|
||||||
|
let csv = try! CSVReader(stream: stream,
|
||||||
|
codecType: UTF16.self,
|
||||||
|
endian: .big)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reading a row into a Decodable object
|
||||||
|
|
||||||
|
If you have a destination object that conforms to the `Decodable` protocol, you can serialize a row with a new instances of the object.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
struct DecodableExample: Decodable {
|
||||||
|
let intKey: Int
|
||||||
|
let stringKey: String
|
||||||
|
let optionalStringKey: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
let csv = """
|
||||||
|
intKey,stringKey,optionalStringKey
|
||||||
|
1234,abcd,
|
||||||
|
"""
|
||||||
|
|
||||||
|
var records = [DecodableExample]()
|
||||||
|
do {
|
||||||
|
let reader = try CSVReader(string: csv, hasHeaderRow: true)
|
||||||
|
let decoder = CSVRowDecoder()
|
||||||
|
while reader.next() != nil {
|
||||||
|
let row = try decoder.decode(DecodableExample.self, from: reader)
|
||||||
|
records.append(row)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Invalid row format
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage for writing CSV
|
||||||
|
|
||||||
|
### Write to memory and get a CSV String
|
||||||
|
|
||||||
|
NOTE: The default character encoding is `UTF8`.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
import Foundation
|
||||||
|
import CSV
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: .toMemory())
|
||||||
|
|
||||||
|
// Write a row
|
||||||
|
try! csv.write(row: ["id", "name"])
|
||||||
|
|
||||||
|
// Write fields separately
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: "1")
|
||||||
|
try! csv.write(field: "foo")
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: "2")
|
||||||
|
try! csv.write(field: "bar")
|
||||||
|
|
||||||
|
csv.stream.close()
|
||||||
|
|
||||||
|
// Get a String
|
||||||
|
let csvData = csv.stream.property(forKey: .dataWrittenToMemoryStreamKey) as! Data
|
||||||
|
let csvString = String(data: csvData, encoding: .utf8)!
|
||||||
|
print(csvString)
|
||||||
|
// => "id,name\n1,foo\n2,bar"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Write to file
|
||||||
|
|
||||||
|
NOTE: The default character encoding is `UTF8`.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
import Foundation
|
||||||
|
import CSV
|
||||||
|
|
||||||
|
let stream = OutputStream(toFileAtPath: "/path/to/file.csv", append: false)!
|
||||||
|
let csv = try! CSVWriter(stream: stream)
|
||||||
|
|
||||||
|
try! csv.write(row: ["id", "name"])
|
||||||
|
try! csv.write(row: ["1", "foo"])
|
||||||
|
try! csv.write(row: ["1", "bar"])
|
||||||
|
|
||||||
|
csv.stream.close()
|
||||||
```
|
```
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
@ -56,28 +169,25 @@ let csv = try! CSV(
|
||||||
### CocoaPods
|
### CocoaPods
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
pod 'CSV.swift', '~> 0.1'
|
pod 'CSV.swift', '~> 2.4.3'
|
||||||
```
|
```
|
||||||
|
|
||||||
### Carthage
|
### Carthage
|
||||||
|
|
||||||
```
|
```
|
||||||
github "yaslab/CSV.swift" ~> 0.1
|
github "yaslab/CSV.swift" ~> 2.4.3
|
||||||
```
|
```
|
||||||
|
|
||||||
### Swift Package Manager
|
### Swift Package Manager
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
import PackageDescription
|
.package(url: "https://github.com/yaslab/CSV.swift.git", .upToNextMinor(from: "2.4.3"))
|
||||||
|
|
||||||
let package = Package(
|
|
||||||
name: "PackageName",
|
|
||||||
dependencies: [
|
|
||||||
.Package(url: "https://github.com/yaslab/CSV.swift", majorVersion: 0, minor: 1)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Reference specification
|
||||||
|
|
||||||
|
- [RFC4180](http://www.ietf.org/rfc/rfc4180.txt) ([en](http://www.ietf.org/rfc/rfc4180.txt), [ja](http://www.kasai.fm/wiki/rfc4180jp))
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
CSV.swift is released under the MIT license. See the LICENSE file for more info.
|
CSV.swift is released under the MIT license. See the [LICENSE](https://github.com/yaslab/CSV.swift/blob/master/LICENSE) file for more info.
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
//
|
|
||||||
// ByteOrder.swift
|
|
||||||
// CSV
|
|
||||||
//
|
|
||||||
// Created by Yasuhiro Hatta on 2016/06/11.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
import CoreFoundation
|
|
||||||
|
|
||||||
internal func ReadBigInt16(base: UnsafePointer<Void>, byteOffset: Int) -> UInt16 {
|
|
||||||
let bytes = UnsafePointer<UInt8>(base).advancedBy(byteOffset)
|
|
||||||
let int16Array = UnsafePointer<UInt16>(bytes)
|
|
||||||
return CFSwapInt16BigToHost(int16Array[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
internal func ReadBigInt32(base: UnsafePointer<Void>, byteOffset: Int) -> UInt32 {
|
|
||||||
let bytes = UnsafePointer<UInt8>(base).advancedBy(byteOffset)
|
|
||||||
let int32Array = UnsafePointer<UInt32>(bytes)
|
|
||||||
return CFSwapInt32BigToHost(int32Array[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
internal func ReadLittleInt16(base: UnsafePointer<Void>, byteOffset: Int) -> UInt16 {
|
|
||||||
let bytes = UnsafePointer<UInt8>(base).advancedBy(byteOffset)
|
|
||||||
let int16Array = UnsafePointer<UInt16>(bytes)
|
|
||||||
return CFSwapInt16LittleToHost(int16Array[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
internal func ReadLittleInt32(base: UnsafePointer<Void>, byteOffset: Int) -> UInt32 {
|
|
||||||
let bytes = UnsafePointer<UInt8>(base).advancedBy(byteOffset)
|
|
||||||
let int32Array = UnsafePointer<UInt32>(bytes)
|
|
||||||
return CFSwapInt32LittleToHost(int32Array[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
internal func IsBigEndian() -> Bool {
|
|
||||||
return CFByteOrderGetCurrent() == CFByteOrder(CFByteOrderBigEndian.rawValue)
|
|
||||||
}
|
|
|
@ -1,391 +0,0 @@
|
||||||
//
|
|
||||||
// CSV.swift
|
|
||||||
// CSV
|
|
||||||
//
|
|
||||||
// Created by Yasuhiro Hatta on 2016/06/11.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
private let LF: UInt32 = 0x0a //'\n'
|
|
||||||
private let CR: UInt32 = 0x0d //'\r'
|
|
||||||
private let DQUOTE: UInt32 = 0x22 //'"'
|
|
||||||
|
|
||||||
internal let defaultHasHeaderRow = false
|
|
||||||
internal let defaultEncoding = NSUTF8StringEncoding
|
|
||||||
internal let defaultDelimiter: CChar = 0x2c //','
|
|
||||||
internal let defaultBufferSize = 8192
|
|
||||||
|
|
||||||
internal let utf8BOM: [UInt8] = [0xef, 0xbb, 0xbf]
|
|
||||||
internal let utf16BigEndianBOM: [UInt8] = [0xfe, 0xff]
|
|
||||||
internal let utf16LittleEndianBOM: [UInt8] = [0xff, 0xfe]
|
|
||||||
internal let utf32BigEndianBOM: [UInt8] = [0x00, 0x00, 0xfe, 0xff]
|
|
||||||
internal let utf32LittleEndianBOM: [UInt8] = [0xff, 0xfe, 0x00, 0x00]
|
|
||||||
|
|
||||||
public class CSV: SequenceType, GeneratorType {
|
|
||||||
|
|
||||||
private let stream: NSInputStream
|
|
||||||
private let encoding: NSStringEncoding
|
|
||||||
private let delimiter: UInt32
|
|
||||||
private let bufferSize: Int
|
|
||||||
|
|
||||||
private var buffer: UnsafeMutablePointer<UInt8>!
|
|
||||||
private var bufferOffset: Int
|
|
||||||
private var lastReadCount: Int
|
|
||||||
|
|
||||||
private let charWidth: Int
|
|
||||||
|
|
||||||
private let fieldBuffer: NSMutableData
|
|
||||||
private var _headerRow: [String]? = nil
|
|
||||||
private var _currentRow: [String]? = nil
|
|
||||||
|
|
||||||
private var closed: Bool = false
|
|
||||||
|
|
||||||
public private(set) var lastError: CSVError? = nil
|
|
||||||
|
|
||||||
public init(
|
|
||||||
stream: NSInputStream,
|
|
||||||
hasHeaderRow: Bool = defaultHasHeaderRow,
|
|
||||||
encoding: NSStringEncoding = defaultEncoding,
|
|
||||||
delimiter: CChar = defaultDelimiter,
|
|
||||||
bufferSize: Int = defaultBufferSize)
|
|
||||||
throws
|
|
||||||
{
|
|
||||||
self.stream = stream
|
|
||||||
|
|
||||||
var bs = bufferSize
|
|
||||||
if bs < 0 {
|
|
||||||
throw CSVError.ParameterError
|
|
||||||
}
|
|
||||||
if bs < 8 {
|
|
||||||
bs = 8
|
|
||||||
}
|
|
||||||
self.bufferSize = bs + (4 - (bs % 4))
|
|
||||||
|
|
||||||
self.delimiter = UInt32(delimiter)
|
|
||||||
|
|
||||||
let b = malloc(bufferSize)
|
|
||||||
if b == nil {
|
|
||||||
throw CSVError.MemoryAllocationFailed
|
|
||||||
}
|
|
||||||
self.buffer = UnsafeMutablePointer<UInt8>(b)
|
|
||||||
self.bufferOffset = 0
|
|
||||||
|
|
||||||
self.fieldBuffer = NSMutableData()
|
|
||||||
|
|
||||||
if stream.streamStatus == .NotOpen {
|
|
||||||
stream.open()
|
|
||||||
}
|
|
||||||
if stream.streamStatus != .Open {
|
|
||||||
throw CSVError.StreamError
|
|
||||||
}
|
|
||||||
|
|
||||||
self.lastReadCount = stream.read(self.buffer, maxLength: bufferSize)
|
|
||||||
|
|
||||||
var e = encoding
|
|
||||||
|
|
||||||
switch encoding {
|
|
||||||
case NSUTF16StringEncoding,
|
|
||||||
NSUTF16BigEndianStringEncoding,
|
|
||||||
NSUTF16LittleEndianStringEncoding:
|
|
||||||
|
|
||||||
charWidth = 2
|
|
||||||
if encoding == NSUTF16StringEncoding {
|
|
||||||
let nativeEndian = IsBigEndian()
|
|
||||||
? NSUTF16BigEndianStringEncoding
|
|
||||||
: NSUTF16LittleEndianStringEncoding
|
|
||||||
e = nativeEndian
|
|
||||||
if lastReadCount >= charWidth {
|
|
||||||
if memcmp(buffer, utf16BigEndianBOM, charWidth) == 0 {
|
|
||||||
e = NSUTF16BigEndianStringEncoding
|
|
||||||
self.bufferOffset += charWidth
|
|
||||||
}
|
|
||||||
else if memcmp(buffer, utf16LittleEndianBOM, charWidth) == 0 {
|
|
||||||
e = NSUTF16LittleEndianStringEncoding
|
|
||||||
self.bufferOffset += charWidth
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case NSUTF32StringEncoding,
|
|
||||||
NSUTF32BigEndianStringEncoding,
|
|
||||||
NSUTF32LittleEndianStringEncoding:
|
|
||||||
|
|
||||||
charWidth = 4
|
|
||||||
if encoding == NSUTF32StringEncoding {
|
|
||||||
let nativeEndian = IsBigEndian()
|
|
||||||
? NSUTF32BigEndianStringEncoding
|
|
||||||
: NSUTF32LittleEndianStringEncoding
|
|
||||||
e = nativeEndian
|
|
||||||
if lastReadCount >= charWidth {
|
|
||||||
if memcmp(buffer, utf32BigEndianBOM, charWidth) == 0 {
|
|
||||||
e = NSUTF32BigEndianStringEncoding
|
|
||||||
self.bufferOffset += charWidth
|
|
||||||
}
|
|
||||||
else if memcmp(buffer, utf32LittleEndianBOM, charWidth) == 0 {
|
|
||||||
e = NSUTF32LittleEndianStringEncoding
|
|
||||||
self.bufferOffset += charWidth
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
charWidth = 1
|
|
||||||
if encoding == NSUTF8StringEncoding {
|
|
||||||
let bomSize = 3
|
|
||||||
if lastReadCount >= bomSize {
|
|
||||||
if memcmp(buffer, utf8BOM, charWidth) == 0 {
|
|
||||||
self.bufferOffset += bomSize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.encoding = e
|
|
||||||
|
|
||||||
if hasHeaderRow {
|
|
||||||
guard let nextRow = next() else {
|
|
||||||
throw CSVError.HeaderReadError
|
|
||||||
}
|
|
||||||
_headerRow = nextRow
|
|
||||||
_currentRow = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
close()
|
|
||||||
}
|
|
||||||
|
|
||||||
public func close() {
|
|
||||||
if !closed {
|
|
||||||
stream.close()
|
|
||||||
if buffer != nil {
|
|
||||||
free(buffer)
|
|
||||||
buffer = nil
|
|
||||||
}
|
|
||||||
_headerRow = nil
|
|
||||||
_currentRow = nil
|
|
||||||
closed = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public var headerRow: [String]? {
|
|
||||||
return _headerRow
|
|
||||||
}
|
|
||||||
|
|
||||||
public var currentRow: [String]? {
|
|
||||||
return _currentRow
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: GeneratorType
|
|
||||||
|
|
||||||
public func next() -> [String]? {
|
|
||||||
fieldBuffer.length = 0
|
|
||||||
_currentRow = nil
|
|
||||||
|
|
||||||
if closed {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if lastReadCount <= 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var fields = [String]()
|
|
||||||
|
|
||||||
var fieldStart = bufferOffset
|
|
||||||
var charLength = 0
|
|
||||||
var escaping = false
|
|
||||||
var quotationCount = 0
|
|
||||||
|
|
||||||
var prev: UInt32 = 0
|
|
||||||
|
|
||||||
while true {
|
|
||||||
if bufferOffset >= lastReadCount {
|
|
||||||
if charLength > 0 {
|
|
||||||
fieldBuffer.appendBytes(buffer + fieldStart, length: charWidth * charLength)
|
|
||||||
}
|
|
||||||
bufferOffset = 0
|
|
||||||
fieldStart = 0
|
|
||||||
charLength = 0
|
|
||||||
|
|
||||||
lastReadCount = stream.read(buffer, maxLength: bufferSize)
|
|
||||||
if lastReadCount < 0 {
|
|
||||||
// bad end
|
|
||||||
lastError = CSVError.StreamError
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if lastReadCount == 0 {
|
|
||||||
// true end
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var c: UInt32 = 0
|
|
||||||
|
|
||||||
switch encoding {
|
|
||||||
case NSUTF16BigEndianStringEncoding:
|
|
||||||
let _c = ReadBigInt16(buffer, byteOffset: bufferOffset)
|
|
||||||
c = UInt32(_c)
|
|
||||||
|
|
||||||
case NSUTF16LittleEndianStringEncoding:
|
|
||||||
let _c = ReadLittleInt16(buffer, byteOffset: bufferOffset)
|
|
||||||
c = UInt32(_c)
|
|
||||||
|
|
||||||
case NSUTF32BigEndianStringEncoding:
|
|
||||||
c = ReadBigInt32(buffer, byteOffset: bufferOffset)
|
|
||||||
|
|
||||||
case NSUTF32LittleEndianStringEncoding:
|
|
||||||
c = ReadLittleInt32(buffer, byteOffset: bufferOffset)
|
|
||||||
|
|
||||||
default: // multi-byte character encodings
|
|
||||||
let _c = (buffer + bufferOffset)[0]
|
|
||||||
c = UInt32(_c)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c == DQUOTE {
|
|
||||||
quotationCount += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if c == DQUOTE && charLength == 0 {
|
|
||||||
escaping = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if c == delimiter && prev == DQUOTE && (quotationCount % 2 == 0) {
|
|
||||||
escaping = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 行の終わり
|
|
||||||
if prev == CR && c != LF && !escaping {
|
|
||||||
fieldBuffer.appendBytes(buffer + fieldStart, length: charWidth * charLength)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// 行の終わり
|
|
||||||
if c == LF && !escaping {
|
|
||||||
fieldBuffer.appendBytes(buffer + fieldStart, length: charWidth * charLength)
|
|
||||||
bufferOffset += charWidth
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
prev = c
|
|
||||||
|
|
||||||
// 行の終わり
|
|
||||||
if c == CR && !escaping {
|
|
||||||
bufferOffset += charWidth
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
bufferOffset += charWidth
|
|
||||||
|
|
||||||
// フィールドの終わり
|
|
||||||
if c == delimiter && !escaping {
|
|
||||||
fieldBuffer.appendBytes(buffer + fieldStart, length: charWidth * charLength)
|
|
||||||
|
|
||||||
guard let field = getField(quotationCount) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
fields.append(field)
|
|
||||||
|
|
||||||
// reset
|
|
||||||
fieldBuffer.length = 0
|
|
||||||
quotationCount = 0
|
|
||||||
charLength = 0
|
|
||||||
|
|
||||||
fieldStart = bufferOffset
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
charLength += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let field = getField(quotationCount) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 最後の空行
|
|
||||||
if isBufferEOF && fields.count == 0 && field.isEmpty {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
fields.append(field)
|
|
||||||
_currentRow = fields
|
|
||||||
|
|
||||||
return fields
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Utility
|
|
||||||
|
|
||||||
private var isBufferEOF: Bool {
|
|
||||||
if stream.hasBytesAvailable {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return bufferOffset >= (lastReadCount - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func getField(quotationCount: Int) -> String? {
|
|
||||||
guard var field = String(data: fieldBuffer, encoding: encoding) else {
|
|
||||||
lastError = CSVError.StringEncodingMismatch
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if quotationCount >= 2
|
|
||||||
&& field.hasPrefix("\"")
|
|
||||||
&& field.hasSuffix("\"")
|
|
||||||
{
|
|
||||||
//let start = field.index(field.startIndex, offsetBy: 1)
|
|
||||||
//let end = field.index(field.endIndex, offsetBy: -1)
|
|
||||||
let start = field.startIndex.advancedBy(1)
|
|
||||||
let end = field.endIndex.advancedBy(-1)
|
|
||||||
field = field[start..<end]
|
|
||||||
}
|
|
||||||
|
|
||||||
if quotationCount >= 4 {
|
|
||||||
field = field.stringByReplacingOccurrencesOfString("\"\"", withString: "\"")
|
|
||||||
}
|
|
||||||
|
|
||||||
return field
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension CSV {
|
|
||||||
|
|
||||||
public convenience init(
|
|
||||||
path: String,
|
|
||||||
hasHeaderRow: Bool = defaultHasHeaderRow,
|
|
||||||
encoding: NSStringEncoding = defaultEncoding)
|
|
||||||
throws
|
|
||||||
{
|
|
||||||
guard let stream = NSInputStream(fileAtPath: path) else {
|
|
||||||
throw CSVError.StreamError
|
|
||||||
}
|
|
||||||
try self.init(stream: stream, hasHeaderRow: hasHeaderRow, encoding: encoding)
|
|
||||||
}
|
|
||||||
|
|
||||||
public convenience init(
|
|
||||||
url: NSURL,
|
|
||||||
hasHeaderRow: Bool = defaultHasHeaderRow,
|
|
||||||
encoding: NSStringEncoding = defaultEncoding)
|
|
||||||
throws
|
|
||||||
{
|
|
||||||
guard let stream = NSInputStream(URL: url) else {
|
|
||||||
throw CSVError.StreamError
|
|
||||||
}
|
|
||||||
try self.init(stream: stream, hasHeaderRow: hasHeaderRow, encoding: encoding)
|
|
||||||
}
|
|
||||||
|
|
||||||
public convenience init(
|
|
||||||
string: String,
|
|
||||||
hasHeaderRow: Bool = defaultHasHeaderRow)
|
|
||||||
throws
|
|
||||||
{
|
|
||||||
let encoding = defaultEncoding
|
|
||||||
guard let data = string.dataUsingEncoding(encoding) else {
|
|
||||||
throw CSVError.StringEncodingMismatch
|
|
||||||
}
|
|
||||||
let memoryStream = NSInputStream(data: data)
|
|
||||||
try self.init(stream: memoryStream, hasHeaderRow: hasHeaderRow, encoding: encoding)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,295 @@
|
||||||
|
//
|
||||||
|
// BinaryReader.swift
|
||||||
|
// CSV
|
||||||
|
//
|
||||||
|
// Created by Yasuhiro Hatta on 2016/06/20.
|
||||||
|
// Copyright © 2016 yaslab. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum UnicodeBOM {
|
||||||
|
// UTF-8
|
||||||
|
static let utf8: [UInt8] = [0xef, 0xbb, 0xbf]
|
||||||
|
// UTF-16 BE
|
||||||
|
static let utf16BE: [UInt8] = [0xfe, 0xff]
|
||||||
|
// UTF-16 LE
|
||||||
|
static let utf16LE: [UInt8] = [0xff, 0xfe]
|
||||||
|
// UTF-32 BE
|
||||||
|
static let utf32BE: [UInt8] = [0x00, 0x00, 0xfe, 0xff]
|
||||||
|
// UTF-32 LE
|
||||||
|
static let utf32LE: [UInt8] = [0xff, 0xfe, 0x00, 0x00]
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UnicodeBOM {
|
||||||
|
|
||||||
|
fileprivate static func readBOM(buffer: UnsafePointer<UInt8>, count: Int) -> (Endian, Int)? {
|
||||||
|
if count >= 4 {
|
||||||
|
// UTF-32 BE
|
||||||
|
if compare(buffer: buffer, bom: UnicodeBOM.utf32BE) {
|
||||||
|
return (.big, UnicodeBOM.utf32BE.count)
|
||||||
|
}
|
||||||
|
// UTF-32 LE
|
||||||
|
if compare(buffer: buffer, bom: UnicodeBOM.utf32LE) {
|
||||||
|
return (.little, UnicodeBOM.utf32LE.count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if count >= 3 {
|
||||||
|
// UTF-8
|
||||||
|
if compare(buffer: buffer, bom: UnicodeBOM.utf8) {
|
||||||
|
return (.unknown, UnicodeBOM.utf8.count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if count >= 2 {
|
||||||
|
// UTF-16 BE
|
||||||
|
if compare(buffer: buffer, bom: UnicodeBOM.utf16BE) {
|
||||||
|
return (.big, UnicodeBOM.utf16BE.count)
|
||||||
|
}
|
||||||
|
// UTF-16 LE
|
||||||
|
if compare(buffer: buffer, bom: UnicodeBOM.utf16LE) {
|
||||||
|
return (.little, UnicodeBOM.utf16LE.count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func compare(buffer: UnsafePointer<UInt8>, bom: [UInt8]) -> Bool {
|
||||||
|
for i in 0 ..< bom.count {
|
||||||
|
guard buffer[i] == bom[i] else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class BinaryReader {
|
||||||
|
|
||||||
|
private let stream: InputStream
|
||||||
|
private let endian: Endian
|
||||||
|
private let closeOnDeinit: Bool
|
||||||
|
|
||||||
|
private let _buffer: UnsafeMutablePointer<UInt8>
|
||||||
|
private let _capacity: Int
|
||||||
|
private var _count: Int = 0
|
||||||
|
private var _position: Int = 0
|
||||||
|
|
||||||
|
internal init(
|
||||||
|
stream: InputStream,
|
||||||
|
endian: Endian,
|
||||||
|
closeOnDeinit: Bool,
|
||||||
|
bufferSize: Int = Int(UInt16.max)) throws {
|
||||||
|
|
||||||
|
var endian = endian
|
||||||
|
|
||||||
|
if stream.streamStatus == .notOpen {
|
||||||
|
stream.open()
|
||||||
|
}
|
||||||
|
if stream.streamStatus != .open {
|
||||||
|
throw CSVError.cannotOpenFile
|
||||||
|
}
|
||||||
|
|
||||||
|
_buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
|
||||||
|
_capacity = bufferSize
|
||||||
|
_count = stream.read(_buffer, maxLength: _capacity)
|
||||||
|
if _count < 0 {
|
||||||
|
throw CSVError.cannotReadFile
|
||||||
|
}
|
||||||
|
|
||||||
|
var position = 0
|
||||||
|
if let (e, l) = UnicodeBOM.readBOM(buffer: _buffer, count: _count) {
|
||||||
|
if endian != .unknown && endian != e {
|
||||||
|
throw CSVError.stringEndianMismatch
|
||||||
|
}
|
||||||
|
endian = e
|
||||||
|
position = l
|
||||||
|
}
|
||||||
|
_position = position
|
||||||
|
|
||||||
|
self.stream = stream
|
||||||
|
self.endian = endian
|
||||||
|
self.closeOnDeinit = closeOnDeinit
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
if closeOnDeinit && stream.streamStatus != .closed {
|
||||||
|
stream.close()
|
||||||
|
}
|
||||||
|
_buffer.deallocate()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal var hasBytesAvailable: Bool {
|
||||||
|
if _count - _position > 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return stream.hasBytesAvailable
|
||||||
|
}
|
||||||
|
|
||||||
|
@inline(__always)
|
||||||
|
private func readStream(_ buffer: UnsafeMutablePointer<UInt8>, maxLength: Int) throws -> Int {
|
||||||
|
var count = 0
|
||||||
|
for i in 0 ..< maxLength {
|
||||||
|
if _position >= _count {
|
||||||
|
let result = stream.read(_buffer, maxLength: _capacity)
|
||||||
|
if result < 0 {
|
||||||
|
if let error = stream.streamError {
|
||||||
|
throw CSVError.streamErrorHasOccurred(error: error)
|
||||||
|
} else {
|
||||||
|
throw CSVError.cannotReadFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_count = result
|
||||||
|
_position = 0
|
||||||
|
if result == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer[i] = _buffer[_position]
|
||||||
|
_position += 1
|
||||||
|
count += 1
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func readUInt8() throws -> UInt8 {
|
||||||
|
let bufferSize = 1
|
||||||
|
var buffer: UInt8 = 0
|
||||||
|
if try readStream(&buffer, maxLength: bufferSize) != bufferSize {
|
||||||
|
throw CSVError.cannotReadFile
|
||||||
|
}
|
||||||
|
return buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func readUInt16() throws -> UInt16 {
|
||||||
|
let bufferSize = 2
|
||||||
|
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
|
||||||
|
defer { buffer.deallocate() }
|
||||||
|
if try readStream(buffer, maxLength: bufferSize) != bufferSize {
|
||||||
|
throw CSVError.stringEncodingMismatch
|
||||||
|
}
|
||||||
|
return try buffer.withMemoryRebound(to: UInt16.self, capacity: 1) {
|
||||||
|
switch endian {
|
||||||
|
case .big:
|
||||||
|
return UInt16(bigEndian: $0.pointee)
|
||||||
|
case .little:
|
||||||
|
return UInt16(littleEndian: $0.pointee)
|
||||||
|
default:
|
||||||
|
throw CSVError.stringEndianMismatch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func readUInt32() throws -> UInt32 {
|
||||||
|
let bufferSize = 4
|
||||||
|
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
|
||||||
|
defer { buffer.deallocate() }
|
||||||
|
if try readStream(buffer, maxLength: bufferSize) != bufferSize {
|
||||||
|
throw CSVError.stringEncodingMismatch
|
||||||
|
}
|
||||||
|
return try buffer.withMemoryRebound(to: UInt32.self, capacity: 1) {
|
||||||
|
switch endian {
|
||||||
|
case .big:
|
||||||
|
return UInt32(bigEndian: $0.pointee)
|
||||||
|
case .little:
|
||||||
|
return UInt32(littleEndian: $0.pointee)
|
||||||
|
default:
|
||||||
|
throw CSVError.stringEndianMismatch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BinaryReader {
|
||||||
|
|
||||||
|
internal class UInt8Iterator: Sequence, IteratorProtocol {
|
||||||
|
|
||||||
|
private let reader: BinaryReader
|
||||||
|
internal var errorHandler: ((Error) -> Void)?
|
||||||
|
|
||||||
|
fileprivate init(reader: BinaryReader) {
|
||||||
|
self.reader = reader
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func next() -> UInt8? {
|
||||||
|
if !reader.hasBytesAvailable {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
return try reader.readUInt8()
|
||||||
|
} catch {
|
||||||
|
errorHandler?(error)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func makeUInt8Iterator() -> UInt8Iterator {
|
||||||
|
return UInt8Iterator(reader: self)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BinaryReader {
|
||||||
|
|
||||||
|
internal class UInt16Iterator: Sequence, IteratorProtocol {
|
||||||
|
|
||||||
|
private let reader: BinaryReader
|
||||||
|
internal var errorHandler: ((Error) -> Void)?
|
||||||
|
|
||||||
|
fileprivate init(reader: BinaryReader) {
|
||||||
|
self.reader = reader
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func next() -> UInt16? {
|
||||||
|
if !reader.hasBytesAvailable {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
return try reader.readUInt16()
|
||||||
|
} catch {
|
||||||
|
errorHandler?(error)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func makeUInt16Iterator() -> UInt16Iterator {
|
||||||
|
return UInt16Iterator(reader: self)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BinaryReader {
|
||||||
|
|
||||||
|
internal class UInt32Iterator: Sequence, IteratorProtocol {
|
||||||
|
|
||||||
|
private let reader: BinaryReader
|
||||||
|
internal var errorHandler: ((Error) -> Void)?
|
||||||
|
|
||||||
|
fileprivate init(reader: BinaryReader) {
|
||||||
|
self.reader = reader
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func next() -> UInt32? {
|
||||||
|
if !reader.hasBytesAvailable {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
return try reader.readUInt32()
|
||||||
|
} catch {
|
||||||
|
errorHandler?(error)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func makeUInt32Iterator() -> UInt32Iterator {
|
||||||
|
return UInt32Iterator(reader: self)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
//
|
||||||
|
// CSVError.swift
|
||||||
|
// CSV
|
||||||
|
//
|
||||||
|
// Created by Yasuhiro Hatta on 2016/06/11.
|
||||||
|
// Copyright © 2016 yaslab. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
/// No overview available.
|
||||||
|
public enum CSVError: Error {
|
||||||
|
|
||||||
|
/// No overview available.
|
||||||
|
case cannotOpenFile
|
||||||
|
/// No overview available.
|
||||||
|
case cannotReadFile
|
||||||
|
/// No overview available.
|
||||||
|
case cannotWriteStream
|
||||||
|
/// No overview available.
|
||||||
|
case streamErrorHasOccurred(error: Error)
|
||||||
|
/// No overview available.
|
||||||
|
case unicodeDecoding
|
||||||
|
/// No overview available.
|
||||||
|
case cannotReadHeaderRow
|
||||||
|
/// No overview available.
|
||||||
|
case stringEncodingMismatch
|
||||||
|
/// No overview available.
|
||||||
|
case stringEndianMismatch
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,391 @@
|
||||||
|
//
|
||||||
|
// CSVReader.swift
|
||||||
|
// CSV
|
||||||
|
//
|
||||||
|
// Created by Yasuhiro Hatta on 2016/06/11.
|
||||||
|
// Copyright © 2016 yaslab. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
internal let LF: UnicodeScalar = "\n"
|
||||||
|
internal let CR: UnicodeScalar = "\r"
|
||||||
|
internal let DQUOTE: UnicodeScalar = "\""
|
||||||
|
|
||||||
|
internal let DQUOTE_STR: String = "\""
|
||||||
|
internal let DQUOTE2_STR: String = "\"\""
|
||||||
|
|
||||||
|
/// No overview available.
|
||||||
|
public class CSVReader {
|
||||||
|
|
||||||
|
/// No overview available.
|
||||||
|
public struct Configuration {
|
||||||
|
|
||||||
|
/// `true` if the CSV has a header row, otherwise `false`. Default: `false`.
|
||||||
|
public var hasHeaderRow: Bool
|
||||||
|
/// No overview available.
|
||||||
|
public var trimFields: Bool
|
||||||
|
/// Default: `","`.
|
||||||
|
public var delimiter: UnicodeScalar
|
||||||
|
/// No overview available.
|
||||||
|
public var whitespaces: CharacterSet
|
||||||
|
|
||||||
|
/// No overview available.
|
||||||
|
internal init(
|
||||||
|
hasHeaderRow: Bool,
|
||||||
|
trimFields: Bool,
|
||||||
|
delimiter: UnicodeScalar,
|
||||||
|
whitespaces: CharacterSet) {
|
||||||
|
|
||||||
|
self.hasHeaderRow = hasHeaderRow
|
||||||
|
self.trimFields = trimFields
|
||||||
|
self.delimiter = delimiter
|
||||||
|
|
||||||
|
var whitespaces = whitespaces
|
||||||
|
_ = whitespaces.remove(delimiter)
|
||||||
|
self.whitespaces = whitespaces
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate var iterator: AnyIterator<UnicodeScalar>
|
||||||
|
public let configuration: Configuration
|
||||||
|
public fileprivate (set) var error: Error?
|
||||||
|
|
||||||
|
fileprivate var back: UnicodeScalar?
|
||||||
|
|
||||||
|
/// CSV header row. To set a value for this property,
|
||||||
|
/// you set `true` to `headerRow` in initializer.
|
||||||
|
public private (set) var headerRow: [String]?
|
||||||
|
|
||||||
|
public fileprivate (set) var currentRow: [String]?
|
||||||
|
|
||||||
|
internal init<T: IteratorProtocol>(
|
||||||
|
iterator: T,
|
||||||
|
configuration: Configuration
|
||||||
|
) throws where T.Element == UnicodeScalar {
|
||||||
|
|
||||||
|
self.iterator = AnyIterator(iterator)
|
||||||
|
self.configuration = configuration
|
||||||
|
|
||||||
|
if configuration.hasHeaderRow {
|
||||||
|
guard let headerRow = readRow() else {
|
||||||
|
throw CSVError.cannotReadHeaderRow
|
||||||
|
}
|
||||||
|
self.headerRow = headerRow
|
||||||
|
self.currentRow = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CSVReader {
|
||||||
|
|
||||||
|
public static let defaultHasHeaderRow: Bool = false
|
||||||
|
public static let defaultTrimFields: Bool = false
|
||||||
|
public static let defaultDelimiter: UnicodeScalar = ","
|
||||||
|
public static let defaultWhitespaces: CharacterSet = .whitespaces
|
||||||
|
|
||||||
|
/// Create an instance with `InputStream`.
|
||||||
|
///
|
||||||
|
/// - parameter stream: An `InputStream` object. If the stream is not open,
|
||||||
|
/// initializer opens automatically.
|
||||||
|
/// - parameter codecType: A `UnicodeCodec` type for `stream`.
|
||||||
|
/// - parameter hasHeaderRow: `true` if the CSV has a header row, otherwise `false`. Default: `false`.
|
||||||
|
/// - parameter delimiter: Default: `","`.
|
||||||
|
public convenience init<T: UnicodeCodec>(
|
||||||
|
stream: InputStream,
|
||||||
|
codecType: T.Type,
|
||||||
|
hasHeaderRow: Bool = defaultHasHeaderRow,
|
||||||
|
trimFields: Bool = defaultTrimFields,
|
||||||
|
delimiter: UnicodeScalar = defaultDelimiter,
|
||||||
|
whitespaces: CharacterSet = defaultWhitespaces
|
||||||
|
) throws where T.CodeUnit == UInt8 {
|
||||||
|
|
||||||
|
let reader = try BinaryReader(stream: stream, endian: .unknown, closeOnDeinit: true)
|
||||||
|
let input = reader.makeUInt8Iterator()
|
||||||
|
let iterator = UnicodeIterator(input: input, inputEncodingType: codecType)
|
||||||
|
let config = Configuration(hasHeaderRow: hasHeaderRow,
|
||||||
|
trimFields: trimFields,
|
||||||
|
delimiter: delimiter,
|
||||||
|
whitespaces: whitespaces)
|
||||||
|
try self.init(iterator: iterator, configuration: config)
|
||||||
|
input.errorHandler = { [unowned self] in self.errorHandler(error: $0) }
|
||||||
|
iterator.errorHandler = { [unowned self] in self.errorHandler(error: $0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create an instance with `InputStream`.
|
||||||
|
///
|
||||||
|
/// - parameter stream: An `InputStream` object. If the stream is not open,
|
||||||
|
/// initializer opens automatically.
|
||||||
|
/// - parameter codecType: A `UnicodeCodec` type for `stream`.
|
||||||
|
/// - parameter endian: Endian to use when reading a stream. Default: `.big`.
|
||||||
|
/// - parameter hasHeaderRow: `true` if the CSV has a header row, otherwise `false`. Default: `false`.
|
||||||
|
/// - parameter delimiter: Default: `","`.
|
||||||
|
public convenience init<T: UnicodeCodec>(
|
||||||
|
stream: InputStream,
|
||||||
|
codecType: T.Type,
|
||||||
|
endian: Endian = .big,
|
||||||
|
hasHeaderRow: Bool = defaultHasHeaderRow,
|
||||||
|
trimFields: Bool = defaultTrimFields,
|
||||||
|
delimiter: UnicodeScalar = defaultDelimiter,
|
||||||
|
whitespaces: CharacterSet = defaultWhitespaces
|
||||||
|
) throws where T.CodeUnit == UInt16 {
|
||||||
|
|
||||||
|
let reader = try BinaryReader(stream: stream, endian: endian, closeOnDeinit: true)
|
||||||
|
let input = reader.makeUInt16Iterator()
|
||||||
|
let iterator = UnicodeIterator(input: input, inputEncodingType: codecType)
|
||||||
|
let config = Configuration(hasHeaderRow: hasHeaderRow,
|
||||||
|
trimFields: trimFields,
|
||||||
|
delimiter: delimiter,
|
||||||
|
whitespaces: whitespaces)
|
||||||
|
try self.init(iterator: iterator, configuration: config)
|
||||||
|
input.errorHandler = { [unowned self] in self.errorHandler(error: $0) }
|
||||||
|
iterator.errorHandler = { [unowned self] in self.errorHandler(error: $0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create an instance with `InputStream`.
|
||||||
|
///
|
||||||
|
/// - parameter stream: An `InputStream` object. If the stream is not open,
|
||||||
|
/// initializer opens automatically.
|
||||||
|
/// - parameter codecType: A `UnicodeCodec` type for `stream`.
|
||||||
|
/// - parameter endian: Endian to use when reading a stream. Default: `.big`.
|
||||||
|
/// - parameter hasHeaderRow: `true` if the CSV has a header row, otherwise `false`. Default: `false`.
|
||||||
|
/// - parameter delimiter: Default: `","`.
|
||||||
|
public convenience init<T: UnicodeCodec>(
|
||||||
|
stream: InputStream,
|
||||||
|
codecType: T.Type,
|
||||||
|
endian: Endian = .big,
|
||||||
|
hasHeaderRow: Bool = defaultHasHeaderRow,
|
||||||
|
trimFields: Bool = defaultTrimFields,
|
||||||
|
delimiter: UnicodeScalar = defaultDelimiter,
|
||||||
|
whitespaces: CharacterSet = defaultWhitespaces
|
||||||
|
) throws where T.CodeUnit == UInt32 {
|
||||||
|
|
||||||
|
let reader = try BinaryReader(stream: stream, endian: endian, closeOnDeinit: true)
|
||||||
|
let input = reader.makeUInt32Iterator()
|
||||||
|
let iterator = UnicodeIterator(input: input, inputEncodingType: codecType)
|
||||||
|
let config = Configuration(hasHeaderRow: hasHeaderRow,
|
||||||
|
trimFields: trimFields,
|
||||||
|
delimiter: delimiter,
|
||||||
|
whitespaces: whitespaces)
|
||||||
|
try self.init(iterator: iterator, configuration: config)
|
||||||
|
input.errorHandler = { [unowned self] in self.errorHandler(error: $0) }
|
||||||
|
iterator.errorHandler = { [unowned self] in self.errorHandler(error: $0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create an instance with `InputStream`.
|
||||||
|
///
|
||||||
|
/// - parameter stream: An `InputStream` object. If the stream is not open,
|
||||||
|
/// initializer opens automatically.
|
||||||
|
/// - parameter hasHeaderRow: `true` if the CSV has a header row, otherwise `false`. Default: `false`.
|
||||||
|
/// - parameter delimiter: Default: `","`.
|
||||||
|
public convenience init(
|
||||||
|
stream: InputStream,
|
||||||
|
hasHeaderRow: Bool = defaultHasHeaderRow,
|
||||||
|
trimFields: Bool = defaultTrimFields,
|
||||||
|
delimiter: UnicodeScalar = defaultDelimiter,
|
||||||
|
whitespaces: CharacterSet = defaultWhitespaces
|
||||||
|
) throws {
|
||||||
|
|
||||||
|
try self.init(
|
||||||
|
stream: stream,
|
||||||
|
codecType: UTF8.self,
|
||||||
|
hasHeaderRow: hasHeaderRow,
|
||||||
|
trimFields: trimFields,
|
||||||
|
delimiter: delimiter,
|
||||||
|
whitespaces: whitespaces)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create an instance with CSV string.
|
||||||
|
///
|
||||||
|
/// - parameter string: An CSV string.
|
||||||
|
/// - parameter hasHeaderRow: `true` if the CSV has a header row, otherwise `false`. Default: `false`.
|
||||||
|
/// - parameter delimiter: Default: `","`.
|
||||||
|
public convenience init(
|
||||||
|
string: String,
|
||||||
|
hasHeaderRow: Bool = defaultHasHeaderRow,
|
||||||
|
trimFields: Bool = defaultTrimFields,
|
||||||
|
delimiter: UnicodeScalar = defaultDelimiter,
|
||||||
|
whitespaces: CharacterSet = defaultWhitespaces
|
||||||
|
) throws {
|
||||||
|
|
||||||
|
let iterator = string.unicodeScalars.makeIterator()
|
||||||
|
let config = Configuration(hasHeaderRow: hasHeaderRow,
|
||||||
|
trimFields: trimFields,
|
||||||
|
delimiter: delimiter,
|
||||||
|
whitespaces: whitespaces)
|
||||||
|
try self.init(iterator: iterator, configuration: config)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func errorHandler(error: Error) {
|
||||||
|
//configuration.fileInputErrorHandler?(error, currentRowIndex, currentFieldIndex)
|
||||||
|
self.error = error
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Parse CSV
|
||||||
|
|
||||||
|
extension CSVReader {
|
||||||
|
|
||||||
|
fileprivate func readRow() -> [String]? {
|
||||||
|
var c = moveNext()
|
||||||
|
if c == nil {
|
||||||
|
currentRow = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var row = [String]()
|
||||||
|
var field: String
|
||||||
|
var end: Bool
|
||||||
|
while true {
|
||||||
|
if configuration.trimFields {
|
||||||
|
// Trim the leading spaces
|
||||||
|
while c != nil && configuration.whitespaces.contains(c!) {
|
||||||
|
c = moveNext()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c == nil {
|
||||||
|
(field, end) = ("", true)
|
||||||
|
} else if c == DQUOTE {
|
||||||
|
(field, end) = readField(quoted: true)
|
||||||
|
} else {
|
||||||
|
back = c
|
||||||
|
(field, end) = readField(quoted: false)
|
||||||
|
|
||||||
|
if configuration.trimFields {
|
||||||
|
// Trim the trailing spaces
|
||||||
|
field = field.trimmingCharacters(in: configuration.whitespaces)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
row.append(field)
|
||||||
|
if end {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
c = moveNext()
|
||||||
|
}
|
||||||
|
|
||||||
|
currentRow = row
|
||||||
|
return row
|
||||||
|
}
|
||||||
|
|
||||||
|
private func readField(quoted: Bool) -> (String, Bool) {
|
||||||
|
var fieldBuffer = String.UnicodeScalarView()
|
||||||
|
|
||||||
|
while let c = moveNext() {
|
||||||
|
if quoted {
|
||||||
|
if c == DQUOTE {
|
||||||
|
var cNext = moveNext()
|
||||||
|
|
||||||
|
if configuration.trimFields {
|
||||||
|
// Trim the trailing spaces
|
||||||
|
while cNext != nil && configuration.whitespaces.contains(cNext!) {
|
||||||
|
cNext = moveNext()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cNext == nil || cNext == CR || cNext == LF {
|
||||||
|
if cNext == CR {
|
||||||
|
let cNextNext = moveNext()
|
||||||
|
if cNextNext != LF {
|
||||||
|
back = cNextNext
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// END ROW
|
||||||
|
return (String(fieldBuffer), true)
|
||||||
|
} else if cNext == configuration.delimiter {
|
||||||
|
// END FIELD
|
||||||
|
return (String(fieldBuffer), false)
|
||||||
|
} else if cNext == DQUOTE {
|
||||||
|
// ESC
|
||||||
|
fieldBuffer.append(DQUOTE)
|
||||||
|
} else {
|
||||||
|
// ERROR?
|
||||||
|
fieldBuffer.append(c)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fieldBuffer.append(c)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if c == CR || c == LF {
|
||||||
|
if c == CR {
|
||||||
|
let cNext = moveNext()
|
||||||
|
if cNext != LF {
|
||||||
|
back = cNext
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// END ROW
|
||||||
|
return (String(fieldBuffer), true)
|
||||||
|
} else if c == configuration.delimiter {
|
||||||
|
// END FIELD
|
||||||
|
return (String(fieldBuffer), false)
|
||||||
|
} else {
|
||||||
|
fieldBuffer.append(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// END FILE
|
||||||
|
return (String(fieldBuffer), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func moveNext() -> UnicodeScalar? {
|
||||||
|
if back != nil {
|
||||||
|
defer {
|
||||||
|
back = nil
|
||||||
|
}
|
||||||
|
return back
|
||||||
|
}
|
||||||
|
return iterator.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//extension CSVReader {
|
||||||
|
//
|
||||||
|
// public func enumerateRows(_ block: ((CSVReader, inout Bool) throws -> Void)) throws {
|
||||||
|
// var stop = false
|
||||||
|
// while next() != nil {
|
||||||
|
// try block(self, &stop)
|
||||||
|
// if stop {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if let error = error {
|
||||||
|
// throw error
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//}
|
||||||
|
|
||||||
|
extension CSVReader: IteratorProtocol {
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
public func next() -> [String]? {
|
||||||
|
return readRow()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CSVReader {
|
||||||
|
|
||||||
|
public subscript(key: String) -> String? {
|
||||||
|
guard let header = headerRow else {
|
||||||
|
fatalError("CSVReader.headerRow must not be nil")
|
||||||
|
}
|
||||||
|
guard let index = header.firstIndex(of: key) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
guard let row = currentRow else {
|
||||||
|
fatalError("CSVReader.currentRow must not be nil")
|
||||||
|
}
|
||||||
|
guard index < row.count else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return row[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,749 @@
|
||||||
|
//
|
||||||
|
// CSVRowDecoder.swift
|
||||||
|
// CSV
|
||||||
|
//
|
||||||
|
// Created by Yasuhiro Hatta on 2018/11/17.
|
||||||
|
// Copyright © 2018 yaslab. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// `CSVRowDecoder` facilitates the decoding of CSV row into semantic `Decodable` types.
|
||||||
|
open class CSVRowDecoder {
|
||||||
|
|
||||||
|
/// The strategy to use for decoding `Bool` values.
|
||||||
|
public enum BoolDecodingStrategy {
|
||||||
|
/// Decode the `Bool` using default initializer.
|
||||||
|
case `default`
|
||||||
|
|
||||||
|
/// Decode the `Bool` as a custom value decoded by the given closure.
|
||||||
|
case custom((_ value: String) throws -> Bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The strategy to use for decoding `Date` values.
|
||||||
|
public enum DateDecodingStrategy {
|
||||||
|
/// Defer to `Date` for decoding. This is the default strategy.
|
||||||
|
case deferredToDate
|
||||||
|
|
||||||
|
/// Decode the `Date` as a UNIX timestamp from a JSON number.
|
||||||
|
case secondsSince1970
|
||||||
|
|
||||||
|
/// Decode the `Date` as UNIX millisecond timestamp from a JSON number.
|
||||||
|
case millisecondsSince1970
|
||||||
|
|
||||||
|
/// Decode the `Date` as an ISO-8601-formatted string (in RFC 3339 format).
|
||||||
|
@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)
|
||||||
|
case iso8601
|
||||||
|
|
||||||
|
/// Decode the `Date` as a string parsed by the given formatter.
|
||||||
|
case formatted(DateFormatter)
|
||||||
|
|
||||||
|
/// Decode the `Date` as a custom value decoded by the given closure.
|
||||||
|
case custom((_ value: String) throws -> Date)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The strategy to use for decoding `Data` values.
|
||||||
|
public enum DataDecodingStrategy {
|
||||||
|
// TODO: Implement unkeyed decoding container.
|
||||||
|
// /// Defer to `Data` for decoding.
|
||||||
|
// case deferredToData
|
||||||
|
|
||||||
|
/// Decode the `Data` from a Base64-encoded string. This is the default strategy.
|
||||||
|
case base64
|
||||||
|
|
||||||
|
/// Decode the `Data` as a custom value decoded by the given closure.
|
||||||
|
case custom((_ value: String) throws -> Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The strategy to use for decoding `nil` values.
|
||||||
|
public enum NilDecodingStrategy {
|
||||||
|
case empty
|
||||||
|
case custom((_ value: String) -> Bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The strategy to use in decoding bools. Defaults to `.default`.
|
||||||
|
open var boolDecodingStrategy: BoolDecodingStrategy = .default
|
||||||
|
|
||||||
|
/// The strategy to use in decoding dates. Defaults to `.deferredToDate`.
|
||||||
|
open var dateDecodingStrategy: DateDecodingStrategy = .deferredToDate
|
||||||
|
|
||||||
|
/// The strategy to use in decoding binary data. Defaults to `.base64`.
|
||||||
|
open var dataDecodingStrategy: DataDecodingStrategy = .base64
|
||||||
|
|
||||||
|
/// The strategy to use in decoding nil data. Defaults to `.empty`.
|
||||||
|
open var nilDecodingStrategy: NilDecodingStrategy = .empty
|
||||||
|
|
||||||
|
/// Contextual user-provided information for use during decoding.
|
||||||
|
open var userInfo: [CodingUserInfoKey: Any] = [:]
|
||||||
|
|
||||||
|
/// Options set on the top-level encoder to pass down the decoding hierarchy.
|
||||||
|
fileprivate struct _Options {
|
||||||
|
let boolDecodingStrategy: BoolDecodingStrategy
|
||||||
|
let dateDecodingStrategy: DateDecodingStrategy
|
||||||
|
let dataDecodingStrategy: DataDecodingStrategy
|
||||||
|
let nilDecodingStrategy: NilDecodingStrategy
|
||||||
|
let userInfo: [CodingUserInfoKey: Any]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The options set on the top-level decoder.
|
||||||
|
fileprivate var options: _Options {
|
||||||
|
return _Options(boolDecodingStrategy: boolDecodingStrategy,
|
||||||
|
dateDecodingStrategy: dateDecodingStrategy,
|
||||||
|
dataDecodingStrategy: dataDecodingStrategy,
|
||||||
|
nilDecodingStrategy: nilDecodingStrategy,
|
||||||
|
userInfo: userInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initializes `self` with default strategies.
|
||||||
|
public init() {}
|
||||||
|
|
||||||
|
/// Decodes a top-level value of the given type from the given CSV row representation.
|
||||||
|
open func decode<T: Decodable>(_ type: T.Type, from reader: CSVReader) throws -> T {
|
||||||
|
let decoder = _CSVRowDecoder(referencing: reader, options: self.options)
|
||||||
|
return try type.init(from: decoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate final class _CSVRowDecoder: Decoder {
|
||||||
|
|
||||||
|
fileprivate let reader: CSVReader
|
||||||
|
|
||||||
|
fileprivate let options: CSVRowDecoder._Options
|
||||||
|
|
||||||
|
public var codingPath: [CodingKey] = []
|
||||||
|
|
||||||
|
public var userInfo: [CodingUserInfoKey: Any] {
|
||||||
|
return self.options.userInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate init(referencing reader: CSVReader, options: CSVRowDecoder._Options) {
|
||||||
|
self.reader = reader
|
||||||
|
self.options = options
|
||||||
|
}
|
||||||
|
|
||||||
|
public func container<Key: CodingKey>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> {
|
||||||
|
let container = CSVKeyedDecodingContainer<Key>(referencing: self)
|
||||||
|
return KeyedDecodingContainer(container)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func unkeyedContainer() throws -> UnkeyedDecodingContainer {
|
||||||
|
throw DecodingError.valueNotFound(UnkeyedDecodingContainer.self,
|
||||||
|
DecodingError.Context(codingPath: self.codingPath,
|
||||||
|
debugDescription: "Cannot get unkeyed decoding container -- found null value instead."))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func singleValueContainer() throws -> SingleValueDecodingContainer {
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate final class CSVKeyedDecodingContainer<K: CodingKey>: KeyedDecodingContainerProtocol {
|
||||||
|
|
||||||
|
typealias Key = K
|
||||||
|
|
||||||
|
private let decoder: _CSVRowDecoder
|
||||||
|
|
||||||
|
public var codingPath: [CodingKey] {
|
||||||
|
return self.decoder.codingPath
|
||||||
|
}
|
||||||
|
|
||||||
|
public var allKeys: [Key] {
|
||||||
|
guard let headerRow = self.decoder.reader.headerRow else { return [] }
|
||||||
|
return headerRow.compactMap { Key(stringValue: $0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate init(referencing decoder: _CSVRowDecoder) {
|
||||||
|
self.decoder = decoder
|
||||||
|
}
|
||||||
|
|
||||||
|
private func value(for key: Key) throws -> String {
|
||||||
|
guard self.contains(key) else {
|
||||||
|
throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\")."))
|
||||||
|
}
|
||||||
|
|
||||||
|
if let index = key.intValue {
|
||||||
|
return self.decoder.reader.currentRow![index]
|
||||||
|
} else {
|
||||||
|
return self.decoder.reader[key.stringValue]!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func _valueNotFound(_ type: Any.Type) -> DecodingError {
|
||||||
|
let description = "Expected \(type) value but found null instead."
|
||||||
|
return .valueNotFound(type, DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: description))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func contains(_ key: Key) -> Bool {
|
||||||
|
guard let row = self.decoder.reader.currentRow else { return false }
|
||||||
|
|
||||||
|
if let index = key.intValue {
|
||||||
|
return index < row.count
|
||||||
|
} else {
|
||||||
|
guard let headerRow = self.decoder.reader.headerRow else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return headerRow.contains(key.stringValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decodeNil(forKey key: Key) throws -> Bool {
|
||||||
|
switch decoder.options.nilDecodingStrategy {
|
||||||
|
case .empty:
|
||||||
|
return try self.value(for: key).isEmpty
|
||||||
|
case .custom(let customClosure):
|
||||||
|
return customClosure(try self.value(for: key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool {
|
||||||
|
let value = try self.value(for: key)
|
||||||
|
|
||||||
|
self.decoder.codingPath.append(key)
|
||||||
|
defer { self.decoder.codingPath.removeLast() }
|
||||||
|
|
||||||
|
guard let result = try self.decoder.unbox(value, as: Bool.self) else {
|
||||||
|
throw _valueNotFound(type)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: String.Type, forKey key: Key) throws -> String {
|
||||||
|
let value = try self.value(for: key)
|
||||||
|
|
||||||
|
self.decoder.codingPath.append(key)
|
||||||
|
defer { self.decoder.codingPath.removeLast() }
|
||||||
|
|
||||||
|
guard let result = try self.decoder.unbox(value, as: String.self) else {
|
||||||
|
throw _valueNotFound(type)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: Double.Type, forKey key: Key) throws -> Double {
|
||||||
|
let value = try self.value(for: key)
|
||||||
|
|
||||||
|
self.decoder.codingPath.append(key)
|
||||||
|
defer { self.decoder.codingPath.removeLast() }
|
||||||
|
|
||||||
|
guard let result = try self.decoder.unbox(value, as: Double.self) else {
|
||||||
|
throw _valueNotFound(type)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: Float.Type, forKey key: Key) throws -> Float {
|
||||||
|
let value = try self.value(for: key)
|
||||||
|
|
||||||
|
self.decoder.codingPath.append(key)
|
||||||
|
defer { self.decoder.codingPath.removeLast() }
|
||||||
|
|
||||||
|
guard let result = try self.decoder.unbox(value, as: Float.self) else {
|
||||||
|
throw _valueNotFound(type)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: Int.Type, forKey key: Key) throws -> Int {
|
||||||
|
let value = try self.value(for: key)
|
||||||
|
|
||||||
|
self.decoder.codingPath.append(key)
|
||||||
|
defer { self.decoder.codingPath.removeLast() }
|
||||||
|
|
||||||
|
guard let result = try self.decoder.unbox(value, as: Int.self) else {
|
||||||
|
throw _valueNotFound(type)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 {
|
||||||
|
let value = try self.value(for: key)
|
||||||
|
|
||||||
|
self.decoder.codingPath.append(key)
|
||||||
|
defer { self.decoder.codingPath.removeLast() }
|
||||||
|
|
||||||
|
guard let result = try self.decoder.unbox(value, as: Int8.self) else {
|
||||||
|
throw _valueNotFound(type)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 {
|
||||||
|
let value = try self.value(for: key)
|
||||||
|
|
||||||
|
self.decoder.codingPath.append(key)
|
||||||
|
defer { self.decoder.codingPath.removeLast() }
|
||||||
|
|
||||||
|
guard let result = try self.decoder.unbox(value, as: Int16.self) else {
|
||||||
|
throw _valueNotFound(type)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 {
|
||||||
|
let value = try self.value(for: key)
|
||||||
|
|
||||||
|
self.decoder.codingPath.append(key)
|
||||||
|
defer { self.decoder.codingPath.removeLast() }
|
||||||
|
|
||||||
|
guard let result = try self.decoder.unbox(value, as: Int32.self) else {
|
||||||
|
throw _valueNotFound(type)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 {
|
||||||
|
let value = try self.value(for: key)
|
||||||
|
|
||||||
|
self.decoder.codingPath.append(key)
|
||||||
|
defer { self.decoder.codingPath.removeLast() }
|
||||||
|
|
||||||
|
guard let result = try self.decoder.unbox(value, as: Int64.self) else {
|
||||||
|
throw _valueNotFound(type)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt {
|
||||||
|
let value = try self.value(for: key)
|
||||||
|
|
||||||
|
self.decoder.codingPath.append(key)
|
||||||
|
defer { self.decoder.codingPath.removeLast() }
|
||||||
|
|
||||||
|
guard let result = try self.decoder.unbox(value, as: UInt.self) else {
|
||||||
|
throw _valueNotFound(type)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 {
|
||||||
|
let value = try self.value(for: key)
|
||||||
|
|
||||||
|
self.decoder.codingPath.append(key)
|
||||||
|
defer { self.decoder.codingPath.removeLast() }
|
||||||
|
|
||||||
|
guard let result = try self.decoder.unbox(value, as: UInt8.self) else {
|
||||||
|
throw _valueNotFound(type)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 {
|
||||||
|
let value = try self.value(for: key)
|
||||||
|
|
||||||
|
self.decoder.codingPath.append(key)
|
||||||
|
defer { self.decoder.codingPath.removeLast() }
|
||||||
|
|
||||||
|
guard let result = try self.decoder.unbox(value, as: UInt16.self) else {
|
||||||
|
throw _valueNotFound(type)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 {
|
||||||
|
let value = try self.value(for: key)
|
||||||
|
|
||||||
|
self.decoder.codingPath.append(key)
|
||||||
|
defer { self.decoder.codingPath.removeLast() }
|
||||||
|
|
||||||
|
guard let result = try self.decoder.unbox(value, as: UInt32.self) else {
|
||||||
|
throw _valueNotFound(type)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 {
|
||||||
|
let value = try self.value(for: key)
|
||||||
|
|
||||||
|
self.decoder.codingPath.append(key)
|
||||||
|
defer { self.decoder.codingPath.removeLast() }
|
||||||
|
|
||||||
|
guard let result = try self.decoder.unbox(value, as: UInt64.self) else {
|
||||||
|
throw _valueNotFound(type)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode<T: Decodable>(_ type: T.Type, forKey key: Key) throws -> T {
|
||||||
|
let value = try self.value(for: key)
|
||||||
|
|
||||||
|
self.decoder.codingPath.append(key)
|
||||||
|
defer { self.decoder.codingPath.removeLast() }
|
||||||
|
|
||||||
|
guard let result = try self.decoder.unbox(value, as: type) else {
|
||||||
|
throw _valueNotFound(type)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public func nestedContainer<NestedKey: CodingKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey> {
|
||||||
|
// Not supported
|
||||||
|
throw DecodingError.dataCorrupted(
|
||||||
|
DecodingError.Context(codingPath: self.codingPath,
|
||||||
|
debugDescription: "nestedContainer(...) CSV does not support nested values")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer {
|
||||||
|
// Not supported
|
||||||
|
throw DecodingError.dataCorrupted(
|
||||||
|
DecodingError.Context(codingPath: self.codingPath,
|
||||||
|
debugDescription: "nestedUnkeyedContainer(...) CSV does not support nested values")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func superDecoder() throws -> Decoder {
|
||||||
|
// Not supported
|
||||||
|
throw DecodingError.dataCorrupted(
|
||||||
|
DecodingError.Context(codingPath: self.codingPath,
|
||||||
|
debugDescription: "CSV does not support nested values")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func superDecoder(forKey key: Key) throws -> Decoder {
|
||||||
|
// Not supported
|
||||||
|
throw DecodingError.dataCorrupted(
|
||||||
|
DecodingError.Context(codingPath: self.codingPath,
|
||||||
|
debugDescription: "CSV does not support nested values")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension _CSVRowDecoder: SingleValueDecodingContainer {
|
||||||
|
|
||||||
|
private var value: String {
|
||||||
|
let key = self.codingPath.last!
|
||||||
|
if let index = key.intValue {
|
||||||
|
return self.reader.currentRow![index]
|
||||||
|
} else {
|
||||||
|
return self.reader[key.stringValue]!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func expectNonNull(_ type: Any.Type) throws {
|
||||||
|
guard !self.decodeNil() else {
|
||||||
|
let description = "Expected \(type) but found null value instead."
|
||||||
|
throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: description))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decodeNil() -> Bool {
|
||||||
|
switch options.nilDecodingStrategy {
|
||||||
|
case .empty:
|
||||||
|
return self.value.isEmpty
|
||||||
|
case .custom(let customClosure):
|
||||||
|
return customClosure(self.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: Bool.Type) throws -> Bool {
|
||||||
|
try self.expectNonNull(type)
|
||||||
|
return try self.unbox(self.value, as: Bool.self)!
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: Int.Type) throws -> Int {
|
||||||
|
try self.expectNonNull(type)
|
||||||
|
return try self.unbox(self.value, as: Int.self)!
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: Int8.Type) throws -> Int8 {
|
||||||
|
try self.expectNonNull(type)
|
||||||
|
return try self.unbox(self.value, as: Int8.self)!
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: Int16.Type) throws -> Int16 {
|
||||||
|
try self.expectNonNull(type)
|
||||||
|
return try self.unbox(self.value, as: Int16.self)!
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: Int32.Type) throws -> Int32 {
|
||||||
|
try self.expectNonNull(type)
|
||||||
|
return try self.unbox(self.value, as: Int32.self)!
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: Int64.Type) throws -> Int64 {
|
||||||
|
try self.expectNonNull(type)
|
||||||
|
return try self.unbox(self.value, as: Int64.self)!
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: UInt.Type) throws -> UInt {
|
||||||
|
try self.expectNonNull(type)
|
||||||
|
return try self.unbox(self.value, as: UInt.self)!
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: UInt8.Type) throws -> UInt8 {
|
||||||
|
try self.expectNonNull(type)
|
||||||
|
return try self.unbox(self.value, as: UInt8.self)!
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: UInt16.Type) throws -> UInt16 {
|
||||||
|
try self.expectNonNull(type)
|
||||||
|
return try self.unbox(self.value, as: UInt16.self)!
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: UInt32.Type) throws -> UInt32 {
|
||||||
|
try self.expectNonNull(type)
|
||||||
|
return try self.unbox(self.value, as: UInt32.self)!
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: UInt64.Type) throws -> UInt64 {
|
||||||
|
try self.expectNonNull(type)
|
||||||
|
return try self.unbox(self.value, as: UInt64.self)!
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: Float.Type) throws -> Float {
|
||||||
|
try self.expectNonNull(type)
|
||||||
|
return try self.unbox(self.value, as: Float.self)!
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: Double.Type) throws -> Double {
|
||||||
|
try self.expectNonNull(type)
|
||||||
|
return try self.unbox(self.value, as: Double.self)!
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode(_ type: String.Type) throws -> String {
|
||||||
|
try self.expectNonNull(type)
|
||||||
|
return try self.unbox(self.value, as: String.self)!
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decode<T: Decodable>(_ type: T.Type) throws -> T {
|
||||||
|
try self.expectNonNull(type)
|
||||||
|
return try self.unbox(self.value, as: type)!
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension _CSVRowDecoder {
|
||||||
|
|
||||||
|
private func _typeMismatch(at path: [CodingKey], expectation: Any.Type, reality: String) -> DecodingError {
|
||||||
|
let description = "Expected to decode \(expectation) but found \(reality) instead."
|
||||||
|
return .typeMismatch(expectation, DecodingError.Context(codingPath: path, debugDescription: description))
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func unbox(_ value: String, as type: Bool.Type) throws -> Bool? {
|
||||||
|
if value.isEmpty { return nil }
|
||||||
|
|
||||||
|
switch self.options.boolDecodingStrategy {
|
||||||
|
case .default:
|
||||||
|
guard let bool = Bool(value) else {
|
||||||
|
throw self._typeMismatch(at: self.codingPath, expectation: type, reality: value)
|
||||||
|
}
|
||||||
|
return bool
|
||||||
|
|
||||||
|
case .custom(let closure):
|
||||||
|
return try closure(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func unbox(_ value: String, as type: Int.Type) throws -> Int? {
|
||||||
|
if value.isEmpty { return nil }
|
||||||
|
|
||||||
|
guard let int = Int(value, radix: 10) else {
|
||||||
|
throw self._typeMismatch(at: self.codingPath, expectation: type, reality: value)
|
||||||
|
}
|
||||||
|
return int
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func unbox(_ value: String, as type: Int8.Type) throws -> Int8? {
|
||||||
|
if value.isEmpty { return nil }
|
||||||
|
|
||||||
|
guard let int8 = Int8(value, radix: 10) else {
|
||||||
|
throw self._typeMismatch(at: self.codingPath, expectation: type, reality: value)
|
||||||
|
}
|
||||||
|
return int8
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func unbox(_ value: String, as type: Int16.Type) throws -> Int16? {
|
||||||
|
if value.isEmpty { return nil }
|
||||||
|
|
||||||
|
guard let int16 = Int16(value, radix: 10) else {
|
||||||
|
throw self._typeMismatch(at: self.codingPath, expectation: type, reality: value)
|
||||||
|
}
|
||||||
|
return int16
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func unbox(_ value: String, as type: Int32.Type) throws -> Int32? {
|
||||||
|
if value.isEmpty { return nil }
|
||||||
|
|
||||||
|
guard let int32 = Int32(value, radix: 10) else {
|
||||||
|
throw self._typeMismatch(at: self.codingPath, expectation: type, reality: value)
|
||||||
|
}
|
||||||
|
return int32
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func unbox(_ value: String, as type: Int64.Type) throws -> Int64? {
|
||||||
|
if value.isEmpty { return nil }
|
||||||
|
|
||||||
|
guard let int64 = Int64(value, radix: 10) else {
|
||||||
|
throw self._typeMismatch(at: self.codingPath, expectation: type, reality: value)
|
||||||
|
}
|
||||||
|
return int64
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func unbox(_ value: String, as type: UInt.Type) throws -> UInt? {
|
||||||
|
if value.isEmpty { return nil }
|
||||||
|
|
||||||
|
guard let uint = UInt(value, radix: 10) else {
|
||||||
|
throw self._typeMismatch(at: self.codingPath, expectation: type, reality: value)
|
||||||
|
}
|
||||||
|
return uint
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func unbox(_ value: String, as type: UInt8.Type) throws -> UInt8? {
|
||||||
|
if value.isEmpty { return nil }
|
||||||
|
|
||||||
|
guard let uint8 = UInt8(value, radix: 10) else {
|
||||||
|
throw self._typeMismatch(at: self.codingPath, expectation: type, reality: value)
|
||||||
|
}
|
||||||
|
return uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func unbox(_ value: String, as type: UInt16.Type) throws -> UInt16? {
|
||||||
|
if value.isEmpty { return nil }
|
||||||
|
|
||||||
|
guard let uint16 = UInt16(value, radix: 10) else {
|
||||||
|
throw self._typeMismatch(at: self.codingPath, expectation: type, reality: value)
|
||||||
|
}
|
||||||
|
return uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func unbox(_ value: String, as type: UInt32.Type) throws -> UInt32? {
|
||||||
|
if value.isEmpty { return nil }
|
||||||
|
|
||||||
|
guard let uint32 = UInt32(value, radix: 10) else {
|
||||||
|
throw self._typeMismatch(at: self.codingPath, expectation: type, reality: value)
|
||||||
|
}
|
||||||
|
return uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func unbox(_ value: String, as type: UInt64.Type) throws -> UInt64? {
|
||||||
|
if value.isEmpty { return nil }
|
||||||
|
|
||||||
|
guard let uint64 = UInt64(value, radix: 10) else {
|
||||||
|
throw self._typeMismatch(at: self.codingPath, expectation: type, reality: value)
|
||||||
|
}
|
||||||
|
return uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func unbox(_ value: String, as type: Float.Type) throws -> Float? {
|
||||||
|
if value.isEmpty { return nil }
|
||||||
|
|
||||||
|
guard let float = Float(value) else {
|
||||||
|
throw self._typeMismatch(at: self.codingPath, expectation: type, reality: value)
|
||||||
|
}
|
||||||
|
return float
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func unbox(_ value: String, as type: Double.Type) throws -> Double? {
|
||||||
|
if value.isEmpty { return nil }
|
||||||
|
|
||||||
|
guard let double = Double(value) else {
|
||||||
|
throw self._typeMismatch(at: self.codingPath, expectation: type, reality: value)
|
||||||
|
}
|
||||||
|
return double
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func unbox(_ value: String, as type: String.Type) throws -> String? {
|
||||||
|
if value.isEmpty { return nil }
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
private func unbox(_ value: String, as type: Date.Type) throws -> Date? {
|
||||||
|
if value.isEmpty { return nil }
|
||||||
|
|
||||||
|
switch self.options.dateDecodingStrategy {
|
||||||
|
case .deferredToDate:
|
||||||
|
return try Date(from: self)
|
||||||
|
|
||||||
|
case .secondsSince1970:
|
||||||
|
let double = try self.unbox(value, as: Double.self)!
|
||||||
|
return Date(timeIntervalSince1970: double)
|
||||||
|
|
||||||
|
case .millisecondsSince1970:
|
||||||
|
let double = try self.unbox(value, as: Double.self)!
|
||||||
|
return Date(timeIntervalSince1970: double / 1000.0)
|
||||||
|
|
||||||
|
case .iso8601:
|
||||||
|
if #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) {
|
||||||
|
guard let date = _iso8601Formatter.date(from: value) else {
|
||||||
|
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Expected date string to be ISO8601-formatted."))
|
||||||
|
}
|
||||||
|
return date
|
||||||
|
} else {
|
||||||
|
fatalError("ISO8601DateFormatter is unavailable on this platform.")
|
||||||
|
}
|
||||||
|
|
||||||
|
case .formatted(let formatter):
|
||||||
|
guard let date = formatter.date(from: value) else {
|
||||||
|
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Date string does not match format expected by formatter."))
|
||||||
|
}
|
||||||
|
return date
|
||||||
|
|
||||||
|
case .custom(let closure):
|
||||||
|
return try closure(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func unbox(_ value: String, as type: Data.Type) throws -> Data? {
|
||||||
|
if value.isEmpty { return nil }
|
||||||
|
|
||||||
|
switch self.options.dataDecodingStrategy {
|
||||||
|
// TODO: Implement unkeyed decoding container.
|
||||||
|
// case .deferredToData:
|
||||||
|
// return try Data(from: self)
|
||||||
|
|
||||||
|
case .base64:
|
||||||
|
guard let data = Data(base64Encoded: value) else {
|
||||||
|
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Encountered Data is not valid Base64."))
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
|
||||||
|
case .custom(let closure):
|
||||||
|
return try closure(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func unbox<T: Decodable>(_ value: String, as type: T.Type) throws -> T? {
|
||||||
|
if value.isEmpty { return nil }
|
||||||
|
|
||||||
|
if type == Date.self {
|
||||||
|
guard let date = try self.unbox(value, as: Date.self) else { return nil }
|
||||||
|
return (date as! T)
|
||||||
|
} else if type == Data.self {
|
||||||
|
guard let data = try self.unbox(value, as: Data.self) else { return nil }
|
||||||
|
return (data as! T)
|
||||||
|
} else if type == URL.self {
|
||||||
|
guard let url = URL(string: value) else {
|
||||||
|
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath,
|
||||||
|
debugDescription: "Invalid URL string."))
|
||||||
|
}
|
||||||
|
return (url as! T)
|
||||||
|
} else if type == Decimal.self {
|
||||||
|
guard let decimal = Decimal(string: value) else {
|
||||||
|
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath,
|
||||||
|
debugDescription: "Invalid Decimal string."))
|
||||||
|
}
|
||||||
|
return (decimal as! T)
|
||||||
|
} else {
|
||||||
|
return try type.init(from: self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Shared ISO8601 Date Formatter
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// NOTE: This value is implicitly lazy and _must_ be lazy.
|
||||||
|
// We're compiled against the latest SDK (w/ ISO8601DateFormatter), but linked against whichever Foundation the user has.
|
||||||
|
// ISO8601DateFormatter might not exist, so we better not hit this code path on an older OS.
|
||||||
|
@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)
|
||||||
|
fileprivate var _iso8601Formatter: ISO8601DateFormatter = {
|
||||||
|
let formatter = ISO8601DateFormatter()
|
||||||
|
formatter.formatOptions = .withInternetDateTime
|
||||||
|
return formatter
|
||||||
|
}()
|
|
@ -0,0 +1,211 @@
|
||||||
|
//
|
||||||
|
// CSVWriter.swift
|
||||||
|
// CSV
|
||||||
|
//
|
||||||
|
// Created by Yasuhiro Hatta on 2017/05/28.
|
||||||
|
// Copyright © 2017 yaslab. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public class CSVWriter {
|
||||||
|
|
||||||
|
public struct Configuration {
|
||||||
|
|
||||||
|
public var delimiter: String
|
||||||
|
public var newline: String
|
||||||
|
|
||||||
|
internal init(delimiter: String, newline: Newline) {
|
||||||
|
self.delimiter = delimiter
|
||||||
|
|
||||||
|
switch newline {
|
||||||
|
case .lf: self.newline = String(LF)
|
||||||
|
case .crlf: self.newline = String(CR) + String(LF)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Newline {
|
||||||
|
|
||||||
|
/// "\n"
|
||||||
|
case lf
|
||||||
|
/// "\r\n"
|
||||||
|
case crlf
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public let stream: OutputStream
|
||||||
|
public let configuration: Configuration
|
||||||
|
fileprivate let writeScalar: ((UnicodeScalar) throws -> Void)
|
||||||
|
|
||||||
|
fileprivate var isFirstRow: Bool = true
|
||||||
|
fileprivate var isFirstField: Bool = true
|
||||||
|
|
||||||
|
fileprivate init(
|
||||||
|
stream: OutputStream,
|
||||||
|
configuration: Configuration,
|
||||||
|
writeScalar: @escaping ((UnicodeScalar) throws -> Void)) throws {
|
||||||
|
|
||||||
|
self.stream = stream
|
||||||
|
self.configuration = configuration
|
||||||
|
self.writeScalar = writeScalar
|
||||||
|
|
||||||
|
if stream.streamStatus == .notOpen {
|
||||||
|
stream.open()
|
||||||
|
}
|
||||||
|
if stream.streamStatus != .open {
|
||||||
|
throw CSVError.cannotOpenFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
if stream.streamStatus == .open {
|
||||||
|
stream.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CSVWriter {
|
||||||
|
|
||||||
|
public static let defaultDelimiter: UnicodeScalar = ","
|
||||||
|
|
||||||
|
public convenience init(
|
||||||
|
stream: OutputStream,
|
||||||
|
delimiter: String = String(defaultDelimiter),
|
||||||
|
newline: Newline = .lf
|
||||||
|
) throws {
|
||||||
|
|
||||||
|
try self.init(stream: stream, codecType: UTF8.self, delimiter: delimiter, newline: newline)
|
||||||
|
}
|
||||||
|
|
||||||
|
public convenience init<T: UnicodeCodec>(
|
||||||
|
stream: OutputStream,
|
||||||
|
codecType: T.Type,
|
||||||
|
delimiter: String = String(defaultDelimiter),
|
||||||
|
newline: Newline = .lf
|
||||||
|
) throws where T.CodeUnit == UInt8 {
|
||||||
|
|
||||||
|
let config = Configuration(delimiter: delimiter, newline: newline)
|
||||||
|
try self.init(stream: stream, configuration: config) { (scalar: UnicodeScalar) throws in
|
||||||
|
var error: CSVError?
|
||||||
|
codecType.encode(scalar) { (code: UInt8) in
|
||||||
|
var code = code
|
||||||
|
let count = stream.write(&code, maxLength: 1)
|
||||||
|
if count != 1 {
|
||||||
|
error = CSVError.cannotWriteStream
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let error = error {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public convenience init<T: UnicodeCodec>(
|
||||||
|
stream: OutputStream,
|
||||||
|
codecType: T.Type,
|
||||||
|
endian: Endian = .big,
|
||||||
|
delimiter: String = String(defaultDelimiter),
|
||||||
|
newline: Newline = .lf
|
||||||
|
) throws where T.CodeUnit == UInt16 {
|
||||||
|
|
||||||
|
let config = Configuration(delimiter: delimiter, newline: newline)
|
||||||
|
try self.init(stream: stream, configuration: config) { (scalar: UnicodeScalar) throws in
|
||||||
|
var error: CSVError?
|
||||||
|
codecType.encode(scalar) { (code: UInt16) in
|
||||||
|
var code = (endian == .big) ? code.bigEndian : code.littleEndian
|
||||||
|
withUnsafeBytes(of: &code) { (buffer) -> Void in
|
||||||
|
let count = stream.write(buffer.baseAddress!.assumingMemoryBound(to: UInt8.self),
|
||||||
|
maxLength: buffer.count)
|
||||||
|
if count != buffer.count {
|
||||||
|
error = CSVError.cannotWriteStream
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let error = error {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public convenience init<T: UnicodeCodec>(
|
||||||
|
stream: OutputStream,
|
||||||
|
codecType: T.Type,
|
||||||
|
endian: Endian = .big,
|
||||||
|
delimiter: String = String(defaultDelimiter),
|
||||||
|
newline: Newline = .lf
|
||||||
|
) throws where T.CodeUnit == UInt32 {
|
||||||
|
|
||||||
|
let config = Configuration(delimiter: delimiter, newline: newline)
|
||||||
|
try self.init(stream: stream, configuration: config) { (scalar: UnicodeScalar) throws in
|
||||||
|
var error: CSVError?
|
||||||
|
codecType.encode(scalar) { (code: UInt32) in
|
||||||
|
var code = (endian == .big) ? code.bigEndian : code.littleEndian
|
||||||
|
withUnsafeBytes(of: &code) { (buffer) -> Void in
|
||||||
|
let count = stream.write(buffer.baseAddress!.assumingMemoryBound(to: UInt8.self),
|
||||||
|
maxLength: buffer.count)
|
||||||
|
if count != buffer.count {
|
||||||
|
error = CSVError.cannotWriteStream
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let error = error {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CSVWriter {
|
||||||
|
|
||||||
|
public func beginNewRow() {
|
||||||
|
isFirstField = true
|
||||||
|
}
|
||||||
|
|
||||||
|
public func write(field value: String, quoted: Bool = false) throws {
|
||||||
|
if isFirstRow {
|
||||||
|
isFirstRow = false
|
||||||
|
} else {
|
||||||
|
if isFirstField {
|
||||||
|
try configuration.newline.unicodeScalars.forEach(writeScalar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if isFirstField {
|
||||||
|
isFirstField = false
|
||||||
|
} else {
|
||||||
|
try configuration.delimiter.unicodeScalars.forEach(writeScalar)
|
||||||
|
}
|
||||||
|
|
||||||
|
var value = value
|
||||||
|
|
||||||
|
var quoted = quoted
|
||||||
|
if !quoted {
|
||||||
|
if value.contains("\"") || value.contains(configuration.delimiter) || value.contains("\r") || value.contains("\n") {
|
||||||
|
quoted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if quoted {
|
||||||
|
value = value.replacingOccurrences(of: DQUOTE_STR, with: DQUOTE2_STR)
|
||||||
|
try writeScalar(DQUOTE)
|
||||||
|
}
|
||||||
|
|
||||||
|
try value.unicodeScalars.forEach(writeScalar)
|
||||||
|
|
||||||
|
if quoted {
|
||||||
|
try writeScalar(DQUOTE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func write(row values: [String], quotedAtIndex: ((Int) -> Bool) = { _ in false }) throws {
|
||||||
|
beginNewRow()
|
||||||
|
for (i, value) in values.enumerated() {
|
||||||
|
try write(field: value, quoted: quotedAtIndex(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
//
|
||||||
|
// Endian.swift
|
||||||
|
// CSV
|
||||||
|
//
|
||||||
|
// Created by Yasuhiro Hatta on 2016/06/21.
|
||||||
|
// Copyright © 2016 yaslab. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
/// Represents byte order.
|
||||||
|
public enum Endian {
|
||||||
|
|
||||||
|
/// Big endian.
|
||||||
|
case big
|
||||||
|
/// Little endian.
|
||||||
|
case little
|
||||||
|
/// Multibyte character sets.
|
||||||
|
case unknown
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
//
|
||||||
|
// Legacy.swift
|
||||||
|
// CSV
|
||||||
|
//
|
||||||
|
// Created by Yasuhiro Hatta on 2016/06/11.
|
||||||
|
// Copyright © 2016 yaslab. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
@available(*, deprecated, renamed: "CSVReader")
|
||||||
|
public typealias CSV = CSVReader
|
||||||
|
|
||||||
|
extension CSVReader: Sequence { }
|
|
@ -0,0 +1,35 @@
|
||||||
|
//
|
||||||
|
// UnicodeIterator.swift
|
||||||
|
// CSV
|
||||||
|
//
|
||||||
|
// Created by Yasuhiro Hatta on 2016/06/20.
|
||||||
|
// Copyright © 2016 yaslab. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
internal class UnicodeIterator<
|
||||||
|
Input: IteratorProtocol,
|
||||||
|
InputEncoding: UnicodeCodec>: IteratorProtocol
|
||||||
|
where InputEncoding.CodeUnit == Input.Element {
|
||||||
|
|
||||||
|
private var input: Input
|
||||||
|
private var inputEncoding: InputEncoding
|
||||||
|
internal var errorHandler: ((Error) -> Void)?
|
||||||
|
|
||||||
|
internal init(input: Input, inputEncodingType: InputEncoding.Type) {
|
||||||
|
self.input = input
|
||||||
|
self.inputEncoding = inputEncodingType.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func next() -> UnicodeScalar? {
|
||||||
|
switch inputEncoding.decode(&input) {
|
||||||
|
case .scalarValue(let c):
|
||||||
|
return c
|
||||||
|
case .emptyInput:
|
||||||
|
return nil
|
||||||
|
case .error:
|
||||||
|
errorHandler?(CSVError.unicodeDecoding)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,17 +0,0 @@
|
||||||
//
|
|
||||||
// CSVError.swift
|
|
||||||
// CSV
|
|
||||||
//
|
|
||||||
// Created by Yasuhiro Hatta on 2016/06/11.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
public enum CSVError: ErrorType {
|
|
||||||
case ParameterError
|
|
||||||
case StreamError
|
|
||||||
case HeaderReadError
|
|
||||||
case MemoryAllocationFailed
|
|
||||||
case StringEncodingMismatch
|
|
||||||
}
|
|
|
@ -3,7 +3,7 @@
|
||||||
// CSV
|
// CSV
|
||||||
//
|
//
|
||||||
// Created by Yasuhiro Hatta on 2016/06/11.
|
// Created by Yasuhiro Hatta on 2016/06/11.
|
||||||
//
|
// Copyright © 2016 yaslab. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
@import Foundation;
|
@import Foundation;
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>FMWK</string>
|
<string>FMWK</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>0.1.0</string>
|
<string>$(MARKETING_VERSION)</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
|
|
|
@ -1,229 +0,0 @@
|
||||||
//
|
|
||||||
// CSVReaderTests.swift
|
|
||||||
// CSV
|
|
||||||
//
|
|
||||||
// Created by Yasuhiro Hatta on 2016/06/11.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
import XCTest
|
|
||||||
@testable import CSV
|
|
||||||
|
|
||||||
class CSVReaderTests: XCTestCase {
|
|
||||||
|
|
||||||
func testExample1() {
|
|
||||||
let csv = "abab,cdcd,efef"
|
|
||||||
let encoding = NSUTF8StringEncoding
|
|
||||||
let records = parseCSV(csv, encoding: encoding)
|
|
||||||
XCTAssertEqual(records[0], ["abab", "cdcd", "efef"])
|
|
||||||
}
|
|
||||||
|
|
||||||
func testExample2() {
|
|
||||||
let csv = "abab,\"cdcd\",efef"
|
|
||||||
let encoding = NSUTF8StringEncoding
|
|
||||||
let records = parseCSV(csv, encoding: encoding)
|
|
||||||
XCTAssertEqual(records[0], ["abab", "cdcd", "efef"])
|
|
||||||
}
|
|
||||||
|
|
||||||
func testExample3() {
|
|
||||||
let csv = "abab,cdcd,efef\nzxcv,asdf,qwer"
|
|
||||||
let encoding = NSUTF8StringEncoding
|
|
||||||
let records = parseCSV(csv, encoding: encoding)
|
|
||||||
XCTAssertEqual(records[0], ["abab", "cdcd", "efef"])
|
|
||||||
XCTAssertEqual(records[1], ["zxcv", "asdf", "qwer"])
|
|
||||||
}
|
|
||||||
|
|
||||||
func testExample4() {
|
|
||||||
let csv = "abab,\"cd,cd\",efef"
|
|
||||||
let encoding = NSUTF8StringEncoding
|
|
||||||
let records = parseCSV(csv, encoding: encoding)
|
|
||||||
XCTAssertEqual(records[0], ["abab", "cd,cd", "efef"])
|
|
||||||
}
|
|
||||||
|
|
||||||
func testExample5() {
|
|
||||||
let csv = "abab,cdcd,efef\r\nzxcv,asdf,qwer"
|
|
||||||
let encoding = NSUTF8StringEncoding
|
|
||||||
let records = parseCSV(csv, encoding: encoding)
|
|
||||||
XCTAssertEqual(records[0], ["abab", "cdcd", "efef"])
|
|
||||||
XCTAssertEqual(records[1], ["zxcv", "asdf", "qwer"])
|
|
||||||
}
|
|
||||||
|
|
||||||
func testExample6() {
|
|
||||||
let csv = "abab,\"\"\"cdcd\",efef\r\nzxcv,asdf,qwer"
|
|
||||||
let encoding = NSUTF8StringEncoding
|
|
||||||
let records = parseCSV(csv, encoding: encoding)
|
|
||||||
XCTAssertEqual(records[0], ["abab", "\"cdcd", "efef"])
|
|
||||||
XCTAssertEqual(records[1], ["zxcv", "asdf", "qwer"])
|
|
||||||
}
|
|
||||||
|
|
||||||
func testExample7() {
|
|
||||||
let csv = "abab,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\""
|
|
||||||
let encoding = NSUTF8StringEncoding
|
|
||||||
let records = parseCSV(csv, encoding: encoding)
|
|
||||||
XCTAssertEqual(records[0], ["abab", "cdcd", "efef"])
|
|
||||||
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er"])
|
|
||||||
}
|
|
||||||
|
|
||||||
func testExample8() {
|
|
||||||
let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\","
|
|
||||||
let encoding = NSUTF8StringEncoding
|
|
||||||
let records = parseCSV(csv, encoding: encoding)
|
|
||||||
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
|
|
||||||
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
|
|
||||||
}
|
|
||||||
|
|
||||||
func testExample9() {
|
|
||||||
let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\",\r"
|
|
||||||
let encoding = NSUTF8StringEncoding
|
|
||||||
let records = parseCSV(csv, encoding: encoding)
|
|
||||||
XCTAssertEqual(records.count, 2)
|
|
||||||
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
|
|
||||||
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
|
|
||||||
}
|
|
||||||
|
|
||||||
func testExample10() {
|
|
||||||
let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\",\r\n"
|
|
||||||
let encoding = NSUTF8StringEncoding
|
|
||||||
let records = parseCSV(csv, encoding: encoding)
|
|
||||||
XCTAssertEqual(records.count, 2)
|
|
||||||
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
|
|
||||||
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
|
|
||||||
}
|
|
||||||
|
|
||||||
func testExample11() {
|
|
||||||
let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\",\n"
|
|
||||||
let encoding = NSUTF8StringEncoding
|
|
||||||
let records = parseCSV(csv, encoding: encoding)
|
|
||||||
XCTAssertEqual(records.count, 2)
|
|
||||||
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
|
|
||||||
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
|
|
||||||
}
|
|
||||||
|
|
||||||
func testExample12() {
|
|
||||||
let csv = "abab,,\"\rcdcd\n\",efef\r\nzxcv,asdf,\"qw\"\"er\",\n"
|
|
||||||
let encoding = NSUTF8StringEncoding
|
|
||||||
let records = parseCSV(csv, encoding: encoding)
|
|
||||||
XCTAssertEqual(records.count, 2)
|
|
||||||
XCTAssertEqual(records[0], ["abab", "", "\rcdcd\n", "efef"])
|
|
||||||
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
|
|
||||||
}
|
|
||||||
|
|
||||||
func testEncodingWithoutBOM() {
|
|
||||||
var index = 0
|
|
||||||
let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\","
|
|
||||||
for encoding in allEncodings() {
|
|
||||||
print("index: \(index)")
|
|
||||||
let records = parseCSV(csv, encoding: encoding)
|
|
||||||
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
|
|
||||||
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
|
|
||||||
index += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testUTF8WithBOM() {
|
|
||||||
let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\","
|
|
||||||
let encoding = NSUTF8StringEncoding
|
|
||||||
let mutableData = NSMutableData()
|
|
||||||
mutableData.appendBytes(utf8BOM, length: utf8BOM.count)
|
|
||||||
mutableData.appendData(csv.dataUsingEncoding(encoding)!)
|
|
||||||
let records = parseData(mutableData, encoding: encoding)
|
|
||||||
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
|
|
||||||
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
|
|
||||||
}
|
|
||||||
|
|
||||||
func testUTF16WithNativeEndianBOM() {
|
|
||||||
let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\","
|
|
||||||
let encoding = NSUTF16StringEncoding
|
|
||||||
let records = parseCSV(csv, encoding: encoding)
|
|
||||||
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
|
|
||||||
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
|
|
||||||
}
|
|
||||||
|
|
||||||
func testUTF16WithBigEndianBOM() {
|
|
||||||
let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\","
|
|
||||||
let encoding = NSUTF16StringEncoding
|
|
||||||
let mutableData = NSMutableData()
|
|
||||||
mutableData.appendBytes(utf16BigEndianBOM, length: utf16BigEndianBOM.count)
|
|
||||||
mutableData.appendData(csv.dataUsingEncoding(NSUTF16BigEndianStringEncoding)!)
|
|
||||||
let records = parseData(mutableData, encoding: encoding)
|
|
||||||
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
|
|
||||||
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
|
|
||||||
}
|
|
||||||
|
|
||||||
func testUTF16WithLittleEndianBOM() {
|
|
||||||
let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\","
|
|
||||||
let encoding = NSUTF16StringEncoding
|
|
||||||
let mutableData = NSMutableData()
|
|
||||||
mutableData.appendBytes(utf16LittleEndianBOM, length: utf16LittleEndianBOM.count)
|
|
||||||
mutableData.appendData(csv.dataUsingEncoding(NSUTF16LittleEndianStringEncoding)!)
|
|
||||||
let records = parseData(mutableData, encoding: encoding)
|
|
||||||
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
|
|
||||||
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
|
|
||||||
}
|
|
||||||
|
|
||||||
func testUTF32WithNativeEndianBOM() {
|
|
||||||
let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\","
|
|
||||||
let encoding = NSUTF32StringEncoding
|
|
||||||
let records = parseCSV(csv, encoding: encoding)
|
|
||||||
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
|
|
||||||
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
|
|
||||||
}
|
|
||||||
|
|
||||||
func testUTF32WithBigEndianBOM() {
|
|
||||||
let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\","
|
|
||||||
let encoding = NSUTF32StringEncoding
|
|
||||||
let mutableData = NSMutableData()
|
|
||||||
mutableData.appendBytes(utf32BigEndianBOM, length: utf32BigEndianBOM.count)
|
|
||||||
mutableData.appendData(csv.dataUsingEncoding(NSUTF32BigEndianStringEncoding)!)
|
|
||||||
let records = parseData(mutableData, encoding: encoding)
|
|
||||||
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
|
|
||||||
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
|
|
||||||
}
|
|
||||||
|
|
||||||
func testUTF32WithLittleEndianBOM() {
|
|
||||||
let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\","
|
|
||||||
let encoding = NSUTF32StringEncoding
|
|
||||||
let mutableData = NSMutableData()
|
|
||||||
mutableData.appendBytes(utf32LittleEndianBOM, length: utf32LittleEndianBOM.count)
|
|
||||||
mutableData.appendData(csv.dataUsingEncoding(NSUTF32LittleEndianStringEncoding)!)
|
|
||||||
let records = parseData(mutableData, encoding: encoding)
|
|
||||||
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
|
|
||||||
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
|
|
||||||
}
|
|
||||||
|
|
||||||
func allEncodings() -> [NSStringEncoding] {
|
|
||||||
return [
|
|
||||||
// multi-byte character encodings
|
|
||||||
NSShiftJISStringEncoding,
|
|
||||||
NSJapaneseEUCStringEncoding,
|
|
||||||
NSUTF8StringEncoding,
|
|
||||||
// wide character encodings
|
|
||||||
NSUTF16BigEndianStringEncoding,
|
|
||||||
NSUTF16LittleEndianStringEncoding,
|
|
||||||
NSUTF32BigEndianStringEncoding,
|
|
||||||
NSUTF32LittleEndianStringEncoding,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseCSV(csv: String, encoding: NSStringEncoding) -> [[String]] {
|
|
||||||
let data = csv.dataUsingEncoding(encoding)!
|
|
||||||
return parseData(data, encoding: encoding)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseData(data: NSData, encoding: NSStringEncoding) -> [[String]] {
|
|
||||||
let stream = NSInputStream(data: data)
|
|
||||||
let reader = try! CSV(stream: stream, encoding: encoding)
|
|
||||||
var records = [[String]]()
|
|
||||||
for row in reader {
|
|
||||||
records.append(row)
|
|
||||||
}
|
|
||||||
return records
|
|
||||||
}
|
|
||||||
|
|
||||||
static var allTests : [(String, (CSVReaderTests) -> () throws -> Void)] {
|
|
||||||
return [
|
|
||||||
("testExample1", testExample1),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
//
|
|
||||||
// CSVTests.swift
|
|
||||||
// CSV
|
|
||||||
//
|
|
||||||
// Created by Yasuhiro Hatta on 2016/06/11.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
import XCTest
|
|
||||||
@testable import CSV
|
|
||||||
|
|
||||||
class CSVTests: XCTestCase {
|
|
||||||
|
|
||||||
func testSample1() {
|
|
||||||
let csv = "\"abc\",1,2\n\"cde\",3,4"
|
|
||||||
var i = 0
|
|
||||||
for row in try! CSV(string: csv) {
|
|
||||||
switch i {
|
|
||||||
case 0: XCTAssertEqual(row, ["abc", "1", "2"])
|
|
||||||
case 1: XCTAssertEqual(row, ["cde", "3", "4"])
|
|
||||||
default: break
|
|
||||||
}
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
XCTAssertEqual(i, 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testSample2() {
|
|
||||||
let csv = "\"abc\",1,2\n\"cde\",3,4\n"
|
|
||||||
var i = 0
|
|
||||||
for row in try! CSV(string: csv) {
|
|
||||||
switch i {
|
|
||||||
case 0: XCTAssertEqual(row, ["abc", "1", "2"])
|
|
||||||
case 1: XCTAssertEqual(row, ["cde", "3", "4"])
|
|
||||||
default: break
|
|
||||||
}
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
XCTAssertEqual(i, 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testSample3() {
|
|
||||||
let csv = "\"abc\",1,2\n\"cde\",3,4\n "
|
|
||||||
var i = 0
|
|
||||||
for row in try! CSV(string: csv) {
|
|
||||||
switch i {
|
|
||||||
case 0: XCTAssertEqual(row, ["abc", "1", "2"])
|
|
||||||
case 1: XCTAssertEqual(row, ["cde", "3", "4"])
|
|
||||||
case 2: XCTAssertEqual(row, [" "])
|
|
||||||
default: break
|
|
||||||
}
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
XCTAssertEqual(i, 3)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testSample4() {
|
|
||||||
let csv = "\"abc\",1,2\n\n\"cde\",3,4"
|
|
||||||
var i = 0
|
|
||||||
for row in try! CSV(string: csv) {
|
|
||||||
switch i {
|
|
||||||
case 0: XCTAssertEqual(row, ["abc", "1", "2"])
|
|
||||||
case 1: XCTAssertEqual(row, [""])
|
|
||||||
case 2: XCTAssertEqual(row, ["cde", "3", "4"])
|
|
||||||
default: break
|
|
||||||
}
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
XCTAssertEqual(i, 3)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
//
|
||||||
|
// BinaryReaderTests.swift
|
||||||
|
// CSV
|
||||||
|
//
|
||||||
|
// Created by Yasuhiro Hatta on 2018/11/15.
|
||||||
|
// Copyright © 2018 yaslab. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
@testable import CSV
|
||||||
|
|
||||||
|
class BinaryReaderTests: XCTestCase {
|
||||||
|
|
||||||
|
private func random(_ count: Int) -> [UInt8] {
|
||||||
|
var array = [UInt8]()
|
||||||
|
for _ in 0 ..< count {
|
||||||
|
array.append(UInt8.random(in: .min ... .max))
|
||||||
|
}
|
||||||
|
return array
|
||||||
|
}
|
||||||
|
|
||||||
|
func testReadUInt8WithSmallBuffer() {
|
||||||
|
let bytes = [0xcc] + random(99)
|
||||||
|
let stream = InputStream(data: Data(bytes))
|
||||||
|
|
||||||
|
do {
|
||||||
|
let reader = try BinaryReader(stream: stream, endian: .unknown, closeOnDeinit: true, bufferSize: 7)
|
||||||
|
for expected in bytes {
|
||||||
|
let actual = try reader.readUInt8()
|
||||||
|
XCTAssertEqual(actual, expected)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
XCTFail("\(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testReadUInt16BEWithSmallBuffer() {
|
||||||
|
let bytes = [0xcc] + random(99)
|
||||||
|
let stream = InputStream(data: Data(bytes))
|
||||||
|
|
||||||
|
do {
|
||||||
|
let reader = try BinaryReader(stream: stream, endian: .big, closeOnDeinit: true, bufferSize: 7)
|
||||||
|
for i in stride(from: 0, to: bytes.count, by: 2) {
|
||||||
|
let expected = (UInt16(bytes[i]) << 8) | UInt16(bytes[i + 1])
|
||||||
|
let actual = try reader.readUInt16()
|
||||||
|
XCTAssertEqual(actual, expected)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
XCTFail("\(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testReadUInt16LEWithSmallBuffer() {
|
||||||
|
let bytes = [0xcc] + random(99)
|
||||||
|
let stream = InputStream(data: Data(bytes))
|
||||||
|
|
||||||
|
do {
|
||||||
|
let reader = try BinaryReader(stream: stream, endian: .little, closeOnDeinit: true, bufferSize: 7)
|
||||||
|
for i in stride(from: 0, to: bytes.count, by: 2) {
|
||||||
|
let expected = UInt16(bytes[i]) | (UInt16(bytes[i + 1]) << 8)
|
||||||
|
let actual = try reader.readUInt16()
|
||||||
|
XCTAssertEqual(actual, expected)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
XCTFail("\(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testReadUInt32BEWithSmallBuffer() {
|
||||||
|
let bytes = [0xcc] + random(99)
|
||||||
|
let stream = InputStream(data: Data(bytes))
|
||||||
|
|
||||||
|
do {
|
||||||
|
let reader = try BinaryReader(stream: stream, endian: .big, closeOnDeinit: true, bufferSize: 7)
|
||||||
|
for i in stride(from: 0, to: bytes.count, by: 4) {
|
||||||
|
let expected =
|
||||||
|
(UInt32(bytes[i ]) << 24) | (UInt32(bytes[i + 1]) << 16) |
|
||||||
|
(UInt32(bytes[i + 2]) << 8) | (UInt32(bytes[i + 3]) )
|
||||||
|
let actual = try reader.readUInt32()
|
||||||
|
XCTAssertEqual(actual, expected)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
XCTFail("\(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testReadUInt32LEWithSmallBuffer() {
|
||||||
|
let bytes = [0xcc] + random(99)
|
||||||
|
let stream = InputStream(data: Data(bytes))
|
||||||
|
|
||||||
|
do {
|
||||||
|
let reader = try BinaryReader(stream: stream, endian: .little, closeOnDeinit: true, bufferSize: 7)
|
||||||
|
for i in stride(from: 0, to: bytes.count, by: 4) {
|
||||||
|
let expected =
|
||||||
|
(UInt32(bytes[i ]) ) | (UInt32(bytes[i + 1]) << 8) |
|
||||||
|
(UInt32(bytes[i + 2]) << 16) | (UInt32(bytes[i + 3]) << 24)
|
||||||
|
let actual = try reader.readUInt32()
|
||||||
|
XCTAssertEqual(actual, expected)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
XCTFail("\(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,242 @@
|
||||||
|
//
|
||||||
|
// CSVReaderTests.swift
|
||||||
|
// CSV
|
||||||
|
//
|
||||||
|
// Created by Yasuhiro Hatta on 2016/06/11.
|
||||||
|
// Copyright © 2016 yaslab. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
@testable import CSV
|
||||||
|
|
||||||
|
class CSVReaderTests: XCTestCase {
|
||||||
|
|
||||||
|
func testOneLine() {
|
||||||
|
let csv = "\"abc\",1,2"
|
||||||
|
var i = 0
|
||||||
|
|
||||||
|
for record in AnyIterator(try! CSVReader(string: csv)) {
|
||||||
|
switch i {
|
||||||
|
case 0: XCTAssertEqual(record, ["abc", "1", "2"])
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
XCTAssertEqual(i, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTwoLines() {
|
||||||
|
let csv = "\"abc\",1,2\n\"cde\",3,4"
|
||||||
|
var i = 0
|
||||||
|
for record in AnyIterator(try! CSVReader(string: csv)) {
|
||||||
|
switch i {
|
||||||
|
case 0: XCTAssertEqual(record, ["abc", "1", "2"])
|
||||||
|
case 1: XCTAssertEqual(record, ["cde", "3", "4"])
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
XCTAssertEqual(i, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLastLineIsEmpty() {
|
||||||
|
let csv = "\"abc\",1,2\n\"cde\",3,4\n"
|
||||||
|
var i = 0
|
||||||
|
for record in AnyIterator(try! CSVReader(string: csv)) {
|
||||||
|
switch i {
|
||||||
|
case 0: XCTAssertEqual(record, ["abc", "1", "2"])
|
||||||
|
case 1: XCTAssertEqual(record, ["cde", "3", "4"])
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
XCTAssertEqual(i, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLastLineIsWhiteSpace() {
|
||||||
|
let csv = "\"abc\",1,2\n\"cde\",3,4\n "
|
||||||
|
var i = 0
|
||||||
|
for record in AnyIterator(try! CSVReader(string: csv)) {
|
||||||
|
switch i {
|
||||||
|
case 0: XCTAssertEqual(record, ["abc", "1", "2"])
|
||||||
|
case 1: XCTAssertEqual(record, ["cde", "3", "4"])
|
||||||
|
case 2: XCTAssertEqual(record, [" "])
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
XCTAssertEqual(i, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMiddleLineIsEmpty() {
|
||||||
|
let csv = "\"abc\",1,2\n\n\"cde\",3,4"
|
||||||
|
var i = 0
|
||||||
|
for record in AnyIterator(try! CSVReader(string: csv)) {
|
||||||
|
switch i {
|
||||||
|
case 0: XCTAssertEqual(record, ["abc", "1", "2"])
|
||||||
|
case 1: XCTAssertEqual(record, [""])
|
||||||
|
case 2: XCTAssertEqual(record, ["cde", "3", "4"])
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
XCTAssertEqual(i, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCommaInQuotationMarks() {
|
||||||
|
let csvString = "abab,\"cd,cd\",efef"
|
||||||
|
let csv = try! CSVReader(string: csvString)
|
||||||
|
let record = csv.next()!
|
||||||
|
XCTAssertEqual(record, ["abab", "cd,cd", "efef"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testEscapedQuotationMark1() {
|
||||||
|
let csvString = "abab,\"\"\"cdcd\",efef\r\nzxcv,asdf,qwer"
|
||||||
|
let csv = try! CSVReader(string: csvString)
|
||||||
|
var record = csv.next()!
|
||||||
|
XCTAssertEqual(record, ["abab", "\"cdcd", "efef"])
|
||||||
|
record = csv.next()!
|
||||||
|
XCTAssertEqual(record, ["zxcv", "asdf", "qwer"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testEscapedQuotationMark2() {
|
||||||
|
let csvString = "abab,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\""
|
||||||
|
let csv = try! CSVReader(string: csvString)
|
||||||
|
var record = csv.next()!
|
||||||
|
XCTAssertEqual(record, ["abab", "cdcd", "efef"])
|
||||||
|
record = csv.next()!
|
||||||
|
XCTAssertEqual(record, ["zxcv", "asdf", "qw\"er"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testEmptyField() {
|
||||||
|
let csvString = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\","
|
||||||
|
let csv = try! CSVReader(string: csvString)
|
||||||
|
var record = csv.next()!
|
||||||
|
XCTAssertEqual(record, ["abab", "", "cdcd", "efef"])
|
||||||
|
record = csv.next()!
|
||||||
|
XCTAssertEqual(record, ["zxcv", "asdf", "qw\"er", ""])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDoubleQuoteBeforeLineBreak1() {
|
||||||
|
let csv = "\"abc\",1,\"2\"\n\n\"cde\",3,\"4\""
|
||||||
|
var i = 0
|
||||||
|
for record in AnyIterator(try! CSVReader(string: csv)) {
|
||||||
|
switch i {
|
||||||
|
case 0: XCTAssertEqual(record, ["abc", "1", "2"])
|
||||||
|
case 1: XCTAssertEqual(record, [""])
|
||||||
|
case 2: XCTAssertEqual(record, ["cde", "3", "4"])
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
XCTAssertEqual(i, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDoubleQuoteBeforeLineBreak2() {
|
||||||
|
let csv = "\"abc\",1,\"2\"\r\n\"cde\",3,\"4\"\r"
|
||||||
|
var i = 0
|
||||||
|
for record in AnyIterator(try! CSVReader(string: csv)) {
|
||||||
|
switch i {
|
||||||
|
case 0: XCTAssertEqual(record, ["abc", "1", "2"])
|
||||||
|
case 1: XCTAssertEqual(record, ["cde", "3", "4"])
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
XCTAssertEqual(i, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCSVState1() {
|
||||||
|
let it = "あ,い1,\"う\",えお\n,,x,".unicodeScalars.makeIterator()
|
||||||
|
let config = CSVReader.Configuration(hasHeaderRow: false,
|
||||||
|
trimFields: false,
|
||||||
|
delimiter: ",",
|
||||||
|
whitespaces: .whitespaces)
|
||||||
|
let csv = try! CSVReader(iterator: it, configuration: config)
|
||||||
|
|
||||||
|
var records = [[String]]()
|
||||||
|
|
||||||
|
while let record = csv.next() {
|
||||||
|
records.append(record)
|
||||||
|
}
|
||||||
|
XCTAssertEqual(records.count, 2)
|
||||||
|
XCTAssertEqual(records[0], ["あ", "い1", "う", "えお"])
|
||||||
|
XCTAssertEqual(records[1], ["", "", "x", ""])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSubscriptInt() {
|
||||||
|
let csvString = "a,bb,ccc"
|
||||||
|
let csv = try! CSVReader(string: csvString)
|
||||||
|
for record in AnyIterator(csv) {
|
||||||
|
XCTAssertEqual(record[0], "a")
|
||||||
|
XCTAssertEqual(record[1], "bb")
|
||||||
|
XCTAssertEqual(record[2], "ccc")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testHasHeaderRow1() {
|
||||||
|
let csvString = "key1,key2\nvalue1,value2"
|
||||||
|
let csv = try! CSVReader(string: csvString, hasHeaderRow: true)
|
||||||
|
XCTAssertEqual(csv.headerRow, ["key1", "key2"])
|
||||||
|
XCTAssertNil(csv.currentRow)
|
||||||
|
csv.next()
|
||||||
|
XCTAssertEqual(csv.currentRow, ["value1", "value2"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testHasHeaderRow2() {
|
||||||
|
let csvString = "key1,key2\n"
|
||||||
|
let csv = try! CSVReader(string: csvString, hasHeaderRow: true)
|
||||||
|
XCTAssertEqual(csv.headerRow, ["key1", "key2"])
|
||||||
|
XCTAssertNil(csv.currentRow)
|
||||||
|
csv.next()
|
||||||
|
XCTAssertNil(csv.currentRow)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testHasHeaderRow3() {
|
||||||
|
let csvString = "key1,key2"
|
||||||
|
let csv = try! CSVReader(string: csvString, hasHeaderRow: true)
|
||||||
|
XCTAssertEqual(csv.headerRow, ["key1", "key2"])
|
||||||
|
XCTAssertNil(csv.currentRow)
|
||||||
|
csv.next()
|
||||||
|
XCTAssertNil(csv.currentRow)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testHasHeaderRow4() {
|
||||||
|
let csvString = ""
|
||||||
|
do {
|
||||||
|
_ = try CSVReader(string: csvString, hasHeaderRow: true)
|
||||||
|
XCTFail("CSVReader did not throw an error")
|
||||||
|
} catch CSVError.cannotReadHeaderRow {
|
||||||
|
// Success
|
||||||
|
} catch {
|
||||||
|
XCTFail("\(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSubscript1() {
|
||||||
|
let csvString = "key1,key2\nvalue1,value2"
|
||||||
|
let csv = try! CSVReader(string: csvString, hasHeaderRow: true)
|
||||||
|
csv.next()
|
||||||
|
XCTAssertEqual(csv["key1"], "value1")
|
||||||
|
XCTAssertEqual(csv["key2"], "value2")
|
||||||
|
XCTAssertNil(csv["key9"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSubscript2() {
|
||||||
|
let csvString = "key1,key2\nvalue1"
|
||||||
|
let csv = try! CSVReader(string: csvString, hasHeaderRow: true)
|
||||||
|
csv.next()
|
||||||
|
XCTAssertEqual(csv["key1"], "value1")
|
||||||
|
XCTAssertEqual(csv["key2"], "")
|
||||||
|
XCTAssertNil(csv["key9"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testToArray() {
|
||||||
|
let csvString = "1,2,3,4,5\n6,7,8,9,0"
|
||||||
|
let csv = try! CSVReader(string: csvString)
|
||||||
|
let records = AnyIterator(csv).map { $0 }
|
||||||
|
XCTAssertEqual(records[0], ["1", "2", "3", "4", "5"])
|
||||||
|
XCTAssertEqual(records[1], ["6", "7", "8", "9", "0"])
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,634 @@
|
||||||
|
//
|
||||||
|
// CSVRowDecoderTests.swift
|
||||||
|
// CSV
|
||||||
|
//
|
||||||
|
// Created by Ian Grossberg on 9/11/18.
|
||||||
|
// Copyright © 2018 yaslab. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
import CSV
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Models
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
fileprivate enum Enum: String, Decodable {
|
||||||
|
case first
|
||||||
|
case second
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate protocol DecodableTest: Equatable {
|
||||||
|
var intKey: Int { get }
|
||||||
|
var stringKey: String { get }
|
||||||
|
var optionalStringKey: String? { get }
|
||||||
|
var dateKey: Date { get }
|
||||||
|
var enumKey: Enum { get }
|
||||||
|
|
||||||
|
static func headerRow() -> String
|
||||||
|
func toRow() -> String
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DecodableTest {
|
||||||
|
fileprivate static func headerRow() -> String {
|
||||||
|
return "stringKey,optionalStringKey,intKey,ignored,dateKey,enumKey\n"
|
||||||
|
}
|
||||||
|
fileprivate func toRow() -> String {
|
||||||
|
var row = ""
|
||||||
|
row += "\(self.stringKey),"
|
||||||
|
row += "\(self.optionalStringKey ?? ""),"
|
||||||
|
row += "\(self.intKey),"
|
||||||
|
row += ","
|
||||||
|
row += "\"\(self.dateKey.timeIntervalSinceReferenceDate)\","
|
||||||
|
row += "\(self.enumKey.rawValue)"
|
||||||
|
row += "\n"
|
||||||
|
return row
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Equatable where Self: DecodableTest {
|
||||||
|
fileprivate static func ==(left: Self, right: Self) -> Bool {
|
||||||
|
return left.intKey == right.intKey
|
||||||
|
&& left.stringKey == right.stringKey
|
||||||
|
&& left.optionalStringKey == right.optionalStringKey
|
||||||
|
&& Int(left.dateKey.timeIntervalSince1970) == Int(right.dateKey.timeIntervalSince1970)
|
||||||
|
&& left.enumKey == right.enumKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// CSVRowDecoderTests
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
class CSVRowDecoderTests: XCTestCase {
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
fileprivate struct SupportedDecodableExample: Decodable, DecodableTest {
|
||||||
|
let intKey: Int
|
||||||
|
let stringKey: String
|
||||||
|
let optionalStringKey: String?
|
||||||
|
let dateKey: Date
|
||||||
|
let enumKey: Enum
|
||||||
|
|
||||||
|
static var examples: [SupportedDecodableExample] {
|
||||||
|
return [
|
||||||
|
SupportedDecodableExample(intKey: 12345, stringKey: "stringValue", optionalStringKey: nil, dateKey: Date(), enumKey: .first),
|
||||||
|
SupportedDecodableExample(intKey: 54321, stringKey: "stringValue2", optionalStringKey: "withValue", dateKey: Date(timeInterval: 100, since: Date()), enumKey: .second)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testNoHeader() {
|
||||||
|
let noHeaderStr = "あ,い1,\"う\",えお\n,,x,"
|
||||||
|
let noHeaderCSV = try! CSVReader(string: noHeaderStr, hasHeaderRow: false)
|
||||||
|
|
||||||
|
do {
|
||||||
|
let decoder = CSVRowDecoder()
|
||||||
|
let _ = try decoder.decode(SupportedDecodableExample.self, from: noHeaderCSV)
|
||||||
|
XCTFail("Expect DecodingError.typeMismatch Error thrown")
|
||||||
|
} catch {
|
||||||
|
// Success
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testNumberOfFieldsIsSmall() {
|
||||||
|
let csv = """
|
||||||
|
stringKey,intKey,optionalStringKey,dateKey,enumKey
|
||||||
|
string 0 0 first
|
||||||
|
string,0,,0,first
|
||||||
|
"""
|
||||||
|
let reader = try! CSVReader(string: csv, hasHeaderRow: true)
|
||||||
|
|
||||||
|
do {
|
||||||
|
let decoder = CSVRowDecoder()
|
||||||
|
if reader.next() != nil {
|
||||||
|
_ = try decoder.decode(SupportedDecodableExample.self, from: reader)
|
||||||
|
}
|
||||||
|
XCTFail("decode<T>() did not threw error")
|
||||||
|
} catch let DecodingError.valueNotFound(_, context) {
|
||||||
|
// Success
|
||||||
|
XCTAssertEqual("intKey", context.codingPath.last!.stringValue)
|
||||||
|
} catch {
|
||||||
|
XCTFail("The error thrown is not a DecodingError.valueNotFound")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testStringCodingKey() {
|
||||||
|
let exampleRecords = SupportedDecodableExample.examples
|
||||||
|
|
||||||
|
let header = SupportedDecodableExample.headerRow()
|
||||||
|
let allRows = exampleRecords.reduce(into: header) { $0 += $1.toRow() }
|
||||||
|
|
||||||
|
let headerCSV = try! CSVReader(string: allRows, hasHeaderRow: true)
|
||||||
|
|
||||||
|
var records = [SupportedDecodableExample]()
|
||||||
|
do {
|
||||||
|
let decoder = CSVRowDecoder()
|
||||||
|
while headerCSV.next() != nil {
|
||||||
|
try records.append(decoder.decode(SupportedDecodableExample.self, from: headerCSV))
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
XCTFail("decode<T>() threw error: \(error)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
XCTAssertEqual(records.count, 2)
|
||||||
|
XCTAssertEqual(records[0], exampleRecords[0])
|
||||||
|
XCTAssertEqual(records[1], exampleRecords[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTypeInvalidDateFormat() {
|
||||||
|
let invalidFieldTypeStr = """
|
||||||
|
dateKey,stringKey,optionalStringKey,intKey,ignored
|
||||||
|
al;ksdjf;akjsdf,asldkj,,1234,
|
||||||
|
"""
|
||||||
|
let invalidFieldTypeCSV = try! CSVReader(string: invalidFieldTypeStr, hasHeaderRow: true)
|
||||||
|
|
||||||
|
do {
|
||||||
|
let decoder = CSVRowDecoder()
|
||||||
|
while invalidFieldTypeCSV.next() != nil {
|
||||||
|
_ = try decoder.decode(SupportedDecodableExample.self, from: invalidFieldTypeCSV)
|
||||||
|
}
|
||||||
|
XCTFail("Expect DecodingError.dataCorrupted Error thrown")
|
||||||
|
} catch {
|
||||||
|
guard let error = error as? DecodingError else {
|
||||||
|
XCTFail("Expect DecodingError Error thrown")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch error {
|
||||||
|
case let DecodingError.typeMismatch(type, context):
|
||||||
|
XCTAssert(type == Double.self)
|
||||||
|
XCTAssertEqual(context.codingPath[0].stringValue, "dateKey", "Type Mismatch Error on unexpected field")
|
||||||
|
default:
|
||||||
|
XCTFail("Expect DecodingError.dataCorrupted Error thrown, got \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
fileprivate struct IntKeyedDecodableExample: Decodable, DecodableTest {
|
||||||
|
private enum CodingKeys: Int, CodingKey {
|
||||||
|
case stringKey = 0
|
||||||
|
case optionalStringKey = 1
|
||||||
|
case intKey = 2
|
||||||
|
case dateKey = 4
|
||||||
|
case enumKey = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
let intKey: Int
|
||||||
|
let stringKey: String
|
||||||
|
let optionalStringKey: String?
|
||||||
|
let dateKey: Date
|
||||||
|
let enumKey: Enum
|
||||||
|
|
||||||
|
static var examples: [IntKeyedDecodableExample] {
|
||||||
|
return [
|
||||||
|
IntKeyedDecodableExample(intKey: 12345, stringKey: "stringValue", optionalStringKey: nil, dateKey: Date(), enumKey: .first),
|
||||||
|
IntKeyedDecodableExample(intKey: 54321, stringKey: "stringValue2", optionalStringKey: "withValue", dateKey: Date(timeInterval: 100, since: Date()), enumKey: .second)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testIntCodingKey() {
|
||||||
|
let exampleRecords = IntKeyedDecodableExample.examples
|
||||||
|
|
||||||
|
let allRows = IntKeyedDecodableExample.examples.reduce(into: "") { $0 += $1.toRow() }
|
||||||
|
|
||||||
|
let headerCSV = try! CSVReader(string: allRows, hasHeaderRow: false)
|
||||||
|
|
||||||
|
var records = [IntKeyedDecodableExample]()
|
||||||
|
do {
|
||||||
|
let decoder = CSVRowDecoder()
|
||||||
|
while headerCSV.next() != nil {
|
||||||
|
try records.append(decoder.decode(IntKeyedDecodableExample.self, from: headerCSV))
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
XCTFail("decode<T>() threw error: \(error)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
XCTAssertEqual(records.count, 2)
|
||||||
|
XCTAssertEqual(records[0], exampleRecords[0])
|
||||||
|
XCTAssertEqual(records[1], exampleRecords[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testIntCodingKeyWhileIgnoringHeaders() {
|
||||||
|
let exampleRecords = IntKeyedDecodableExample.examples
|
||||||
|
|
||||||
|
let header = IntKeyedDecodableExample.headerRow()
|
||||||
|
let allRows = exampleRecords.reduce(into: header) { $0 += $1.toRow() }
|
||||||
|
|
||||||
|
let headerCSV = try! CSVReader(string: allRows, hasHeaderRow: true)
|
||||||
|
|
||||||
|
var records = [IntKeyedDecodableExample]()
|
||||||
|
do {
|
||||||
|
let decoder = CSVRowDecoder()
|
||||||
|
while headerCSV.next() != nil {
|
||||||
|
try records.append(decoder.decode(IntKeyedDecodableExample.self, from: headerCSV))
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
XCTFail("decode<T>() threw error: \(error)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
XCTAssertEqual(records.count, 2)
|
||||||
|
XCTAssertEqual(records[0], exampleRecords[0])
|
||||||
|
XCTAssertEqual(records[1], exampleRecords[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTypeMismatch() {
|
||||||
|
let exampleRecords = SupportedDecodableExample.examples
|
||||||
|
|
||||||
|
let invalidFieldTypeStr = """
|
||||||
|
stringKey,optionalStringKey,intKey,ignored
|
||||||
|
\(exampleRecords[0].stringKey),,this is a string where we expect an Int,
|
||||||
|
\(exampleRecords[1].stringKey),\(exampleRecords[1].optionalStringKey!),\(exampleRecords[1].intKey),
|
||||||
|
"""
|
||||||
|
let invalidFieldTypeCSV = try! CSVReader(string: invalidFieldTypeStr, hasHeaderRow: true)
|
||||||
|
|
||||||
|
do {
|
||||||
|
let decoder = CSVRowDecoder()
|
||||||
|
while invalidFieldTypeCSV.next() != nil {
|
||||||
|
_ = try decoder.decode(IntKeyedDecodableExample.self, from: invalidFieldTypeCSV)
|
||||||
|
}
|
||||||
|
XCTFail("Expect DecodingError.typeMismatch Error thrown")
|
||||||
|
} catch {
|
||||||
|
guard let error = error as? DecodingError else {
|
||||||
|
XCTFail("Expect DecodingError Error thrown")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch error {
|
||||||
|
case let .typeMismatch(_, context):
|
||||||
|
XCTAssertEqual(context.codingPath[0].stringValue, "intKey", "Type Mismatch Error on unexpected field")
|
||||||
|
default:
|
||||||
|
XCTFail("Expect Type Mismatch Error thrown")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
fileprivate struct UnsupportedDecodableExample: Decodable, Equatable {
|
||||||
|
let enumKey: Enum
|
||||||
|
|
||||||
|
static var examples: [UnsupportedDecodableExample] {
|
||||||
|
return [
|
||||||
|
UnsupportedDecodableExample(enumKey: .first),
|
||||||
|
UnsupportedDecodableExample(enumKey: .second)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUnsupportedDecodableField() {
|
||||||
|
let exampleRecords = UnsupportedDecodableExample.examples
|
||||||
|
|
||||||
|
let headerStr = """
|
||||||
|
enumKey,optionalStringKey,intKey,ignored,dateKey
|
||||||
|
\(exampleRecords[0].enumKey),"hiiiii",123445,,
|
||||||
|
\(exampleRecords[1].enumKey),,54231,,
|
||||||
|
\("third"),,54231,,
|
||||||
|
"""
|
||||||
|
let headerCSV = try! CSVReader(string: headerStr, hasHeaderRow: true)
|
||||||
|
|
||||||
|
var records = [UnsupportedDecodableExample]()
|
||||||
|
do {
|
||||||
|
let decoder = CSVRowDecoder()
|
||||||
|
while headerCSV.next() != nil {
|
||||||
|
try records.append(decoder.decode(UnsupportedDecodableExample.self, from: headerCSV))
|
||||||
|
}
|
||||||
|
XCTFail("Expect Data Corrupted Error thrown")
|
||||||
|
} catch {
|
||||||
|
XCTAssertEqual(records.count, 2)
|
||||||
|
guard let decodingError = error as? DecodingError else {
|
||||||
|
XCTFail("Expect DecodingError Error thrown, instead we go \(error)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch decodingError {
|
||||||
|
case let .dataCorrupted(context):
|
||||||
|
guard context.codingPath[0].stringValue == "enumKey" else {
|
||||||
|
XCTFail("Data Corrupted Error on unexpected field")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
XCTFail("Expect Data Corrupted Error thrown, instead we got \(decodingError)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
fileprivate struct IntegerDecodableExample: Decodable {
|
||||||
|
let intValue: Int
|
||||||
|
let int8Value: Int8
|
||||||
|
let int16Value: Int16
|
||||||
|
let int32Value: Int32
|
||||||
|
let int64Value: Int64
|
||||||
|
let uintValue: UInt
|
||||||
|
let uint8Value: UInt8
|
||||||
|
let uint16Value: UInt16
|
||||||
|
let uint32Value: UInt32
|
||||||
|
let uint64Value: UInt64
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDecodeInteger() {
|
||||||
|
let csv = """
|
||||||
|
intValue,int8Value,int16Value,int32Value,int64Value,uintValue,uint8Value,uint16Value,uint32Value,uint64Value
|
||||||
|
0,123,4567,89012,345678901234567890,1,124,4568,89013,345678901234567891
|
||||||
|
"""
|
||||||
|
do {
|
||||||
|
let reader = try CSVReader(string: csv, hasHeaderRow: true)
|
||||||
|
reader.next()
|
||||||
|
|
||||||
|
let decoder = CSVRowDecoder()
|
||||||
|
let row = try decoder.decode(IntegerDecodableExample.self, from: reader)
|
||||||
|
XCTAssertEqual(row.intValue, 0)
|
||||||
|
XCTAssertEqual(row.int8Value, 123)
|
||||||
|
XCTAssertEqual(row.int16Value, 4567)
|
||||||
|
XCTAssertEqual(row.int32Value, 89012)
|
||||||
|
XCTAssertEqual(row.int64Value, 345678901234567890)
|
||||||
|
XCTAssertEqual(row.uintValue, 1)
|
||||||
|
XCTAssertEqual(row.uint8Value, 124)
|
||||||
|
XCTAssertEqual(row.uint16Value, 4568)
|
||||||
|
XCTAssertEqual(row.uint32Value, 89013)
|
||||||
|
XCTAssertEqual(row.uint64Value, 345678901234567891)
|
||||||
|
} catch {
|
||||||
|
XCTFail("\(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
fileprivate struct FloatDecodableExample: Decodable {
|
||||||
|
let floatValue: Float
|
||||||
|
let doubleValue: Double
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDecodeFloat() {
|
||||||
|
let csv = """
|
||||||
|
floatValue,doubleValue
|
||||||
|
123.456,7890.1234
|
||||||
|
"""
|
||||||
|
do {
|
||||||
|
let reader = try CSVReader(string: csv, hasHeaderRow: true)
|
||||||
|
reader.next()
|
||||||
|
|
||||||
|
let decoder = CSVRowDecoder()
|
||||||
|
let row = try decoder.decode(FloatDecodableExample.self, from: reader)
|
||||||
|
XCTAssertEqual(row.floatValue, 123.456)
|
||||||
|
XCTAssertEqual(row.doubleValue, 7890.1234)
|
||||||
|
} catch {
|
||||||
|
XCTFail("\(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
fileprivate struct BoolDecodingStrategyExample: Decodable {
|
||||||
|
let falseValue: Bool
|
||||||
|
let trueValue: Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBoolDecodingStrategy_default() {
|
||||||
|
let csv = """
|
||||||
|
falseValue,trueValue
|
||||||
|
false,true
|
||||||
|
"""
|
||||||
|
do {
|
||||||
|
let reader = try CSVReader(string: csv, hasHeaderRow: true)
|
||||||
|
reader.next()
|
||||||
|
|
||||||
|
let decoder = CSVRowDecoder()
|
||||||
|
decoder.boolDecodingStrategy = .default
|
||||||
|
let row = try decoder.decode(BoolDecodingStrategyExample.self, from: reader)
|
||||||
|
XCTAssertEqual(row.falseValue, false)
|
||||||
|
XCTAssertEqual(row.trueValue, true)
|
||||||
|
} catch {
|
||||||
|
XCTFail("\(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBoolDecodingStrategy_custom() {
|
||||||
|
let csv = """
|
||||||
|
falseValue,trueValue
|
||||||
|
0,1
|
||||||
|
"""
|
||||||
|
do {
|
||||||
|
let reader = try CSVReader(string: csv, hasHeaderRow: true)
|
||||||
|
reader.next()
|
||||||
|
|
||||||
|
let decoder = CSVRowDecoder()
|
||||||
|
decoder.boolDecodingStrategy = .custom({ $0 != "0" })
|
||||||
|
let row = try decoder.decode(BoolDecodingStrategyExample.self, from: reader)
|
||||||
|
XCTAssertEqual(row.falseValue, false)
|
||||||
|
XCTAssertEqual(row.trueValue, true)
|
||||||
|
} catch {
|
||||||
|
XCTFail("\(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
fileprivate struct DateDecodingStrategyExample: Decodable {
|
||||||
|
let date: Date
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDateDecodingStrategy_deferredToDate() {
|
||||||
|
let expected = Date()
|
||||||
|
let csv = """
|
||||||
|
date
|
||||||
|
\(expected.timeIntervalSinceReferenceDate)
|
||||||
|
"""
|
||||||
|
do {
|
||||||
|
let reader = try CSVReader(string: csv, hasHeaderRow: true)
|
||||||
|
reader.next()
|
||||||
|
|
||||||
|
let decoder = CSVRowDecoder()
|
||||||
|
decoder.dateDecodingStrategy = .deferredToDate
|
||||||
|
let row = try decoder.decode(DateDecodingStrategyExample.self, from: reader)
|
||||||
|
XCTAssertEqual(row.date, expected)
|
||||||
|
} catch {
|
||||||
|
XCTFail("\(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDateDecodingStrategy_secondsSince1970() {
|
||||||
|
let expected = Date()
|
||||||
|
let csv = """
|
||||||
|
date
|
||||||
|
\(expected.timeIntervalSince1970)
|
||||||
|
"""
|
||||||
|
do {
|
||||||
|
let reader = try CSVReader(string: csv, hasHeaderRow: true)
|
||||||
|
reader.next()
|
||||||
|
|
||||||
|
let decoder = CSVRowDecoder()
|
||||||
|
decoder.dateDecodingStrategy = .secondsSince1970
|
||||||
|
let row = try decoder.decode(DateDecodingStrategyExample.self, from: reader)
|
||||||
|
XCTAssertEqual(row.date.timeIntervalSince1970, expected.timeIntervalSince1970)
|
||||||
|
} catch {
|
||||||
|
XCTFail("\(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDateDecodingStrategy_millisecondsSince1970() {
|
||||||
|
let expected = Date()
|
||||||
|
let csv = """
|
||||||
|
date
|
||||||
|
\(expected.timeIntervalSince1970 * 1000.0)
|
||||||
|
"""
|
||||||
|
do {
|
||||||
|
let reader = try CSVReader(string: csv, hasHeaderRow: true)
|
||||||
|
reader.next()
|
||||||
|
|
||||||
|
let decoder = CSVRowDecoder()
|
||||||
|
decoder.dateDecodingStrategy = .millisecondsSince1970
|
||||||
|
let row = try decoder.decode(DateDecodingStrategyExample.self, from: reader)
|
||||||
|
XCTAssertEqual(row.date.timeIntervalSince1970, expected.timeIntervalSince1970)
|
||||||
|
} catch {
|
||||||
|
XCTFail("\(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)
|
||||||
|
func testDateDecodingStrategy_iso8601() {
|
||||||
|
let csv = """
|
||||||
|
date
|
||||||
|
2018-11-22T12:34:56+09:00
|
||||||
|
"""
|
||||||
|
do {
|
||||||
|
let reader = try CSVReader(string: csv, hasHeaderRow: true)
|
||||||
|
reader.next()
|
||||||
|
|
||||||
|
let decoder = CSVRowDecoder()
|
||||||
|
decoder.dateDecodingStrategy = .iso8601
|
||||||
|
let row = try decoder.decode(DateDecodingStrategyExample.self, from: reader)
|
||||||
|
XCTAssertEqual(row.date.timeIntervalSince1970, 1542857696)
|
||||||
|
} catch {
|
||||||
|
XCTFail("\(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDateDecodingStrategy_formatted() {
|
||||||
|
let csv = """
|
||||||
|
date
|
||||||
|
2018/11/22
|
||||||
|
"""
|
||||||
|
do {
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
formatter.locale = Locale(identifier: "en_US_POSIX")
|
||||||
|
formatter.timeZone = TimeZone(identifier: "Asia/Tokyo")
|
||||||
|
formatter.dateFormat = "yyyy/MM/dd"
|
||||||
|
|
||||||
|
let reader = try CSVReader(string: csv, hasHeaderRow: true)
|
||||||
|
reader.next()
|
||||||
|
|
||||||
|
let decoder = CSVRowDecoder()
|
||||||
|
decoder.dateDecodingStrategy = .formatted(formatter)
|
||||||
|
let row = try decoder.decode(DateDecodingStrategyExample.self, from: reader)
|
||||||
|
XCTAssertEqual(row.date.timeIntervalSince1970, 1542812400)
|
||||||
|
} catch {
|
||||||
|
XCTFail("\(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDateDecodingStrategy_custom() {
|
||||||
|
let expected = Date()
|
||||||
|
let csv = """
|
||||||
|
date
|
||||||
|
\(expected.timeIntervalSinceReferenceDate)
|
||||||
|
"""
|
||||||
|
do {
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
formatter.locale = Locale(identifier: "en_US_POSIX")
|
||||||
|
formatter.timeZone = TimeZone(identifier: "Asia/Tokyo")
|
||||||
|
formatter.dateFormat = "yyyy/MM/dd"
|
||||||
|
|
||||||
|
let reader = try CSVReader(string: csv, hasHeaderRow: true)
|
||||||
|
reader.next()
|
||||||
|
|
||||||
|
let decoder = CSVRowDecoder()
|
||||||
|
decoder.dateDecodingStrategy = .custom({ Date(timeIntervalSinceReferenceDate: Double($0)!) })
|
||||||
|
let row = try decoder.decode(DateDecodingStrategyExample.self, from: reader)
|
||||||
|
XCTAssertEqual(row.date, expected)
|
||||||
|
} catch {
|
||||||
|
XCTFail("\(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
fileprivate struct DataDecodingStrategyExample: Decodable {
|
||||||
|
let data: Data
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDataDecodingStrategy_base64() {
|
||||||
|
let expected = Data([0x56, 0x12, 0x00, 0x34, 0x1a, 0xfe])
|
||||||
|
let csv = """
|
||||||
|
data
|
||||||
|
"\(expected.base64EncodedString())"
|
||||||
|
"""
|
||||||
|
do {
|
||||||
|
let reader = try CSVReader(string: csv, hasHeaderRow: true)
|
||||||
|
reader.next()
|
||||||
|
|
||||||
|
let decoder = CSVRowDecoder()
|
||||||
|
decoder.dataDecodingStrategy = .base64
|
||||||
|
let row = try decoder.decode(DataDecodingStrategyExample.self, from: reader)
|
||||||
|
XCTAssertEqual(row.data, expected)
|
||||||
|
} catch {
|
||||||
|
XCTFail("\(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDataDecodingStrategy_custom() {
|
||||||
|
let expected = Data([0x34, 0x1a, 0xfe, 0x56, 0x12, 0x00])
|
||||||
|
let csv = """
|
||||||
|
data
|
||||||
|
"\(expected.map({ String(format: "%02x", $0) }).joined())"
|
||||||
|
"""
|
||||||
|
do {
|
||||||
|
let reader = try CSVReader(string: csv, hasHeaderRow: true)
|
||||||
|
reader.next()
|
||||||
|
|
||||||
|
let decoder = CSVRowDecoder()
|
||||||
|
decoder.dataDecodingStrategy = .custom { value in
|
||||||
|
var bytes = [UInt8]()
|
||||||
|
for i in stride(from: 0, to: value.count, by: 2) {
|
||||||
|
let start = value.index(value.startIndex, offsetBy: i)
|
||||||
|
let end = value.index(value.startIndex, offsetBy: i + 1)
|
||||||
|
bytes.append(UInt8(value[start...end], radix: 16)!)
|
||||||
|
}
|
||||||
|
return Data(bytes)
|
||||||
|
}
|
||||||
|
let row = try decoder.decode(DataDecodingStrategyExample.self, from: reader)
|
||||||
|
XCTAssertEqual(row.data, expected)
|
||||||
|
} catch {
|
||||||
|
XCTFail("\(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
fileprivate struct FoundationDecodingExample: Decodable {
|
||||||
|
let url: URL
|
||||||
|
let decimal: Decimal
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFoundationDecoding() {
|
||||||
|
let csv = """
|
||||||
|
url,decimal
|
||||||
|
"https://www.example.com/path?param=1",99999999999999999999.9999999999999999
|
||||||
|
"""
|
||||||
|
do {
|
||||||
|
let reader = try CSVReader(string: csv, hasHeaderRow: true)
|
||||||
|
reader.next()
|
||||||
|
|
||||||
|
let decoder = CSVRowDecoder()
|
||||||
|
let row = try decoder.decode(FoundationDecodingExample.self, from: reader)
|
||||||
|
XCTAssertEqual(row.url.absoluteString, "https://www.example.com/path?param=1")
|
||||||
|
XCTAssertEqual(row.decimal.description, "99999999999999999999.9999999999999999")
|
||||||
|
} catch {
|
||||||
|
XCTFail("\(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,336 @@
|
||||||
|
//
|
||||||
|
// CSVWriterTests.swift
|
||||||
|
// CSV
|
||||||
|
//
|
||||||
|
// Created by Yasuhiro Hatta on 2017/05/28.
|
||||||
|
// Copyright © 2017年 yaslab. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
import CSV
|
||||||
|
|
||||||
|
extension OutputStream {
|
||||||
|
|
||||||
|
var data: Data? {
|
||||||
|
return self.property(forKey: .dataWrittenToMemoryStreamKey) as? Data
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class CSVWriterTests: XCTestCase {
|
||||||
|
|
||||||
|
let str = "TEST-test-1234-😄😆👨👩👧👦"
|
||||||
|
|
||||||
|
/// xxxx
|
||||||
|
func testSingleFieldSingleRecord() {
|
||||||
|
let csv = try! CSVWriter(stream: .toMemory())
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str)
|
||||||
|
|
||||||
|
let data = csv.stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf8)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// xxxx
|
||||||
|
/// xxxx
|
||||||
|
func testSingleFieldMultipleRecord() {
|
||||||
|
let stream = OutputStream.toMemory()
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: stream)
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str + "-1")
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str + "-2")
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf8)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, "\(str)-1\n\(str)-2")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// xxxx,xxxx
|
||||||
|
func testMultipleFieldSingleRecord() {
|
||||||
|
let stream = OutputStream.toMemory()
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: stream)
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str + "-1")
|
||||||
|
try! csv.write(field: str + "-2")
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf8)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, "\(str)-1,\(str)-2")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// xxxx,xxxx
|
||||||
|
/// xxxx,xxxx
|
||||||
|
func testMultipleFieldMultipleRecord() {
|
||||||
|
let stream = OutputStream.toMemory()
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: stream)
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str + "-1-1")
|
||||||
|
try! csv.write(field: str + "-1-2")
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str + "-2-1")
|
||||||
|
try! csv.write(field: str + "-2-2")
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf8)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, "\(str)-1-1,\(str)-1-2\n\(str)-2-1,\(str)-2-2")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// "xxxx",xxxx
|
||||||
|
func testQuoted() {
|
||||||
|
let stream = OutputStream.toMemory()
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: stream)
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str + "-1", quoted: true)
|
||||||
|
try! csv.write(field: str + "-2") // quoted: false
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf8)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, "\"\(str)-1\",\(str)-2")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// xxxx,"xx\nxx"
|
||||||
|
func testQuotedNewline() {
|
||||||
|
let stream = OutputStream.toMemory()
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: stream)
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str + "-1") // quoted: false
|
||||||
|
try! csv.write(field: str + "-\n-2", quoted: true)
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf8)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, "\(str)-1,\"\(str)-\n-2\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// xxxx,"xx""xx"
|
||||||
|
func testEscapeQuote() {
|
||||||
|
let stream = OutputStream.toMemory()
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: stream)
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str + "-1") // quoted: false
|
||||||
|
try! csv.write(field: str + "-\"-2", quoted: true)
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf8)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, "\(str)-1,\"\(str)-\"\"-2\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// csv.write(row: ["xxxx", "xx,\"xx"])
|
||||||
|
/// -> xxxx,"xx,""xx"
|
||||||
|
func testEscapeQuoteAutomatically() {
|
||||||
|
let stream = OutputStream.toMemory()
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: stream)
|
||||||
|
try! csv.write(row: ["id", "testing,\"comma"]) // quoted: false
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf8)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, "id,\"testing,\"\"comma\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// csv.write(row: ["xxxx", "xx\rxx", "xx\nxx", "xx\r\nrxx"])
|
||||||
|
/// -> xxxx,"xx\rxx","xx\nxx","xx\r\nxx"
|
||||||
|
func testEscapeNewlineAutomatically() {
|
||||||
|
let stream = OutputStream.toMemory()
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: stream)
|
||||||
|
try! csv.write(row: ["id", "testing\rCR", "testing\nLF", "testing\r\nCRLF"]) // quoted: false
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf8)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, "id,\"testing\rCR\",\"testing\nLF\",\"testing\r\nCRLF\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test delimiter: $
|
||||||
|
/// csv.write(row: ["xxxx", "xx$xx"])
|
||||||
|
/// -> xxxx$"xx$xx"
|
||||||
|
func testEscapeDelimiterAutomatically() {
|
||||||
|
let stream = OutputStream.toMemory()
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: stream, delimiter: "$")
|
||||||
|
try! csv.write(row: ["id", "testing$dollar"]) // quoted: false
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf8)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, "id$\"testing$dollar\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test delimiter: "\t"
|
||||||
|
func testDelimiter() {
|
||||||
|
let stream = OutputStream.toMemory()
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter.init(stream: stream, delimiter: "\t")
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str + "-1")
|
||||||
|
try! csv.write(field: str + "-2")
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf8)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, "\(str)-1\t\(str)-2")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test newline: "\r\n"
|
||||||
|
func testNewline() {
|
||||||
|
let stream = OutputStream.toMemory()
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter.init(stream: stream, newline: .crlf)
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str + "-1")
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str + "-2")
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf8)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, "\(str)-1\r\n\(str)-2")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// xxxx,xxxx
|
||||||
|
func testValueContainsComma() {
|
||||||
|
let stream = OutputStream.toMemory()
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: stream)
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str + ",1", quoted: true)
|
||||||
|
try! csv.write(field: str + ",2") // quoted: false
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf8)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, "\"\(str),1\",\"\(str),2\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// UTF16 Big Endian
|
||||||
|
func testUTF16BE() {
|
||||||
|
let stream = OutputStream.toMemory()
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: stream, codecType: UTF16.self, endian: .big)
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str)
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf16BigEndian)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// UTF16 Little Endian
|
||||||
|
func testUTF16LE() {
|
||||||
|
let stream = OutputStream.toMemory()
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: stream, codecType: UTF16.self, endian: .little)
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str)
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf16LittleEndian)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// UTF32 Big Endian
|
||||||
|
func testUTF32BE() {
|
||||||
|
let stream = OutputStream.toMemory()
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: stream, codecType: UTF32.self, endian: .big)
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str)
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf32BigEndian)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// UTF32 Little Endian
|
||||||
|
func testUTF32LE() {
|
||||||
|
let stream = OutputStream.toMemory()
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: stream, codecType: UTF32.self, endian: .little)
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str)
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf32LittleEndian)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testReadme() {
|
||||||
|
let csv = try! CSVWriter(stream: .toMemory())
|
||||||
|
|
||||||
|
// Write a row
|
||||||
|
try! csv.write(row: ["id", "name"])
|
||||||
|
|
||||||
|
// Write fields separately
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: "1")
|
||||||
|
try! csv.write(field: "foo")
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: "2")
|
||||||
|
try! csv.write(field: "bar")
|
||||||
|
|
||||||
|
csv.stream.close()
|
||||||
|
|
||||||
|
// Get a String
|
||||||
|
let csvData = csv.stream.property(forKey: .dataWrittenToMemoryStreamKey) as! Data
|
||||||
|
let csvString = String(data: csvData, encoding: .utf8)!
|
||||||
|
print(csvString)
|
||||||
|
// => "id,name\n1,foo\n2,bar"
|
||||||
|
|
||||||
|
XCTAssertEqual("id,name\n1,foo\n2,bar", csvString)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
//
|
||||||
|
// LineBreakTests.swift
|
||||||
|
// CSV
|
||||||
|
//
|
||||||
|
// Created by Yasuhiro Hatta on 2016/06/11.
|
||||||
|
// Copyright © 2016 yaslab. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
@testable import CSV
|
||||||
|
|
||||||
|
class LineBreakTests: XCTestCase {
|
||||||
|
|
||||||
|
func testLF() {
|
||||||
|
let csv = "abab,cdcd,efef\nzxcv,asdf,qwer"
|
||||||
|
let records = parse(csv: csv)
|
||||||
|
XCTAssertEqual(records[0], ["abab", "cdcd", "efef"])
|
||||||
|
XCTAssertEqual(records[1], ["zxcv", "asdf", "qwer"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCRLF() {
|
||||||
|
let csv = "abab,cdcd,efef\r\nzxcv,asdf,qwer"
|
||||||
|
let records = parse(csv: csv)
|
||||||
|
XCTAssertEqual(records[0], ["abab", "cdcd", "efef"])
|
||||||
|
XCTAssertEqual(records[1], ["zxcv", "asdf", "qwer"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLastCR() {
|
||||||
|
let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\",\r"
|
||||||
|
let records = parse(csv: csv)
|
||||||
|
XCTAssertEqual(records.count, 2)
|
||||||
|
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
|
||||||
|
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLastCRLF() {
|
||||||
|
let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\",\r\n"
|
||||||
|
let records = parse(csv: csv)
|
||||||
|
XCTAssertEqual(records.count, 2)
|
||||||
|
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
|
||||||
|
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLastLF() {
|
||||||
|
let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\",\n"
|
||||||
|
let records = parse(csv: csv)
|
||||||
|
XCTAssertEqual(records.count, 2)
|
||||||
|
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
|
||||||
|
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLFInQuotationMarks() {
|
||||||
|
let csv = "abab,,\"\rcdcd\n\",efef\r\nzxcv,asdf,\"qw\"\"er\",\n"
|
||||||
|
let records = parse(csv: csv)
|
||||||
|
XCTAssertEqual(records.count, 2)
|
||||||
|
XCTAssertEqual(records[0], ["abab", "", "\rcdcd\n", "efef"])
|
||||||
|
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLineBreakLF() {
|
||||||
|
let csv = "qwe,asd\nzxc,rty"
|
||||||
|
let records = parse(csv: csv)
|
||||||
|
XCTAssertEqual(records.count, 2)
|
||||||
|
XCTAssertEqual(records[0], ["qwe", "asd"])
|
||||||
|
XCTAssertEqual(records[1], ["zxc", "rty"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLineBreakCR() {
|
||||||
|
let csv = "qwe,asd\rzxc,rty"
|
||||||
|
let records = parse(csv: csv)
|
||||||
|
XCTAssertEqual(records.count, 2)
|
||||||
|
XCTAssertEqual(records[0], ["qwe", "asd"])
|
||||||
|
XCTAssertEqual(records[1], ["zxc", "rty"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLineBreakCRLF() {
|
||||||
|
let csv = "qwe,asd\r\nzxc,rty"
|
||||||
|
let records = parse(csv: csv)
|
||||||
|
XCTAssertEqual(records.count, 2)
|
||||||
|
XCTAssertEqual(records[0], ["qwe", "asd"])
|
||||||
|
XCTAssertEqual(records[1], ["zxc", "rty"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLineBreakLFLF() {
|
||||||
|
let csv = "qwe,asd\n\nzxc,rty"
|
||||||
|
let records = parse(csv: csv)
|
||||||
|
XCTAssertEqual(records.count, 3)
|
||||||
|
XCTAssertEqual(records[0], ["qwe", "asd"])
|
||||||
|
XCTAssertEqual(records[1], [""])
|
||||||
|
XCTAssertEqual(records[2], ["zxc", "rty"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLineBreakCRCR() {
|
||||||
|
let csv = "qwe,asd\r\rzxc,rty"
|
||||||
|
let records = parse(csv: csv)
|
||||||
|
XCTAssertEqual(records.count, 3)
|
||||||
|
XCTAssertEqual(records[0], ["qwe", "asd"])
|
||||||
|
XCTAssertEqual(records[1], [""])
|
||||||
|
XCTAssertEqual(records[2], ["zxc", "rty"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLineBreakCRLFCRLF() {
|
||||||
|
let csv = "qwe,asd\r\n\r\nzxc,rty"
|
||||||
|
let records = parse(csv: csv)
|
||||||
|
XCTAssertEqual(records.count, 3)
|
||||||
|
XCTAssertEqual(records[0], ["qwe", "asd"])
|
||||||
|
XCTAssertEqual(records[1], [""])
|
||||||
|
XCTAssertEqual(records[2], ["zxc", "rty"])
|
||||||
|
}
|
||||||
|
|
||||||
|
private func parse(csv: String) -> [[String]] {
|
||||||
|
let reader = try! CSVReader(string: csv)
|
||||||
|
return reader.map { $0 }
|
||||||
|
// var records = [[String]]()
|
||||||
|
// try! reader.enumerateRows { (row, _, _) in
|
||||||
|
// records.append(row)
|
||||||
|
// }
|
||||||
|
// return records
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
//
|
||||||
|
// ReadmeTests.swift
|
||||||
|
// CSV
|
||||||
|
//
|
||||||
|
// Created by Yasuhiro Hatta on 2016/06/13.
|
||||||
|
// Copyright © 2016 yaslab. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
@testable import CSV
|
||||||
|
|
||||||
|
class ReadmeTests: XCTestCase {
|
||||||
|
|
||||||
|
// MARK: - Reading
|
||||||
|
|
||||||
|
func testFromCSVString() {
|
||||||
|
let csvString = "1,foo\n2,bar"
|
||||||
|
let csv = try! CSVReader(string: csvString)
|
||||||
|
while let row = csv.next() {
|
||||||
|
print("\(row)")
|
||||||
|
}
|
||||||
|
// => ["1", "foo"]
|
||||||
|
// => ["2", "bar"]
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFromFile() {
|
||||||
|
// let stream = InputStream(fileAtPath: "/path/to/file.csv")!
|
||||||
|
// let csv = try! CSVReader(stream: stream)
|
||||||
|
// while let row = csv.next() {
|
||||||
|
// print("\(row)")
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGettingTheHeaderRow() {
|
||||||
|
let csvString = "id,name\n1,foo\n2,bar"
|
||||||
|
let csv = try! CSVReader(string: csvString,
|
||||||
|
hasHeaderRow: true) // It must be true.
|
||||||
|
|
||||||
|
let headerRow = csv.headerRow!
|
||||||
|
print("\(headerRow)") // => ["id", "name"]
|
||||||
|
|
||||||
|
while let row = csv.next() {
|
||||||
|
print("\(row)")
|
||||||
|
}
|
||||||
|
// => ["1", "foo"]
|
||||||
|
// => ["2", "bar"]
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGetTheFieldValueUsingKey() {
|
||||||
|
let csvString = "id,name\n1,foo"
|
||||||
|
let csv = try! CSVReader(string: csvString,
|
||||||
|
hasHeaderRow: true) // It must be true.
|
||||||
|
|
||||||
|
while csv.next() != nil {
|
||||||
|
print("\(csv["id"]!)") // => "1"
|
||||||
|
print("\(csv["name"]!)") // => "foo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testProvideTheCharacterEncoding() {
|
||||||
|
// let stream = InputStream(fileAtPath: "/path/to/file.csv")!
|
||||||
|
// let csv = try! CSVReader(stream: stream,
|
||||||
|
// codecType: UTF16.self,
|
||||||
|
// endian: .big)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Writing
|
||||||
|
|
||||||
|
func testWriteToMemory() {
|
||||||
|
let stream = OutputStream(toMemory: ())
|
||||||
|
let csv = try! CSVWriter(stream: stream)
|
||||||
|
|
||||||
|
// Write a row
|
||||||
|
try! csv.write(row: ["id", "name"])
|
||||||
|
|
||||||
|
// Write fields separately
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: "1")
|
||||||
|
try! csv.write(field: "foo")
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: "2")
|
||||||
|
try! csv.write(field: "bar")
|
||||||
|
|
||||||
|
csv.stream.close()
|
||||||
|
|
||||||
|
// Get a String
|
||||||
|
let csvData = stream.property(forKey: .dataWrittenToMemoryStreamKey) as! NSData
|
||||||
|
let csvString = String(data: Data(referencing: csvData), encoding: .utf8)!
|
||||||
|
print(csvString)
|
||||||
|
// => "id,name\n1,foo\n2,bar"
|
||||||
|
}
|
||||||
|
|
||||||
|
func testWriteToFile() {
|
||||||
|
// let stream = OutputStream(toFileAtPath: "/path/to/file.csv", append: false)!
|
||||||
|
// let csv = try! CSVWriter(stream: stream)
|
||||||
|
//
|
||||||
|
// try! csv.write(row: ["id", "name"])
|
||||||
|
// try! csv.write(row: ["1", "foo"])
|
||||||
|
// try! csv.write(row: ["1", "bar"])
|
||||||
|
//
|
||||||
|
// csv.stream.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
//
|
||||||
|
// TrimFieldsTests.swift
|
||||||
|
// CSV
|
||||||
|
//
|
||||||
|
// Created by Yasuhiro Hatta on 2016/10/18.
|
||||||
|
// Copyright © 2016 yaslab. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
@testable import CSV
|
||||||
|
|
||||||
|
class TrimFieldsTests: XCTestCase {
|
||||||
|
|
||||||
|
func testTrimFields1() {
|
||||||
|
let csvString = "abc,def,ghi"
|
||||||
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
|
for record in AnyIterator(csv) {
|
||||||
|
XCTAssertEqual(record, ["abc", "def", "ghi"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTrimFields2() {
|
||||||
|
let csvString = " abc, def, ghi"
|
||||||
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
|
for record in AnyIterator(csv) {
|
||||||
|
XCTAssertEqual(record, ["abc", "def", "ghi"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTrimFields3() {
|
||||||
|
let csvString = "abc ,def ,ghi "
|
||||||
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
|
for record in AnyIterator(csv) {
|
||||||
|
XCTAssertEqual(record, ["abc", "def", "ghi"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTrimFields4() {
|
||||||
|
let csvString = " abc , def , ghi "
|
||||||
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
|
for record in AnyIterator(csv) {
|
||||||
|
XCTAssertEqual(record, ["abc", "def", "ghi"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTrimFields5() {
|
||||||
|
let csvString = "\"abc\",\"def\",\"ghi\""
|
||||||
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
|
for record in AnyIterator(csv) {
|
||||||
|
XCTAssertEqual(record, ["abc", "def", "ghi"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTrimFields6() {
|
||||||
|
let csvString = " \"abc\", \"def\", \"ghi\""
|
||||||
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
|
for record in AnyIterator(csv) {
|
||||||
|
XCTAssertEqual(record, ["abc", "def", "ghi"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTrimFields7() {
|
||||||
|
let csvString = "\"abc\" ,\"def\" ,\"ghi\" "
|
||||||
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
|
for record in AnyIterator(csv) {
|
||||||
|
XCTAssertEqual(record, ["abc", "def", "ghi"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTrimFields8() {
|
||||||
|
let csvString = " \"abc\" , \"def\" , \"ghi\" "
|
||||||
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
|
for record in AnyIterator(csv) {
|
||||||
|
XCTAssertEqual(record, ["abc", "def", "ghi"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTrimFields9() {
|
||||||
|
let csvString = "\" abc \",\" def \",\" ghi \""
|
||||||
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
|
for record in AnyIterator(csv) {
|
||||||
|
XCTAssertEqual(record, [" abc ", " def ", " ghi "])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTrimFields10() {
|
||||||
|
let csvString = "\tabc,\t\tdef\t,ghi\t"
|
||||||
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
|
for record in AnyIterator(csv) {
|
||||||
|
XCTAssertEqual(record, ["abc", "def", "ghi"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTrimFields11() {
|
||||||
|
let csvString = " abc \n def "
|
||||||
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
|
|
||||||
|
let record1 = csv.next()!
|
||||||
|
XCTAssertEqual(record1, ["abc"])
|
||||||
|
let record2 = csv.next()!
|
||||||
|
XCTAssertEqual(record2, ["def"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTrimFields12() {
|
||||||
|
let csvString = " \"abc \" \n \" def\" "
|
||||||
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
|
|
||||||
|
let record1 = csv.next()!
|
||||||
|
XCTAssertEqual(record1, ["abc "])
|
||||||
|
let record2 = csv.next()!
|
||||||
|
XCTAssertEqual(record2, [" def"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTrimFields13() {
|
||||||
|
let csvString = " abc \t\tdef\t ghi "
|
||||||
|
let csv = try! CSVReader(string: csvString, trimFields: true, delimiter: "\t")
|
||||||
|
for record in AnyIterator(csv) {
|
||||||
|
XCTAssertEqual(record, ["abc", "", "def", "ghi"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTrimFields14() {
|
||||||
|
let csvString = ""
|
||||||
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
|
let records = AnyIterator(csv).map { $0 }
|
||||||
|
|
||||||
|
XCTAssertEqual(records.count, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTrimFields15() {
|
||||||
|
let csvString = " "
|
||||||
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
|
let records = AnyIterator(csv).map { $0 }
|
||||||
|
|
||||||
|
XCTAssertEqual(records.count, 1)
|
||||||
|
XCTAssertEqual(records[0], [""])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTrimFields16() {
|
||||||
|
let csvString = " , "
|
||||||
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
|
let records = AnyIterator(csv).map { $0 }
|
||||||
|
|
||||||
|
XCTAssertEqual(records.count, 1)
|
||||||
|
XCTAssertEqual(records[0], ["", ""])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTrimFields17() {
|
||||||
|
let csvString = " , \n"
|
||||||
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
|
let records = AnyIterator(csv).map { $0 }
|
||||||
|
|
||||||
|
XCTAssertEqual(records.count, 1)
|
||||||
|
XCTAssertEqual(records[0], ["", ""])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTrimFields18() {
|
||||||
|
let csvString = " , \n "
|
||||||
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
|
let records = AnyIterator(csv).map { $0 }
|
||||||
|
|
||||||
|
XCTAssertEqual(records.count, 2)
|
||||||
|
XCTAssertEqual(records[0], ["", ""])
|
||||||
|
XCTAssertEqual(records[1], [""])
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
//
|
||||||
|
// UnicodeTests.swift
|
||||||
|
// CSV
|
||||||
|
//
|
||||||
|
// Created by Yasuhiro Hatta on 2016/10/18.
|
||||||
|
// Copyright © 2016 yaslab. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
@testable import CSV
|
||||||
|
|
||||||
|
class UnicodeTests: XCTestCase {
|
||||||
|
|
||||||
|
func testUTF8WithBOM() {
|
||||||
|
let csvString = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\","
|
||||||
|
let encoding = String.Encoding.utf8
|
||||||
|
var mutableData = Data()
|
||||||
|
mutableData.append(contentsOf: UnicodeBOM.utf8)
|
||||||
|
mutableData.append(csvString.data(using: encoding)!)
|
||||||
|
let stream = InputStream(data: mutableData)
|
||||||
|
let csv = try! CSVReader(stream: stream, codecType: UTF8.self)
|
||||||
|
let records = getRecords(csv: csv)
|
||||||
|
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
|
||||||
|
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUTF16WithNativeEndianBOM() {
|
||||||
|
let csvString = "abab,,cdcd,efef\r\nzxcv,😆asdf,\"qw\"\"er\","
|
||||||
|
let encoding = String.Encoding.utf16
|
||||||
|
var mutableData = Data()
|
||||||
|
mutableData.append(csvString.data(using: encoding)!)
|
||||||
|
let stream = InputStream(data: mutableData as Data)
|
||||||
|
let csv = try! CSVReader(stream: stream, codecType: UTF16.self, endian: .unknown)
|
||||||
|
let records = getRecords(csv: csv)
|
||||||
|
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
|
||||||
|
XCTAssertEqual(records[1], ["zxcv", "😆asdf", "qw\"er", ""])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUTF16WithBigEndianBOM() {
|
||||||
|
let csvString = "abab,,cdcd,efef\r\n😆zxcv,asdf,\"qw\"\"er\","
|
||||||
|
let encoding = String.Encoding.utf16BigEndian
|
||||||
|
var mutableData = Data()
|
||||||
|
mutableData.append(contentsOf: UnicodeBOM.utf16BE)
|
||||||
|
mutableData.append(csvString.data(using: encoding)!)
|
||||||
|
let stream = InputStream(data: mutableData as Data)
|
||||||
|
let csv = try! CSVReader(stream: stream, codecType: UTF16.self, endian: .big)
|
||||||
|
let records = getRecords(csv: csv)
|
||||||
|
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
|
||||||
|
XCTAssertEqual(records[1], ["😆zxcv", "asdf", "qw\"er", ""])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUTF16WithLittleEndianBOM() {
|
||||||
|
let csvString = "abab,,cdcd,efef\r\nzxcv😆,asdf,\"qw\"\"er\","
|
||||||
|
let encoding = String.Encoding.utf16LittleEndian
|
||||||
|
var mutableData = Data()
|
||||||
|
mutableData.append(contentsOf: UnicodeBOM.utf16LE)
|
||||||
|
mutableData.append(csvString.data(using: encoding)!)
|
||||||
|
let stream = InputStream(data: mutableData as Data)
|
||||||
|
let csv = try! CSVReader(stream: stream, codecType: UTF16.self, endian: .little)
|
||||||
|
let records = getRecords(csv: csv)
|
||||||
|
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
|
||||||
|
XCTAssertEqual(records[1], ["zxcv😆", "asdf", "qw\"er", ""])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUTF32WithNativeEndianBOM() {
|
||||||
|
let csvString = "😆abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\","
|
||||||
|
let encoding = String.Encoding.utf32
|
||||||
|
var mutableData = Data()
|
||||||
|
mutableData.append(csvString.data(using: encoding)!)
|
||||||
|
let stream = InputStream(data: mutableData as Data)
|
||||||
|
let csv = try! CSVReader(stream: stream, codecType: UTF32.self, endian: .unknown)
|
||||||
|
let records = getRecords(csv: csv)
|
||||||
|
XCTAssertEqual(records[0], ["😆abab", "", "cdcd", "efef"])
|
||||||
|
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUTF32WithBigEndianBOM() {
|
||||||
|
let csvString = "abab,,cd😆cd,efef\r\nzxcv,asdf,\"qw\"\"er\","
|
||||||
|
let encoding = String.Encoding.utf32BigEndian
|
||||||
|
var mutableData = Data()
|
||||||
|
mutableData.append(contentsOf: UnicodeBOM.utf32BE)
|
||||||
|
mutableData.append(csvString.data(using: encoding)!)
|
||||||
|
let stream = InputStream(data: mutableData as Data)
|
||||||
|
let csv = try! CSVReader(stream: stream, codecType: UTF32.self, endian: .big)
|
||||||
|
let records = getRecords(csv: csv)
|
||||||
|
XCTAssertEqual(records[0], ["abab", "", "cd😆cd", "efef"])
|
||||||
|
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUTF32WithLittleEndianBOM() {
|
||||||
|
let csvString = "abab,,cdcd,ef😆ef\r\nzxcv,asdf,\"qw\"\"er\","
|
||||||
|
let encoding = String.Encoding.utf32LittleEndian
|
||||||
|
var mutableData = Data()
|
||||||
|
mutableData.append(contentsOf: UnicodeBOM.utf32LE)
|
||||||
|
mutableData.append(csvString.data(using: encoding)!)
|
||||||
|
let stream = InputStream(data: mutableData as Data)
|
||||||
|
let csv = try! CSVReader(stream: stream, codecType: UTF32.self, endian: .little)
|
||||||
|
let records = getRecords(csv: csv)
|
||||||
|
XCTAssertEqual(records[0], ["abab", "", "cdcd", "ef😆ef"])
|
||||||
|
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
|
||||||
|
}
|
||||||
|
|
||||||
|
private func getRecords(csv: CSVReader) -> [[String]] {
|
||||||
|
return csv.map { $0 }
|
||||||
|
// var records = [[String]]()
|
||||||
|
// try! csv.enumerateRows { (record, _, _) in
|
||||||
|
// records.append(record)
|
||||||
|
// }
|
||||||
|
// return records
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
//
|
||||||
|
// Version1Tests.swift
|
||||||
|
// CSV
|
||||||
|
//
|
||||||
|
// Created by Yasuhiro Hatta on 2017/06/18.
|
||||||
|
// Copyright © 2017 yaslab. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
import CSV
|
||||||
|
|
||||||
|
class Version1Tests: XCTestCase {
|
||||||
|
|
||||||
|
func testV1() {
|
||||||
|
let str = "a,b,c\n1,2,3"
|
||||||
|
let data8 = str.data(using: .utf8)!
|
||||||
|
let data16 = str.data(using: .utf16BigEndian)!
|
||||||
|
let data32 = str.data(using: .utf32BigEndian)!
|
||||||
|
|
||||||
|
let headerRow = ["a", "b", "c"]
|
||||||
|
let row = ["1", "2", "3"]
|
||||||
|
|
||||||
|
do {
|
||||||
|
let stream = InputStream(data: data8)
|
||||||
|
let csv = try CSVReader(stream: stream,
|
||||||
|
codecType: UTF8.self,
|
||||||
|
hasHeaderRow: true,
|
||||||
|
trimFields: false,
|
||||||
|
delimiter: ",")
|
||||||
|
XCTAssertEqual(csv.headerRow!, headerRow)
|
||||||
|
XCTAssertEqual(csv.next()!, row)
|
||||||
|
XCTAssertEqual(csv["a"], row[0])
|
||||||
|
} catch {
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let stream = InputStream(data: data16)
|
||||||
|
let csv = try CSVReader(stream: stream,
|
||||||
|
codecType: UTF16.self,
|
||||||
|
endian: .big,
|
||||||
|
hasHeaderRow: true,
|
||||||
|
trimFields: false,
|
||||||
|
delimiter: ",")
|
||||||
|
XCTAssertEqual(csv.headerRow!, headerRow)
|
||||||
|
XCTAssertEqual(csv.next()!, row)
|
||||||
|
XCTAssertEqual(csv["a"], row[0])
|
||||||
|
} catch {
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let stream = InputStream(data: data32)
|
||||||
|
let csv = try CSVReader(stream: stream,
|
||||||
|
codecType: UTF32.self,
|
||||||
|
endian: .big,
|
||||||
|
hasHeaderRow: true,
|
||||||
|
trimFields: false,
|
||||||
|
delimiter: ",")
|
||||||
|
XCTAssertEqual(csv.headerRow!, headerRow)
|
||||||
|
XCTAssertEqual(csv.next()!, row)
|
||||||
|
XCTAssertEqual(csv["a"], row[0])
|
||||||
|
} catch {
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let stream = InputStream(data: data8)
|
||||||
|
let csv = try CSVReader(stream: stream,
|
||||||
|
hasHeaderRow: true,
|
||||||
|
trimFields: false,
|
||||||
|
delimiter: ",")
|
||||||
|
XCTAssertEqual(csv.headerRow!, headerRow)
|
||||||
|
XCTAssertEqual(csv.next()!, row)
|
||||||
|
XCTAssertEqual(csv["a"], row[0])
|
||||||
|
} catch {
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let csv = try CSVReader(string: str,
|
||||||
|
hasHeaderRow: true,
|
||||||
|
trimFields: false,
|
||||||
|
delimiter: ",")
|
||||||
|
XCTAssertEqual(csv.headerRow!, headerRow)
|
||||||
|
XCTAssertEqual(csv.next()!, row)
|
||||||
|
XCTAssertEqual(csv["a"], row[0])
|
||||||
|
} catch {
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = CSVError.cannotOpenFile
|
||||||
|
_ = CSVError.cannotReadFile
|
||||||
|
_ = CSVError.streamErrorHasOccurred(error: NSError(domain: "", code: 0, userInfo: nil))
|
||||||
|
_ = CSVError.cannotReadHeaderRow
|
||||||
|
_ = CSVError.stringEncodingMismatch
|
||||||
|
_ = CSVError.stringEndianMismatch
|
||||||
|
|
||||||
|
_ = Endian.big
|
||||||
|
_ = Endian.little
|
||||||
|
_ = Endian.unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,196 @@
|
||||||
|
#if !canImport(ObjectiveC)
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
extension BinaryReaderTests {
|
||||||
|
// DO NOT MODIFY: This is autogenerated, use:
|
||||||
|
// `swift test --generate-linuxmain`
|
||||||
|
// to regenerate.
|
||||||
|
static let __allTests__BinaryReaderTests = [
|
||||||
|
("testReadUInt16BEWithSmallBuffer", testReadUInt16BEWithSmallBuffer),
|
||||||
|
("testReadUInt16LEWithSmallBuffer", testReadUInt16LEWithSmallBuffer),
|
||||||
|
("testReadUInt32BEWithSmallBuffer", testReadUInt32BEWithSmallBuffer),
|
||||||
|
("testReadUInt32LEWithSmallBuffer", testReadUInt32LEWithSmallBuffer),
|
||||||
|
("testReadUInt8WithSmallBuffer", testReadUInt8WithSmallBuffer),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CSVReaderTests {
|
||||||
|
// DO NOT MODIFY: This is autogenerated, use:
|
||||||
|
// `swift test --generate-linuxmain`
|
||||||
|
// to regenerate.
|
||||||
|
static let __allTests__CSVReaderTests = [
|
||||||
|
("testCommaInQuotationMarks", testCommaInQuotationMarks),
|
||||||
|
("testCSVState1", testCSVState1),
|
||||||
|
("testDoubleQuoteBeforeLineBreak1", testDoubleQuoteBeforeLineBreak1),
|
||||||
|
("testDoubleQuoteBeforeLineBreak2", testDoubleQuoteBeforeLineBreak2),
|
||||||
|
("testEmptyField", testEmptyField),
|
||||||
|
("testEscapedQuotationMark1", testEscapedQuotationMark1),
|
||||||
|
("testEscapedQuotationMark2", testEscapedQuotationMark2),
|
||||||
|
("testHasHeaderRow1", testHasHeaderRow1),
|
||||||
|
("testHasHeaderRow2", testHasHeaderRow2),
|
||||||
|
("testHasHeaderRow3", testHasHeaderRow3),
|
||||||
|
("testHasHeaderRow4", testHasHeaderRow4),
|
||||||
|
("testLastLineIsEmpty", testLastLineIsEmpty),
|
||||||
|
("testLastLineIsWhiteSpace", testLastLineIsWhiteSpace),
|
||||||
|
("testMiddleLineIsEmpty", testMiddleLineIsEmpty),
|
||||||
|
("testOneLine", testOneLine),
|
||||||
|
("testSubscript1", testSubscript1),
|
||||||
|
("testSubscript2", testSubscript2),
|
||||||
|
("testSubscriptInt", testSubscriptInt),
|
||||||
|
("testToArray", testToArray),
|
||||||
|
("testTwoLines", testTwoLines),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CSVRowDecoderTests {
|
||||||
|
// DO NOT MODIFY: This is autogenerated, use:
|
||||||
|
// `swift test --generate-linuxmain`
|
||||||
|
// to regenerate.
|
||||||
|
static let __allTests__CSVRowDecoderTests = [
|
||||||
|
("testBoolDecodingStrategy_custom", testBoolDecodingStrategy_custom),
|
||||||
|
("testBoolDecodingStrategy_default", testBoolDecodingStrategy_default),
|
||||||
|
("testDataDecodingStrategy_base64", testDataDecodingStrategy_base64),
|
||||||
|
("testDataDecodingStrategy_custom", testDataDecodingStrategy_custom),
|
||||||
|
("testDateDecodingStrategy_custom", testDateDecodingStrategy_custom),
|
||||||
|
("testDateDecodingStrategy_deferredToDate", testDateDecodingStrategy_deferredToDate),
|
||||||
|
("testDateDecodingStrategy_formatted", testDateDecodingStrategy_formatted),
|
||||||
|
("testDateDecodingStrategy_iso8601", testDateDecodingStrategy_iso8601),
|
||||||
|
("testDateDecodingStrategy_millisecondsSince1970", testDateDecodingStrategy_millisecondsSince1970),
|
||||||
|
("testDateDecodingStrategy_secondsSince1970", testDateDecodingStrategy_secondsSince1970),
|
||||||
|
("testDecodeFloat", testDecodeFloat),
|
||||||
|
("testDecodeInteger", testDecodeInteger),
|
||||||
|
("testFoundationDecoding", testFoundationDecoding),
|
||||||
|
("testIntCodingKey", testIntCodingKey),
|
||||||
|
("testIntCodingKeyWhileIgnoringHeaders", testIntCodingKeyWhileIgnoringHeaders),
|
||||||
|
("testNoHeader", testNoHeader),
|
||||||
|
("testNumberOfFieldsIsSmall", testNumberOfFieldsIsSmall),
|
||||||
|
("testStringCodingKey", testStringCodingKey),
|
||||||
|
("testTypeInvalidDateFormat", testTypeInvalidDateFormat),
|
||||||
|
("testTypeMismatch", testTypeMismatch),
|
||||||
|
("testUnsupportedDecodableField", testUnsupportedDecodableField),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CSVWriterTests {
|
||||||
|
// DO NOT MODIFY: This is autogenerated, use:
|
||||||
|
// `swift test --generate-linuxmain`
|
||||||
|
// to regenerate.
|
||||||
|
static let __allTests__CSVWriterTests = [
|
||||||
|
("testDelimiter", testDelimiter),
|
||||||
|
("testEscapeQuote", testEscapeQuote),
|
||||||
|
("testEscapeQuoteAutomatically", testEscapeQuoteAutomatically),
|
||||||
|
("testMultipleFieldMultipleRecord", testMultipleFieldMultipleRecord),
|
||||||
|
("testMultipleFieldSingleRecord", testMultipleFieldSingleRecord),
|
||||||
|
("testNewline", testNewline),
|
||||||
|
("testQuoted", testQuoted),
|
||||||
|
("testQuotedNewline", testQuotedNewline),
|
||||||
|
("testReadme", testReadme),
|
||||||
|
("testSingleFieldMultipleRecord", testSingleFieldMultipleRecord),
|
||||||
|
("testSingleFieldSingleRecord", testSingleFieldSingleRecord),
|
||||||
|
("testUTF16BE", testUTF16BE),
|
||||||
|
("testUTF16LE", testUTF16LE),
|
||||||
|
("testUTF32BE", testUTF32BE),
|
||||||
|
("testUTF32LE", testUTF32LE),
|
||||||
|
("testValueContainsComma", testValueContainsComma),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
extension LineBreakTests {
|
||||||
|
// DO NOT MODIFY: This is autogenerated, use:
|
||||||
|
// `swift test --generate-linuxmain`
|
||||||
|
// to regenerate.
|
||||||
|
static let __allTests__LineBreakTests = [
|
||||||
|
("testCRLF", testCRLF),
|
||||||
|
("testLastCR", testLastCR),
|
||||||
|
("testLastCRLF", testLastCRLF),
|
||||||
|
("testLastLF", testLastLF),
|
||||||
|
("testLF", testLF),
|
||||||
|
("testLFInQuotationMarks", testLFInQuotationMarks),
|
||||||
|
("testLineBreakCR", testLineBreakCR),
|
||||||
|
("testLineBreakCRCR", testLineBreakCRCR),
|
||||||
|
("testLineBreakCRLF", testLineBreakCRLF),
|
||||||
|
("testLineBreakCRLFCRLF", testLineBreakCRLFCRLF),
|
||||||
|
("testLineBreakLF", testLineBreakLF),
|
||||||
|
("testLineBreakLFLF", testLineBreakLFLF),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ReadmeTests {
|
||||||
|
// DO NOT MODIFY: This is autogenerated, use:
|
||||||
|
// `swift test --generate-linuxmain`
|
||||||
|
// to regenerate.
|
||||||
|
static let __allTests__ReadmeTests = [
|
||||||
|
("testFromCSVString", testFromCSVString),
|
||||||
|
("testFromFile", testFromFile),
|
||||||
|
("testGetTheFieldValueUsingKey", testGetTheFieldValueUsingKey),
|
||||||
|
("testGettingTheHeaderRow", testGettingTheHeaderRow),
|
||||||
|
("testProvideTheCharacterEncoding", testProvideTheCharacterEncoding),
|
||||||
|
("testWriteToFile", testWriteToFile),
|
||||||
|
("testWriteToMemory", testWriteToMemory),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TrimFieldsTests {
|
||||||
|
// DO NOT MODIFY: This is autogenerated, use:
|
||||||
|
// `swift test --generate-linuxmain`
|
||||||
|
// to regenerate.
|
||||||
|
static let __allTests__TrimFieldsTests = [
|
||||||
|
("testTrimFields1", testTrimFields1),
|
||||||
|
("testTrimFields10", testTrimFields10),
|
||||||
|
("testTrimFields11", testTrimFields11),
|
||||||
|
("testTrimFields12", testTrimFields12),
|
||||||
|
("testTrimFields13", testTrimFields13),
|
||||||
|
("testTrimFields14", testTrimFields14),
|
||||||
|
("testTrimFields15", testTrimFields15),
|
||||||
|
("testTrimFields16", testTrimFields16),
|
||||||
|
("testTrimFields17", testTrimFields17),
|
||||||
|
("testTrimFields18", testTrimFields18),
|
||||||
|
("testTrimFields2", testTrimFields2),
|
||||||
|
("testTrimFields3", testTrimFields3),
|
||||||
|
("testTrimFields4", testTrimFields4),
|
||||||
|
("testTrimFields5", testTrimFields5),
|
||||||
|
("testTrimFields6", testTrimFields6),
|
||||||
|
("testTrimFields7", testTrimFields7),
|
||||||
|
("testTrimFields8", testTrimFields8),
|
||||||
|
("testTrimFields9", testTrimFields9),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UnicodeTests {
|
||||||
|
// DO NOT MODIFY: This is autogenerated, use:
|
||||||
|
// `swift test --generate-linuxmain`
|
||||||
|
// to regenerate.
|
||||||
|
static let __allTests__UnicodeTests = [
|
||||||
|
("testUTF16WithBigEndianBOM", testUTF16WithBigEndianBOM),
|
||||||
|
("testUTF16WithLittleEndianBOM", testUTF16WithLittleEndianBOM),
|
||||||
|
("testUTF16WithNativeEndianBOM", testUTF16WithNativeEndianBOM),
|
||||||
|
("testUTF32WithBigEndianBOM", testUTF32WithBigEndianBOM),
|
||||||
|
("testUTF32WithLittleEndianBOM", testUTF32WithLittleEndianBOM),
|
||||||
|
("testUTF32WithNativeEndianBOM", testUTF32WithNativeEndianBOM),
|
||||||
|
("testUTF8WithBOM", testUTF8WithBOM),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Version1Tests {
|
||||||
|
// DO NOT MODIFY: This is autogenerated, use:
|
||||||
|
// `swift test --generate-linuxmain`
|
||||||
|
// to regenerate.
|
||||||
|
static let __allTests__Version1Tests = [
|
||||||
|
("testV1", testV1),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
public func __allTests() -> [XCTestCaseEntry] {
|
||||||
|
return [
|
||||||
|
testCase(BinaryReaderTests.__allTests__BinaryReaderTests),
|
||||||
|
testCase(CSVReaderTests.__allTests__CSVReaderTests),
|
||||||
|
testCase(CSVRowDecoderTests.__allTests__CSVRowDecoderTests),
|
||||||
|
testCase(CSVWriterTests.__allTests__CSVWriterTests),
|
||||||
|
testCase(LineBreakTests.__allTests__LineBreakTests),
|
||||||
|
testCase(ReadmeTests.__allTests__ReadmeTests),
|
||||||
|
testCase(TrimFieldsTests.__allTests__TrimFieldsTests),
|
||||||
|
testCase(UnicodeTests.__allTests__UnicodeTests),
|
||||||
|
testCase(Version1Tests.__allTests__Version1Tests),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -1,14 +1,8 @@
|
||||||
//
|
|
||||||
// LinuxMain.swift
|
|
||||||
// CSV
|
|
||||||
//
|
|
||||||
// Created by Yasuhiro Hatta on 2016/06/11.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
import XCTest
|
import XCTest
|
||||||
@testable import CSVTestSuite
|
|
||||||
|
|
||||||
XCTMain([
|
import CSVTests
|
||||||
testCase(CSVReaderTests.allTests),
|
|
||||||
])
|
var tests = [XCTestCaseEntry]()
|
||||||
|
tests += CSVTests.__allTests()
|
||||||
|
|
||||||
|
XCTMain(tests)
|
||||||
|
|
Loading…
Reference in New Issue