Compare commits
241 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 |
|
@ -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
|
||||
osx_image: xcode7.3
|
||||
env:
|
||||
- LC_CTYPE=en_US.UTF-8
|
||||
git:
|
||||
submodules: false
|
||||
before_install:
|
||||
- git submodule update --init --recursive
|
||||
language: swift
|
||||
osx_image: xcode11.3
|
||||
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:
|
||||
email: false
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'CSV.swift'
|
||||
s.version = '0.1.1'
|
||||
s.license = 'MIT'
|
||||
s.summary = 'CSV reading library written in Swift.'
|
||||
s.homepage = 'https://github.com/yaslab/CSV.swift'
|
||||
s.authors = { 'Yasuhiro Hatta' => 'hatta.yasuhiro@gmail.com' }
|
||||
s.source = { :git => 'https://github.com/yaslab/CSV.swift.git', :tag => s.version }
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = 'CSV.swift'
|
||||
spec.version = '2.4.3'
|
||||
spec.license = { :type => 'MIT' }
|
||||
spec.homepage = 'https://github.com/yaslab/CSV.swift'
|
||||
spec.authors = { 'Yasuhiro Hatta' => 'hatta.yasuhiro@gmail.com' }
|
||||
spec.summary = 'CSV reading and writing library written in Swift.'
|
||||
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'
|
||||
s.ios.deployment_target = '8.0'
|
||||
s.tvos.deployment_target = '9.0'
|
||||
s.watchos.deployment_target = '2.0'
|
||||
spec.ios.deployment_target = '8.0'
|
||||
spec.tvos.deployment_target = '9.0'
|
||||
spec.watchos.deployment_target = '2.0'
|
||||
spec.osx.deployment_target = '10.9'
|
||||
|
||||
s.module_name = 'CSV'
|
||||
s.source_files = 'Sources/*.swift'
|
||||
spec.module_name = 'CSV'
|
||||
spec.swift_version = '5.0'
|
||||
end
|
||||
|
|
|
@ -7,31 +7,72 @@
|
|||
objects = {
|
||||
|
||||
/* 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 */; };
|
||||
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, ); }; };
|
||||
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, ); }; };
|
||||
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, ); }; };
|
||||
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, ); }; };
|
||||
0E7E8D031D0BCDDD0057A1C1 /* CSVReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8CA61D0BC8050057A1C1 /* CSVReaderTests.swift */; };
|
||||
0E7E8D041D0BCDDD0057A1C1 /* CSVTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8CA71D0BC8050057A1C1 /* CSVTests.swift */; };
|
||||
0E7F657B1EF6437E00E1E1A0 /* Version1Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7F657A1EF6437E00E1E1A0 /* Version1Tests.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 */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
@ -40,14 +81,14 @@
|
|||
containerPortal = 0E7E8C781D0BC7BB0057A1C1 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 0E7E8C801D0BC7BB0057A1C1;
|
||||
remoteInfo = CSV;
|
||||
remoteInfo = "CSV-iOS";
|
||||
};
|
||||
0E7E8CD11D0BCA2A0057A1C1 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 0E7E8C781D0BC7BB0057A1C1 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 0E7E8CC51D0BCA2A0057A1C1;
|
||||
remoteInfo = "CSV-OSX";
|
||||
remoteInfo = "CSV-macOS";
|
||||
};
|
||||
0E7E8CF31D0BCD0B0057A1C1 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
|
@ -59,21 +100,33 @@
|
|||
/* End PBXContainerItemProxy 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; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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; };
|
||||
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; };
|
||||
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 */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -140,6 +193,7 @@
|
|||
0E7E8C821D0BC7BB0057A1C1 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
usesTabs = 0;
|
||||
};
|
||||
0E7E8C821D0BC7BB0057A1C1 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
|
@ -148,7 +202,7 @@
|
|||
0E7E8C8B1D0BC7BB0057A1C1 /* CSVTests-iOS.xctest */,
|
||||
0E7E8CB51D0BC98B0057A1C1 /* CSV.framework */,
|
||||
0E7E8CC61D0BCA2A0057A1C1 /* CSV.framework */,
|
||||
0E7E8CCF1D0BCA2A0057A1C1 /* CSVTests-OSX.xctest */,
|
||||
0E7E8CCF1D0BCA2A0057A1C1 /* CSVTests-macOS.xctest */,
|
||||
0E7E8CE81D0BCD0B0057A1C1 /* CSV.framework */,
|
||||
0E7E8CF11D0BCD0B0057A1C1 /* CSVTests-tvOS.xctest */,
|
||||
);
|
||||
|
@ -158,9 +212,7 @@
|
|||
0E7E8C9B1D0BC7F10057A1C1 /* Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0E7E8C9C1D0BC7F10057A1C1 /* ByteOrder.swift */,
|
||||
0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */,
|
||||
0E7E8C9E1D0BC7F10057A1C1 /* CSVError.swift */,
|
||||
0E959C5A208B611F005F8D01 /* CSV */,
|
||||
0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */,
|
||||
0E7E8CAC1D0BC8610057A1C1 /* Info.plist */,
|
||||
);
|
||||
|
@ -170,21 +222,43 @@
|
|||
0E7E8CA41D0BC8050057A1C1 /* Tests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0E7E8CA51D0BC8050057A1C1 /* CSV */,
|
||||
0EA37AC81DD8C0B900F5B274 /* CSVTests */,
|
||||
0E7E8CAE1D0BC8690057A1C1 /* Info.plist */,
|
||||
);
|
||||
path = Tests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0E7E8CA51D0BC8050057A1C1 /* CSV */ = {
|
||||
0E959C5A208B611F005F8D01 /* CSV */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0E7E8CA61D0BC8050057A1C1 /* CSVReaderTests.swift */,
|
||||
0E7E8CA71D0BC8050057A1C1 /* CSVTests.swift */,
|
||||
0E959C5E208B611F005F8D01 /* BinaryReader.swift */,
|
||||
0E959C5D208B611F005F8D01 /* Legacy.swift */,
|
||||
0E959C5B208B611F005F8D01 /* CSVError.swift */,
|
||||
0E959C61208B611F005F8D01 /* CSVReader.swift */,
|
||||
0E87607E219FF43B00C6C7FA /* CSVRowDecoder.swift */,
|
||||
0E959C5C208B611F005F8D01 /* CSVWriter.swift */,
|
||||
0E959C60208B611F005F8D01 /* Endian.swift */,
|
||||
0E959C5F208B611F005F8D01 /* UnicodeIterator.swift */,
|
||||
);
|
||||
path = CSV;
|
||||
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 */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
|
@ -255,7 +329,7 @@
|
|||
0E7E8C8E1D0BC7BB0057A1C1 /* PBXTargetDependency */,
|
||||
);
|
||||
name = "CSVTests-iOS";
|
||||
productName = CSVTests;
|
||||
productName = "CSVTests-iOS";
|
||||
productReference = 0E7E8C8B1D0BC7BB0057A1C1 /* CSVTests-iOS.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
|
@ -277,10 +351,11 @@
|
|||
productReference = 0E7E8CB51D0BC98B0057A1C1 /* CSV.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
0E7E8CC51D0BCA2A0057A1C1 /* CSV-OSX */ = {
|
||||
0E7E8CC51D0BCA2A0057A1C1 /* CSV-macOS */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 0E7E8CD71D0BCA2A0057A1C1 /* Build configuration list for PBXNativeTarget "CSV-OSX" */;
|
||||
buildConfigurationList = 0E7E8CD71D0BCA2A0057A1C1 /* Build configuration list for PBXNativeTarget "CSV-macOS" */;
|
||||
buildPhases = (
|
||||
0E47EEBF1DBBC05700EBF783 /* Run Script () */,
|
||||
0E7E8CC11D0BCA2A0057A1C1 /* Sources */,
|
||||
0E7E8CC21D0BCA2A0057A1C1 /* Frameworks */,
|
||||
0E7E8CC31D0BCA2A0057A1C1 /* Headers */,
|
||||
|
@ -290,14 +365,14 @@
|
|||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "CSV-OSX";
|
||||
productName = "CSV-OSX";
|
||||
name = "CSV-macOS";
|
||||
productName = "CSV-macOS";
|
||||
productReference = 0E7E8CC61D0BCA2A0057A1C1 /* CSV.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
0E7E8CCE1D0BCA2A0057A1C1 /* CSVTests-OSX */ = {
|
||||
0E7E8CCE1D0BCA2A0057A1C1 /* CSVTests-macOS */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 0E7E8CDA1D0BCA2A0057A1C1 /* Build configuration list for PBXNativeTarget "CSVTests-OSX" */;
|
||||
buildConfigurationList = 0E7E8CDA1D0BCA2A0057A1C1 /* Build configuration list for PBXNativeTarget "CSVTests-macOS" */;
|
||||
buildPhases = (
|
||||
0E7E8CCB1D0BCA2A0057A1C1 /* Sources */,
|
||||
0E7E8CCC1D0BCA2A0057A1C1 /* Frameworks */,
|
||||
|
@ -308,9 +383,9 @@
|
|||
dependencies = (
|
||||
0E7E8CD21D0BCA2A0057A1C1 /* PBXTargetDependency */,
|
||||
);
|
||||
name = "CSVTests-OSX";
|
||||
productName = "CSV-OSXTests";
|
||||
productReference = 0E7E8CCF1D0BCA2A0057A1C1 /* CSVTests-OSX.xctest */;
|
||||
name = "CSVTests-macOS";
|
||||
productName = "CSVTests-macOS";
|
||||
productReference = 0E7E8CCF1D0BCA2A0057A1C1 /* CSVTests-macOS.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
0E7E8CE71D0BCD0B0057A1C1 /* CSV-tvOS */ = {
|
||||
|
@ -345,7 +420,7 @@
|
|||
0E7E8CF41D0BCD0B0057A1C1 /* PBXTargetDependency */,
|
||||
);
|
||||
name = "CSVTests-tvOS";
|
||||
productName = "CSV-tvOSTests";
|
||||
productName = "CSVTests-tvOS";
|
||||
productReference = 0E7E8CF11D0BCD0B0057A1C1 /* CSVTests-tvOS.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
|
@ -355,50 +430,58 @@
|
|||
0E7E8C781D0BC7BB0057A1C1 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0730;
|
||||
LastUpgradeCheck = 0730;
|
||||
LastSwiftUpdateCheck = 1010;
|
||||
LastUpgradeCheck = 1020;
|
||||
ORGANIZATIONNAME = yaslab;
|
||||
TargetAttributes = {
|
||||
0E7E8C801D0BC7BB0057A1C1 = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
LastSwiftMigration = 1020;
|
||||
};
|
||||
0E7E8C8A1D0BC7BB0057A1C1 = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
LastSwiftMigration = 1020;
|
||||
};
|
||||
0E7E8CB41D0BC98B0057A1C1 = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
LastSwiftMigration = 0800;
|
||||
};
|
||||
0E7E8CC51D0BCA2A0057A1C1 = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
LastSwiftMigration = 1020;
|
||||
};
|
||||
0E7E8CCE1D0BCA2A0057A1C1 = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
LastSwiftMigration = 1020;
|
||||
};
|
||||
0E7E8CE71D0BCD0B0057A1C1 = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
LastSwiftMigration = 0800;
|
||||
};
|
||||
0E7E8CF01D0BCD0B0057A1C1 = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
LastSwiftMigration = 0930;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 0E7E8C7B1D0BC7BB0057A1C1 /* Build configuration list for PBXProject "CSV" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 0E7E8C771D0BC7BB0057A1C1;
|
||||
productRefGroup = 0E7E8C821D0BC7BB0057A1C1 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
0E7E8CC51D0BCA2A0057A1C1 /* CSV-OSX */,
|
||||
0E7E8CC51D0BCA2A0057A1C1 /* CSV-macOS */,
|
||||
0E7E8C801D0BC7BB0057A1C1 /* CSV-iOS */,
|
||||
0E7E8CE71D0BCD0B0057A1C1 /* CSV-tvOS */,
|
||||
0E7E8CB41D0BC98B0057A1C1 /* CSV-watchOS */,
|
||||
0E7E8CCE1D0BCA2A0057A1C1 /* CSVTests-OSX */,
|
||||
0E7E8CCE1D0BCA2A0057A1C1 /* CSVTests-macOS */,
|
||||
0E7E8C8A1D0BC7BB0057A1C1 /* CSVTests-iOS */,
|
||||
0E7E8CF01D0BCD0B0057A1C1 /* CSVTests-tvOS */,
|
||||
);
|
||||
|
@ -457,14 +540,36 @@
|
|||
};
|
||||
/* 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 */
|
||||
0E7E8C7C1D0BC7BB0057A1C1 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0E7E8CA11D0BC7F10057A1C1 /* CSV.swift in Sources */,
|
||||
0E7E8CA21D0BC7F10057A1C1 /* CSVError.swift in Sources */,
|
||||
0E7E8CA01D0BC7F10057A1C1 /* ByteOrder.swift in Sources */,
|
||||
0E959C63208B611F005F8D01 /* CSVError.swift in Sources */,
|
||||
0E876080219FF43B00C6C7FA /* CSVRowDecoder.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;
|
||||
};
|
||||
|
@ -472,8 +577,15 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0E7E8CAA1D0BC8050057A1C1 /* CSVTests.swift in Sources */,
|
||||
0E7E8CA91D0BC8050057A1C1 /* CSVReaderTests.swift in Sources */,
|
||||
0EDF8EDE1DDB73520068056A /* ReadmeTests.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;
|
||||
};
|
||||
|
@ -481,9 +593,14 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0E7E8CBE1D0BC9D70057A1C1 /* CSV.swift in Sources */,
|
||||
0E7E8CBF1D0BC9D70057A1C1 /* CSVError.swift in Sources */,
|
||||
0E7E8CBD1D0BC9D70057A1C1 /* ByteOrder.swift in Sources */,
|
||||
0E959C65208B611F005F8D01 /* CSVError.swift in Sources */,
|
||||
0E876082219FF43B00C6C7FA /* CSVRowDecoder.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;
|
||||
};
|
||||
|
@ -491,9 +608,14 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0E7E8CE01D0BCA8E0057A1C1 /* CSV.swift in Sources */,
|
||||
0E7E8CE11D0BCA8E0057A1C1 /* CSVError.swift in Sources */,
|
||||
0E7E8CDF1D0BCA8E0057A1C1 /* ByteOrder.swift in Sources */,
|
||||
0E959C62208B611F005F8D01 /* CSVError.swift in Sources */,
|
||||
0E87607F219FF43B00C6C7FA /* CSVRowDecoder.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;
|
||||
};
|
||||
|
@ -501,8 +623,15 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0E7E8CDE1D0BCA840057A1C1 /* CSVTests.swift in Sources */,
|
||||
0E7E8CDD1D0BCA840057A1C1 /* CSVReaderTests.swift in Sources */,
|
||||
0EDF8ED91DDB73520068056A /* ReadmeTests.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;
|
||||
};
|
||||
|
@ -510,9 +639,14 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0E7E8D001D0BCDCF0057A1C1 /* CSV.swift in Sources */,
|
||||
0E7E8D011D0BCDCF0057A1C1 /* CSVError.swift in Sources */,
|
||||
0E7E8CFF1D0BCDCF0057A1C1 /* ByteOrder.swift in Sources */,
|
||||
0E959C64208B611F005F8D01 /* CSVError.swift in Sources */,
|
||||
0E876081219FF43B00C6C7FA /* CSVRowDecoder.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;
|
||||
};
|
||||
|
@ -520,8 +654,15 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0E7E8D041D0BCDDD0057A1C1 /* CSVTests.swift in Sources */,
|
||||
0E7E8D031D0BCDDD0057A1C1 /* CSVReaderTests.swift in Sources */,
|
||||
0EDF8EE31DDB73530068056A /* ReadmeTests.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;
|
||||
};
|
||||
|
@ -535,7 +676,7 @@
|
|||
};
|
||||
0E7E8CD21D0BCA2A0057A1C1 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 0E7E8CC51D0BCA2A0057A1C1 /* CSV-OSX */;
|
||||
target = 0E7E8CC51D0BCA2A0057A1C1 /* CSV-macOS */;
|
||||
targetProxy = 0E7E8CD11D0BCA2A0057A1C1 /* PBXContainerItemProxy */;
|
||||
};
|
||||
0E7E8CF41D0BCD0B0057A1C1 /* PBXTargetDependency */ = {
|
||||
|
@ -550,18 +691,29 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = 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_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
|
@ -584,11 +736,13 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = 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;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
|
@ -599,18 +753,29 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = 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_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
|
@ -627,9 +792,11 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = 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;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
|
@ -640,6 +807,7 @@
|
|||
0E7E8C961D0BC7BB0057A1C1 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
|
@ -648,6 +816,7 @@
|
|||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
MARKETING_VERSION = 2.4.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV;
|
||||
PRODUCT_NAME = CSV;
|
||||
SKIP_INSTALL = YES;
|
||||
|
@ -657,6 +826,7 @@
|
|||
0E7E8C971D0BC7BB0057A1C1 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
|
@ -665,15 +835,18 @@
|
|||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
MARKETING_VERSION = 2.4.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV;
|
||||
PRODUCT_NAME = CSV;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
0E7E8C991D0BC7BB0057A1C1 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
INFOPLIST_FILE = Tests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-iOS";
|
||||
|
@ -684,10 +857,12 @@
|
|||
0E7E8C9A1D0BC7BB0057A1C1 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
INFOPLIST_FILE = Tests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-iOS";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
@ -695,6 +870,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
|
@ -702,6 +878,7 @@
|
|||
INFOPLIST_FILE = Sources/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
MARKETING_VERSION = 2.4.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV;
|
||||
PRODUCT_NAME = CSV;
|
||||
SDKROOT = watchos;
|
||||
|
@ -715,6 +892,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
|
@ -722,10 +900,12 @@
|
|||
INFOPLIST_FILE = Sources/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
MARKETING_VERSION = 2.4.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV;
|
||||
PRODUCT_NAME = CSV;
|
||||
SDKROOT = watchos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
TARGETED_DEVICE_FAMILY = 4;
|
||||
WATCHOS_DEPLOYMENT_TARGET = 2.0;
|
||||
};
|
||||
|
@ -745,6 +925,7 @@
|
|||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.9;
|
||||
MARKETING_VERSION = 2.4.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV;
|
||||
PRODUCT_NAME = CSV;
|
||||
SDKROOT = macosx;
|
||||
|
@ -766,22 +947,25 @@
|
|||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.9;
|
||||
MARKETING_VERSION = 2.4.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV;
|
||||
PRODUCT_NAME = CSV;
|
||||
SDKROOT = macosx;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
0E7E8CDB1D0BCA2A0057A1C1 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INFOPLIST_FILE = Tests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-OSX";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-macOS";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
|
@ -790,20 +974,23 @@
|
|||
0E7E8CDC1D0BCA2A0057A1C1 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INFOPLIST_FILE = Tests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-OSX";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-macOS";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
0E7E8CFA1D0BCD0B0057A1C1 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
|
@ -811,6 +998,7 @@
|
|||
INFOPLIST_FILE = Sources/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
MARKETING_VERSION = 2.4.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV;
|
||||
PRODUCT_NAME = CSV;
|
||||
SDKROOT = appletvos;
|
||||
|
@ -823,6 +1011,7 @@
|
|||
0E7E8CFB1D0BCD0B0057A1C1 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
|
@ -830,10 +1019,12 @@
|
|||
INFOPLIST_FILE = Sources/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
MARKETING_VERSION = 2.4.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV;
|
||||
PRODUCT_NAME = CSV;
|
||||
SDKROOT = appletvos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
TARGETED_DEVICE_FAMILY = 3;
|
||||
TVOS_DEPLOYMENT_TARGET = 9.0;
|
||||
};
|
||||
|
@ -842,6 +1033,7 @@
|
|||
0E7E8CFD1D0BCD0B0057A1C1 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
INFOPLIST_FILE = Tests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-tvOS";
|
||||
|
@ -854,11 +1046,13 @@
|
|||
0E7E8CFE1D0BCD0B0057A1C1 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
INFOPLIST_FILE = Tests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-tvOS";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = appletvos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
TVOS_DEPLOYMENT_TARGET = 9.2;
|
||||
};
|
||||
name = Release;
|
||||
|
@ -902,7 +1096,7 @@
|
|||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
0E7E8CD71D0BCA2A0057A1C1 /* Build configuration list for PBXNativeTarget "CSV-OSX" */ = {
|
||||
0E7E8CD71D0BCA2A0057A1C1 /* Build configuration list for PBXNativeTarget "CSV-macOS" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
0E7E8CD81D0BCA2A0057A1C1 /* Debug */,
|
||||
|
@ -911,7 +1105,7 @@
|
|||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
0E7E8CDA1D0BCA2A0057A1C1 /* Build configuration list for PBXNativeTarget "CSVTests-OSX" */ = {
|
||||
0E7E8CDA1D0BCA2A0057A1C1 /* Build configuration list for PBXNativeTarget "CSVTests-macOS" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
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"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0730"
|
||||
LastUpgradeVersion = "1020"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
@ -26,6 +26,7 @@
|
|||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
codeCoverageEnabled = "YES"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0730"
|
||||
LastUpgradeVersion = "1020"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
@ -16,7 +16,7 @@
|
|||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0E7E8CC51D0BCA2A0057A1C1"
|
||||
BuildableName = "CSV.framework"
|
||||
BlueprintName = "CSV-OSX"
|
||||
BlueprintName = "CSV-macOS"
|
||||
ReferencedContainer = "container:CSV.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
|
@ -26,6 +26,7 @@
|
|||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
codeCoverageEnabled = "YES"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
|
@ -33,8 +34,8 @@
|
|||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0E7E8CCE1D0BCA2A0057A1C1"
|
||||
BuildableName = "CSVTests-OSX.xctest"
|
||||
BlueprintName = "CSVTests-OSX"
|
||||
BuildableName = "CSVTests-macOS.xctest"
|
||||
BlueprintName = "CSVTests-macOS"
|
||||
ReferencedContainer = "container:CSV.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
|
@ -44,7 +45,7 @@
|
|||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0E7E8CC51D0BCA2A0057A1C1"
|
||||
BuildableName = "CSV.framework"
|
||||
BlueprintName = "CSV-OSX"
|
||||
BlueprintName = "CSV-macOS"
|
||||
ReferencedContainer = "container:CSV.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
|
@ -66,7 +67,7 @@
|
|||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0E7E8CC51D0BCA2A0057A1C1"
|
||||
BuildableName = "CSV.framework"
|
||||
BlueprintName = "CSV-OSX"
|
||||
BlueprintName = "CSV-macOS"
|
||||
ReferencedContainer = "container:CSV.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
|
@ -84,7 +85,7 @@
|
|||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0E7E8CC51D0BCA2A0057A1C1"
|
||||
BuildableName = "CSV.framework"
|
||||
BlueprintName = "CSV-OSX"
|
||||
BlueprintName = "CSV-macOS"
|
||||
ReferencedContainer = "container:CSV.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0730"
|
||||
LastUpgradeVersion = "1020"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0730"
|
||||
LastUpgradeVersion = "1020"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
// swift-tools-version:5.0
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "CSV",
|
||||
targets: [],
|
||||
dependencies: []
|
||||
name: "CSV.swift",
|
||||
products: [
|
||||
.library(name: "CSV", targets: ["CSV"])
|
||||
],
|
||||
targets: [
|
||||
.target(name: "CSV"),
|
||||
.testTarget(name: "CSVTests", dependencies: ["CSV"])
|
||||
],
|
||||
swiftLanguageVersions: [.v5]
|
||||
)
|
||||
|
|
160
README.md
160
README.md
|
@ -1,30 +1,38 @@
|
|||
# CSV.swift
|
||||
|
||||
[](https://travis-ci.org/yaslab/CSV.swift)
|
||||
[](https://codecov.io/gh/yaslab/CSV.swift)
|
||||
[](https://www.codetriage.com/yaslab/csv.swift)
|
||||
|
||||
CSV reading library written in Swift.
|
||||
CSV reading and writing library written in Swift.
|
||||
|
||||
## Usage
|
||||
## Usage for reading CSV
|
||||
|
||||
### From CSV string
|
||||
### From string
|
||||
|
||||
```swift
|
||||
import CSV
|
||||
|
||||
let csvString = "1,\"foo\"\n2,\"bar\""
|
||||
for row in try! CSV(string: csvString) {
|
||||
let csvString = "1,foo\n2,bar"
|
||||
let csv = try! CSVReader(string: csvString)
|
||||
while let row = csv.next() {
|
||||
print("\(row)")
|
||||
// => ["1", "foo"]
|
||||
// => ["2", "bar"]
|
||||
}
|
||||
// => ["1", "foo"]
|
||||
// => ["2", "bar"]
|
||||
```
|
||||
|
||||
### From file path
|
||||
### From file
|
||||
|
||||
NOTE: The default character encoding is `UTF8`.
|
||||
|
||||
```swift
|
||||
import Foundation
|
||||
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)")
|
||||
}
|
||||
```
|
||||
|
@ -32,14 +40,34 @@ for row in try! CSV(path: "/path/to/file.csv") {
|
|||
### Getting the header row
|
||||
|
||||
```swift
|
||||
let csv = try! CSV(
|
||||
path: "/path/to/file.csv",
|
||||
hasHeaderRow: true) // default: false
|
||||
import CSV
|
||||
|
||||
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"]
|
||||
|
||||
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"
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -48,9 +76,92 @@ for row in csv {
|
|||
If you use a file path, you can provide the character encoding to initializer.
|
||||
|
||||
```swift
|
||||
let csv = try! CSV(
|
||||
path: "/path/to/file.csv",
|
||||
encoding: NSUTF8StringEncoding)
|
||||
import Foundation
|
||||
import CSV
|
||||
|
||||
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
|
||||
|
@ -58,26 +169,19 @@ let csv = try! CSV(
|
|||
### CocoaPods
|
||||
|
||||
```ruby
|
||||
pod 'CSV.swift', '~> 0.1'
|
||||
pod 'CSV.swift', '~> 2.4.3'
|
||||
```
|
||||
|
||||
### Carthage
|
||||
|
||||
```
|
||||
github "yaslab/CSV.swift" ~> 0.1
|
||||
github "yaslab/CSV.swift" ~> 2.4.3
|
||||
```
|
||||
|
||||
### Swift Package Manager
|
||||
|
||||
```swift
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "PackageName",
|
||||
dependencies: [
|
||||
.Package(url: "https://github.com/yaslab/CSV.swift", majorVersion: 0, minor: 1)
|
||||
]
|
||||
)
|
||||
.package(url: "https://github.com/yaslab/CSV.swift.git", .upToNextMinor(from: "2.4.3"))
|
||||
```
|
||||
|
||||
## Reference specification
|
||||
|
@ -86,4 +190,4 @@ let package = Package(
|
|||
|
||||
## 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,395 +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 (c == CR || c == LF) && 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
|
||||
//
|
||||
// Created by Yasuhiro Hatta on 2016/06/11.
|
||||
//
|
||||
// Copyright © 2016 yaslab. All rights reserved.
|
||||
//
|
||||
|
||||
@import Foundation;
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.1.1</string>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<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,87 +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)
|
||||
}
|
||||
|
||||
func testSample5() {
|
||||
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
|
||||
@testable import CSVTestSuite
|
||||
|
||||
XCTMain([
|
||||
testCase(CSVReaderTests.allTests),
|
||||
])
|
||||
import CSVTests
|
||||
|
||||
var tests = [XCTestCaseEntry]()
|
||||
tests += CSVTests.__allTests()
|
||||
|
||||
XCTMain(tests)
|
||||
|
|
Loading…
Reference in New Issue