1+ import Foundation
2+
13extension ShadowEncoder {
2- ///
4+ /// Single value container for the CSV shadow encoder.
35 struct SingleValueContainer {
46 /// The representation of the encoding process point-in-time.
57 private let encoder : ShadowEncoder
@@ -18,6 +20,7 @@ extension ShadowEncoder {
1820 /// Creates a single value container only if the passed encoder's coding path is valid.
1921 /// - parameter encoder: The `Encoder` instance in charge of encoding CSV data.
2022 init ( encoder: ShadowEncoder ) {
23+ #warning("TODO")
2124 fatalError ( )
2225 }
2326
@@ -29,7 +32,7 @@ extension ShadowEncoder {
2932
3033extension ShadowEncoder . SingleValueContainer : SingleValueEncodingContainer {
3134 mutating func encode( _ value: String ) throws {
32- try self . lowlevelEncoding { $0 }
35+ try self . lowlevelEncoding { value }
3336 }
3437
3538 mutating func encodeNil( ) throws {
@@ -84,8 +87,7 @@ extension ShadowEncoder.SingleValueContainer: SingleValueEncodingContainer {
8487 let strategy = self . encoder. sink. configuration. floatStrategy
8588 try self . lowlevelEncoding {
8689 switch strategy {
87- case . throw:
88- throw DecodingError . invalidFloatingPoint ( value, codingPath: self . codingPath)
90+ case . throw: throw DecodingError . invalidFloatingPoint ( value, codingPath: self . codingPath)
8991 case . convert( let positiveInfinity, let negativeInfinity, let nan) :
9092 if value. isNaN {
9193 return nan
@@ -100,8 +102,7 @@ extension ShadowEncoder.SingleValueContainer: SingleValueEncodingContainer {
100102 let strategy = self . encoder. sink. configuration. floatStrategy
101103 try self . lowlevelEncoding {
102104 switch strategy {
103- case . throw:
104- throw DecodingError . invalidFloatingPoint ( value, codingPath: self . codingPath)
105+ case . throw: throw DecodingError . invalidFloatingPoint ( value, codingPath: self . codingPath)
105106 case . convert( let positiveInfinity, let negativeInfinity, let nan) :
106107 if value. isNaN {
107108 return nan
@@ -112,11 +113,74 @@ extension ShadowEncoder.SingleValueContainer: SingleValueEncodingContainer {
112113 }
113114 }
114115
115- mutating func encode< T> ( _ value: T ) throws where T : Encodable {
116- fatalError ( )
116+ mutating func encode< T> ( _ value: T ) throws where T: Encodable {
117+ switch value {
118+ case let date as Date : try self . encode ( date)
119+ case let data as Data : try self . encode ( data)
120+ case let num as Decimal : try self . encode ( num)
121+ case let url as URL : try self . encode ( url)
122+ default : try value. encode ( to: self . encoder)
123+ }
117124 }
118125}
119126
127+ internal extension ShadowEncoder . SingleValueContainer {
128+ /// Encodes a single value of the given type.
129+ /// - parameter value: The value to encode.
130+ mutating func encode( _ value: Date ) throws {
131+ switch self . encoder. sink. configuration. dateStrategy {
132+ case . deferredToDate:
133+ try value. encode ( to: self . encoder)
134+ case . secondsSince1970:
135+ try self . encode ( value. timeIntervalSince1970)
136+ case . millisecondsSince1970:
137+ try self . encode ( value. timeIntervalSince1970 * 1_000 )
138+ case . iso8601:
139+ let string = DateFormatter . iso8601. string ( from: value)
140+ try self . encode ( string)
141+ case . formatted( let formatter) :
142+ let string = formatter. string ( from: value)
143+ try self . encode ( string)
144+ case . custom( let closure) :
145+ try closure ( value, self . encoder)
146+ }
147+ }
148+
149+ /// Encodes a single value of the given type.
150+ /// - parameter value: The value to encode.
151+ mutating func encode( _ value: Data ) throws {
152+ switch self . encoder. sink. configuration. dataStrategy {
153+ case . deferredToData:
154+ try value. encode ( to: self . encoder)
155+ case . base64:
156+ try self . encode ( value. base64EncodedString ( ) )
157+ case . custom( let closure) :
158+ try closure ( value, self . encoder)
159+ }
160+ }
161+
162+ /// Encodes a single value of the given type.
163+ /// - parameter value: The value to encode.
164+ mutating func encode( _ value: Decimal ) throws {
165+ switch self . encoder. sink. configuration. decimalStrategy {
166+ case . locale( let locale) :
167+ var number = value
168+ let string = NSDecimalString ( & number, locale)
169+ try self . encode ( string)
170+ case . custom( let closure) :
171+ try closure ( value, self . encoder)
172+ }
173+ }
174+
175+ /// Encodes a single value of the given type.
176+ /// - parameter value: The value to encode.
177+ func encode( _ value: URL ) throws {
178+ try self . lowlevelEncoding { value. path }
179+ }
180+ }
181+
182+ // MARK: -
183+
120184extension ShadowEncoder . SingleValueContainer {
121185 /// CSV keyed container focus (i.e. where the container is able to operate on).
122186 private enum Focus {
@@ -129,14 +193,26 @@ extension ShadowEncoder.SingleValueContainer {
129193 }
130194
131195 /// Encodes a value by transforming it into a `String` through the closure and then passing it to the sink.
132- private func lowlevelEncoding< T> ( transform: ( T ) throws -> String ) throws {
133- fatalError ( )
196+ private func lowlevelEncoding( transform: ( ) throws -> String ) throws {
197+ let sink = self . encoder. sink
198+
199+ switch self . focus {
200+ case . field( let rowIndex, let fieldIndex) :
201+ let string = try transform ( )
202+ try sink. field ( value: string, at: rowIndex, fieldIndex)
203+ case . row( let rowIndex) :
204+ // Values are only allowed to be encoded directly from a single value container in "row level" if the CSV has single column rows.
205+ fatalError ( )
206+ case . file:
207+ // Values are only allowed to be decoded directly from a single value container in "file level" if the CSV file has a single row with a single column.
208+ fatalError ( )
209+ }
134210 }
135211}
136212
137213fileprivate extension DecodingError {
138214 /// Error raised when a non-conformant floating-point is being encoded and there is no support.
139- static func invalidFloatingPoint< T> ( _ value: T , codingPath: [ CodingKey ] ) -> DecodingError where T : BinaryFloatingPoint {
215+ static func invalidFloatingPoint< T: BinaryFloatingPoint > ( _ value: T , codingPath: [ CodingKey ] ) -> DecodingError {
140216 DecodingError . dataCorrupted (
141217 Context ( codingPath: codingPath,
142218 debugDescription: " The value ' \( value) ' is a non-conformant floating-point. " )
0 commit comments