Skip to content

Commit 3d18c64

Browse files
committed
Convenience adapters
1 parent e9f390f commit 3d18c64

2 files changed

Lines changed: 149 additions & 0 deletions

File tree

Sources/Otter/DatabaseValueAdapter.swift

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,3 +383,108 @@ public struct URLDatabaseValueAdapter: DatabaseValueAdapter {
383383
return url
384384
}
385385
}
386+
387+
388+
/// A convenience adapter for a type that can only be encoded to a `String`
389+
public struct AsStringAdapter<Value>: DatabaseValueAdapter {
390+
@usableFromInline let encode: @Sendable (Value) throws(OtterError) -> String
391+
@usableFromInline let decode: @Sendable (String) throws(OtterError) -> Value
392+
393+
public init(
394+
encode: @Sendable @escaping (Value) throws(OtterError) -> String,
395+
decode: @Sendable @escaping (String) throws(OtterError) -> Value
396+
) {
397+
self.encode = encode
398+
self.decode = decode
399+
}
400+
401+
@inlinable public func encodeToString(value: Value) throws(OtterError) -> String {
402+
try encode(value)
403+
}
404+
405+
@inlinable public func encodeToAny(value: Value) throws(OtterError) -> SQLAny {
406+
try .string(encode(value))
407+
}
408+
409+
@inlinable public func decode(from primitive: String) throws(OtterError) -> Value {
410+
try decode(primitive)
411+
}
412+
}
413+
414+
/// A convenience adapter for a type that can only be encoded to a `Int`
415+
public struct AsIntAdapter<Value>: DatabaseValueAdapter {
416+
@usableFromInline let encode: @Sendable (Value) throws(OtterError) -> Int
417+
@usableFromInline let decode: @Sendable (Int) throws(OtterError) -> Value
418+
419+
public init(
420+
encode: @Sendable @escaping (Value) throws(OtterError) -> Int,
421+
decode: @Sendable @escaping (Int) throws(OtterError) -> Value
422+
) {
423+
self.encode = encode
424+
self.decode = decode
425+
}
426+
427+
@inlinable public func encodeToInt(value: Value) throws(OtterError) -> Int {
428+
try encode(value)
429+
}
430+
431+
@inlinable public func encodeToAny(value: Value) throws(OtterError) -> SQLAny {
432+
try .int(encode(value))
433+
}
434+
435+
@inlinable public func decode(from primitive: Int) throws(OtterError) -> Value {
436+
try decode(primitive)
437+
}
438+
}
439+
440+
/// A convenience adapter for a type that can only be encoded to a `Double`
441+
public struct AsDoubleAdapter<Value>: DatabaseValueAdapter {
442+
@usableFromInline let encode: @Sendable (Value) throws(OtterError) -> Double
443+
@usableFromInline let decode: @Sendable (Double) throws(OtterError) -> Value
444+
445+
public init(
446+
encode: @Sendable @escaping (Value) throws(OtterError) -> Double,
447+
decode: @Sendable @escaping (Double) throws(OtterError) -> Value
448+
) {
449+
self.encode = encode
450+
self.decode = decode
451+
}
452+
453+
@inlinable public func encodeToDouble(value: Value) throws(OtterError) -> Double {
454+
try encode(value)
455+
}
456+
457+
@inlinable public func encodeToAny(value: Value) throws(OtterError) -> SQLAny {
458+
try .double(encode(value))
459+
}
460+
461+
@inlinable public func decode(from primitive: Double) throws(OtterError) -> Value {
462+
try decode(primitive)
463+
}
464+
}
465+
466+
/// A convenience adapter for a type that can only be encoded to a `Data`
467+
public struct AsDataAdapter<Value>: DatabaseValueAdapter {
468+
@usableFromInline let encode: @Sendable (Value) throws(OtterError) -> Data
469+
@usableFromInline let decode: @Sendable (Data) throws(OtterError) -> Value
470+
471+
public init(
472+
encode: @Sendable @escaping (Value) throws(OtterError) -> Data,
473+
decode: @Sendable @escaping (Data) throws(OtterError) -> Value
474+
) {
475+
self.encode = encode
476+
self.decode = decode
477+
}
478+
479+
@inlinable public func encodeToData(value: Value) throws(OtterError) -> Data {
480+
try encode(value)
481+
}
482+
483+
@inlinable public func encodeToAny(value: Value) throws(OtterError) -> SQLAny {
484+
try .data(encode(value))
485+
}
486+
487+
@inlinable public func decode(from primitive: Data) throws(OtterError) -> Value {
488+
try decode(primitive)
489+
}
490+
}

Tests/OtterTests/DatabaseValueAdapterTests.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,50 @@ struct DatabaseValueAdapterTests {
128128
#expect(value == decoded)
129129
}
130130

131+
@Test func asStringAdapter() throws {
132+
let adapter: any DatabaseValueAdapter<Int> = AsStringAdapter<Int> { value in
133+
value.description
134+
} decode: { primitive in
135+
Int(primitive) ?? 0
136+
}
137+
138+
#expect(try adapter.encodeToString(value: 123) == "123")
139+
#expect(try adapter.decode(from: "123") == 123)
140+
}
141+
142+
@Test func asIntAdapter() throws {
143+
let adapter: any DatabaseValueAdapter<String> = AsIntAdapter<String> { value in
144+
Int(value.description) ?? 0
145+
} decode: { primitive in
146+
primitive.description
147+
}
148+
149+
#expect(try adapter.encodeToInt(value: "123") == 123)
150+
#expect(try adapter.decode(from: 123) == "123")
151+
}
152+
153+
@Test func asDoubleAdapter() throws {
154+
let adapter: any DatabaseValueAdapter<String> = AsDoubleAdapter<String> { value in
155+
Double(value.description) ?? 0
156+
} decode: { primitive in
157+
primitive.description
158+
}
159+
160+
#expect(try adapter.encodeToDouble(value: "123") == 123.0)
161+
#expect(try adapter.decode(from: 123.0) == "123.0")
162+
}
163+
164+
@Test func asDataAdapter() throws {
165+
let adapter: any DatabaseValueAdapter<Int> = AsDataAdapter<Int> { value in
166+
Data([UInt8(value)])
167+
} decode: { primitive in
168+
Int(primitive[0])
169+
}
170+
171+
#expect(try adapter.encodeToData(value: 123) == Data([123]))
172+
#expect(try adapter.decode(from: Data([123])) == 123)
173+
}
174+
131175
@Test func customDatabaseValueAdapter() async throws {
132176
let db: TestDB = try .inMemory(
133177
adapters: TestDB.Adapters(

0 commit comments

Comments
 (0)