DataLiteCore swift package

This commit is contained in:
2025-04-24 23:48:46 +03:00
parent b0e52a72b7
commit 6f955b2c43
70 changed files with 7939 additions and 1 deletions

View File

@@ -0,0 +1,34 @@
import Foundation
public extension SQLiteRawBindable where Self: BinaryFloatingPoint {
/// Provides the `SQLiteRawValue` representation for floating-point types.
///
/// This implementation converts the floating-point value to a `real` SQLite raw value.
///
/// - Returns: An `SQLiteRawValue` of type `.real`, containing the floating-point value.
var sqliteRawValue: SQLiteRawValue {
.real(.init(self))
}
}
public extension SQLiteRawRepresentable where Self: BinaryFloatingPoint {
/// Initializes an instance of the conforming type from an `SQLiteRawValue`.
///
/// This initializer handles `SQLiteRawValue` of type `.real`, converting it to the floating-point value.
/// It also handles `SQLiteRawValue` of type `.int`, converting it to the floating-point value.
///
/// - Parameter sqliteRawValue: The raw SQLite value used to initialize the instance.
init?(_ sqliteRawValue: SQLiteRawValue) {
switch sqliteRawValue {
case .int(let value):
self.init(Double(value))
case .real(let value):
self.init(value)
default:
return nil
}
}
}
extension Float: SQLiteRawRepresentable {}
extension Double: SQLiteRawRepresentable {}

View File

@@ -0,0 +1,43 @@
import Foundation
public extension SQLiteRawBindable where Self: BinaryInteger {
/// Provides the `SQLiteRawValue` representation for integer types.
///
/// This implementation converts the integer value to an `SQLiteRawValue` of type `.int`.
///
/// - Returns: An `SQLiteRawValue` of type `.int`, containing the integer value.
var sqliteRawValue: SQLiteRawValue {
.int(Int64(self))
}
}
public extension SQLiteRawRepresentable where Self: BinaryInteger {
/// Initializes an instance of the conforming type from an `SQLiteRawValue`.
///
/// This initializer handles `SQLiteRawValue` of type `.int`, converting it to the integer value.
/// It uses the `init(exactly:)` initializer to ensure that the value fits within the range of the
/// integer type. If the value cannot be exactly represented by the integer type, the initializer
/// will return `nil`.
///
/// - Parameter sqliteRawValue: The raw SQLite value used to initialize the instance.
init?(_ sqliteRawValue: SQLiteRawValue) {
switch sqliteRawValue {
case .int(let value):
self.init(exactly: value)
default:
return nil
}
}
}
extension Int: SQLiteRawRepresentable {}
extension Int8: SQLiteRawRepresentable {}
extension Int16: SQLiteRawRepresentable {}
extension Int32: SQLiteRawRepresentable {}
extension Int64: SQLiteRawRepresentable {}
extension UInt: SQLiteRawRepresentable {}
extension UInt8: SQLiteRawRepresentable {}
extension UInt16: SQLiteRawRepresentable {}
extension UInt32: SQLiteRawRepresentable {}
extension UInt64: SQLiteRawRepresentable {}

View File

@@ -0,0 +1,32 @@
import Foundation
extension Bool: SQLiteRawRepresentable {
/// Provides the `SQLiteRawValue` representation for boolean types.
///
/// This implementation converts the boolean value to an `SQLiteRawValue` of type `.int`.
/// - `true` is represented as `1`.
/// - `false` is represented as `0`.
///
/// - Returns: An `SQLiteRawValue` of type `.int`, containing `1` for `true` and `0` for `false`.
public var sqliteRawValue: SQLiteRawValue {
.int(self ? 1 : 0)
}
/// Initializes an instance of the conforming type from an `SQLiteRawValue`.
///
/// This initializer handles `SQLiteRawValue` of type `.int`, converting it to a boolean value.
/// - `1` is converted to `true`.
/// - `0` is converted to `false`.
///
/// If the integer value is not `0` or `1`, the initializer returns `nil`.
///
/// - Parameter sqliteRawValue: The raw SQLite value used to initialize the instance.
public init?(_ sqliteRawValue: SQLiteRawValue) {
switch sqliteRawValue {
case .int(let value) where value == 0 || value == 1:
self = value == 1
default:
return nil
}
}
}

View File

@@ -0,0 +1,26 @@
import Foundation
extension Data: SQLiteRawRepresentable {
/// Provides the `SQLiteRawValue` representation for `Data` types.
///
/// This implementation converts the `Data` value to an `SQLiteRawValue` of type `.blob`.
///
/// - Returns: An `SQLiteRawValue` of type `.blob`, containing the data.
public var sqliteRawValue: SQLiteRawValue {
.blob(self)
}
/// Initializes an instance of the conforming type from an `SQLiteRawValue`.
///
/// This initializer handles `SQLiteRawValue` of type `.blob`, converting it to `Data`.
///
/// - Parameter sqliteRawValue: The raw SQLite value used to initialize the instance.
public init?(_ sqliteRawValue: SQLiteRawValue) {
switch sqliteRawValue {
case .blob(let data):
self = data
default:
return nil
}
}
}

View File

@@ -0,0 +1,39 @@
import Foundation
extension Date: SQLiteRawRepresentable {
/// Provides the `SQLiteRawValue` representation for `Date` types.
///
/// This implementation converts the `Date` value to an `SQLiteRawValue` of type `.text`.
/// The date is formatted as an ISO 8601 string.
///
/// - Returns: An `SQLiteRawValue` of type `.text`, containing the ISO 8601 string representation of the date.
public var sqliteRawValue: SQLiteRawValue {
let formatter = ISO8601DateFormatter()
let dateString = formatter.string(from: self)
return .text(dateString)
}
/// Initializes an instance of `Date` from an `SQLiteRawValue`.
///
/// This initializer handles `SQLiteRawValue` of type `.text`, converting it from an ISO 8601 string.
/// It also supports `.int` and `.real` types representing time intervals since 1970.
///
/// - Parameter sqliteRawValue: The raw SQLite value used to initialize the instance.
public init?(_ sqliteRawValue: SQLiteRawValue) {
switch sqliteRawValue {
case .int(let value):
self.init(timeIntervalSince1970: TimeInterval(value))
case .real(let value):
self.init(timeIntervalSince1970: value)
case .text(let value):
let formatter = ISO8601DateFormatter()
if let date = formatter.date(from: value) {
self = date
} else {
return nil
}
default:
return nil
}
}
}

View File

@@ -0,0 +1,31 @@
import Foundation
public extension SQLiteRawBindable where Self: RawRepresentable, RawValue: SQLiteRawBindable {
/// Provides the `SQLiteRawValue` representation for `RawRepresentable` types.
///
/// This implementation converts the `RawRepresentable` type's `rawValue` to its corresponding
/// `SQLiteRawValue` representation. The `rawValue` itself must conform to `SQLiteRawBindable`.
///
/// - Returns: An `SQLiteRawValue` representation of the `RawRepresentable` type.
var sqliteRawValue: SQLiteRawValue {
rawValue.sqliteRawValue
}
}
public extension SQLiteRawRepresentable where Self: RawRepresentable, RawValue: SQLiteRawRepresentable {
/// Initializes an instance of the conforming type from an `SQLiteRawValue`.
///
/// This initializer converts the `SQLiteRawValue` to the `RawRepresentable` type's `rawValue`.
/// It first attempts to create a `RawValue` from the `SQLiteRawValue`, then uses that to initialize
/// the `RawRepresentable` instance. If the `SQLiteRawValue` cannot be converted to the `RawValue`, the
/// initializer returns `nil`.
///
/// - Parameter sqliteRawValue: The raw SQLite value used to initialize the instance.
init?(_ sqliteRawValue: SQLiteRawValue) {
if let value = RawValue(sqliteRawValue) {
self.init(rawValue: value)
} else {
return nil
}
}
}

View File

@@ -0,0 +1,731 @@
import Foundation
extension String {
/// Removes SQL comments from the string while preserving string literals.
///
/// This function preserves escaped single quotes inside string literals
/// and removes both single-line (`-- ...`) and multi-line (`/* ... */`) comments.
///
/// The implementation of this function is generated using
/// [`re2swift`](https://re2c.org/manual/manual_swift.html)
/// from the following parsing template:
///
/// ```swift
/// @inline(__always)
/// func removingComments() -> String {
/// withCString {
/// let yyinput = $0
/// let yylimit = strlen($0)
/// var yyoutput = [CChar]()
/// var yycursor = 0
/// loop: while yycursor < yylimit {
/// var llmarker = yycursor/*!re2c
/// re2c:define:YYCTYPE = "CChar";
/// re2c:yyfill:enable = 0;
///
/// "'" ([^'] | "''")* "'" {
/// while llmarker < yycursor {
/// yyoutput.append(yyinput[llmarker])
/// llmarker += 1
/// }
/// continue loop }
///
/// "--" [^\r\n\x00]* {
/// continue loop }
///
/// "/*" ([^*] | "*"[^/])* "*/" {
/// continue loop }
///
/// [^] {
/// yyoutput.append(yyinput[llmarker])
/// continue loop }
/// */}
/// yyoutput.append(0)
/// return String(
/// cString: yyoutput,
/// encoding: .utf8
/// ) ?? ""
/// }
/// }
/// ```
@inline(__always)
func removingComments() -> String {
withCString {
let yyinput = $0
let yylimit = strlen($0)
var yyoutput = [CChar]()
var yycursor = 0
loop: while yycursor < yylimit {
var llmarker = yycursor
var yych: CChar = 0
var yystate: UInt = 0
yyl: while true {
switch yystate {
case 0:
yych = yyinput[yycursor]
yycursor += 1
switch yych {
case 0x27:
yystate = 3
continue yyl
case 0x2D:
yystate = 4
continue yyl
case 0x2F:
yystate = 5
continue yyl
default:
yystate = 1
continue yyl
}
case 1:
yystate = 2
continue yyl
case 2:
yyoutput.append(yyinput[llmarker])
continue loop
case 3:
yych = yyinput[yycursor]
yycursor += 1
switch yych {
case 0x27:
yystate = 6
continue yyl
default:
yystate = 3
continue yyl
}
case 4:
yych = yyinput[yycursor]
switch yych {
case 0x2D:
yycursor += 1
yystate = 8
continue yyl
default:
yystate = 2
continue yyl
}
case 5:
yych = yyinput[yycursor]
switch yych {
case 0x2A:
yycursor += 1
yystate = 10
continue yyl
default:
yystate = 2
continue yyl
}
case 6:
yych = yyinput[yycursor]
switch yych {
case 0x27:
yycursor += 1
yystate = 3
continue yyl
default:
yystate = 7
continue yyl
}
case 7:
while llmarker < yycursor {
yyoutput.append(yyinput[llmarker])
llmarker += 1
}
continue loop
case 8:
yych = yyinput[yycursor]
switch yych {
case 0x00:
fallthrough
case 0x0A:
fallthrough
case 0x0D:
yystate = 9
continue yyl
default:
yycursor += 1
yystate = 8
continue yyl
}
case 9:
continue loop
case 10:
yych = yyinput[yycursor]
yycursor += 1
switch yych {
case 0x2A:
yystate = 11
continue yyl
default:
yystate = 10
continue yyl
}
case 11:
yych = yyinput[yycursor]
yycursor += 1
switch yych {
case 0x2F:
yystate = 12
continue yyl
default:
yystate = 10
continue yyl
}
case 12:
continue loop
default: fatalError("internal lexer error")
}
}
}
yyoutput.append(0)
return String(cString: yyoutput, encoding: .utf8) ?? ""
}
}
/// Trims empty lines and trailing whitespace outside string literals.
///
/// This function preserves line breaks and whitespace inside string literals,
/// removing only redundant empty lines and trailing whitespace outside literals.
///
/// The implementation of this function is generated using
/// [`re2swift`](https://re2c.org/manual/manual_swift.html)
/// from the following parsing template:
///
/// ```swift
/// @inline(__always)
/// func trimmingLines() -> String {
/// withCString {
/// let yyinput = $0
/// let yylimit = strlen($0)
/// var yyoutput = [CChar]()
/// var yycursor = 0
/// var yymarker = 0
/// loop: while yycursor < yylimit {
/// var llmarker = yycursor/*!re2c
/// re2c:define:YYCTYPE = "CChar";
/// re2c:yyfill:enable = 0;
///
/// "'" ([^'] | "''")* "'" {
/// while llmarker < yycursor {
/// yyoutput.append(yyinput[llmarker])
/// llmarker += 1
/// }
/// continue loop }
///
/// [ \t]* "\x00" {
/// continue loop }
///
/// [ \t]* "\n\x00" {
/// continue loop }
///
/// [ \t\n]* "\n"+ {
/// if llmarker > 0 && yycursor < yylimit {
/// yyoutput.append(0x0A)
/// }
/// continue loop }
///
/// [^] {
/// yyoutput.append(yyinput[llmarker])
/// continue loop }
/// */}
/// yyoutput.append(0)
/// return String(
/// cString: yyoutput,
/// encoding: .utf8
/// ) ?? ""
/// }
/// }
/// ```
@inline(__always)
func trimmingLines() -> String {
withCString {
let yyinput = $0
let yylimit = strlen($0)
var yyoutput = [CChar]()
var yycursor = 0
var yymarker = 0
loop: while yycursor < yylimit {
var llmarker = yycursor
var yych: CChar = 0
var yyaccept: UInt = 0
var yystate: UInt = 0
yyl: while true {
switch yystate {
case 0:
yych = yyinput[yycursor]
yycursor += 1
switch yych {
case 0x00:
yystate = 1
continue yyl
case 0x09:
fallthrough
case 0x20:
yystate = 4
continue yyl
case 0x0A:
yystate = 5
continue yyl
case 0x27:
yystate = 7
continue yyl
default:
yystate = 2
continue yyl
}
case 1:
continue loop
case 2:
yystate = 3
continue yyl
case 3:
yyoutput.append(yyinput[llmarker])
continue loop
case 4:
yyaccept = 0
yymarker = yycursor
yych = yyinput[yycursor]
switch yych {
case 0x00:
fallthrough
case 0x09...0x0A:
fallthrough
case 0x20:
yystate = 9
continue yyl
default:
yystate = 3
continue yyl
}
case 5:
yyaccept = 1
yymarker = yycursor
yych = yyinput[yycursor]
switch yych {
case 0x00:
yycursor += 1
yystate = 11
continue yyl
case 0x09...0x0A:
fallthrough
case 0x20:
yystate = 13
continue yyl
default:
yystate = 6
continue yyl
}
case 6:
if llmarker > 0 && yycursor < yylimit {
yyoutput.append(0x0A)
}
continue loop
case 7:
yych = yyinput[yycursor]
yycursor += 1
switch yych {
case 0x27:
yystate = 15
continue yyl
default:
yystate = 7
continue yyl
}
case 8:
yych = yyinput[yycursor]
yystate = 9
continue yyl
case 9:
switch yych {
case 0x00:
yycursor += 1
yystate = 1
continue yyl
case 0x09:
fallthrough
case 0x20:
yycursor += 1
yystate = 8
continue yyl
case 0x0A:
yycursor += 1
yystate = 5
continue yyl
default:
yystate = 10
continue yyl
}
case 10:
yycursor = yymarker
if yyaccept == 0 {
yystate = 3
continue yyl
} else {
yystate = 6
continue yyl
}
case 11:
continue loop
case 12:
yych = yyinput[yycursor]
yystate = 13
continue yyl
case 13:
switch yych {
case 0x09:
fallthrough
case 0x20:
yycursor += 1
yystate = 12
continue yyl
case 0x0A:
yycursor += 1
yystate = 14
continue yyl
default:
yystate = 10
continue yyl
}
case 14:
yyaccept = 1
yymarker = yycursor
yych = yyinput[yycursor]
switch yych {
case 0x09:
fallthrough
case 0x20:
yycursor += 1
yystate = 12
continue yyl
case 0x0A:
yycursor += 1
yystate = 14
continue yyl
default:
yystate = 6
continue yyl
}
case 15:
yych = yyinput[yycursor]
switch yych {
case 0x27:
yycursor += 1
yystate = 7
continue yyl
default:
yystate = 16
continue yyl
}
case 16:
while llmarker < yycursor {
yyoutput.append(yyinput[llmarker])
llmarker += 1
}
continue loop
default: fatalError("internal lexer error")
}
}
}
yyoutput.append(0)
return String(
cString: yyoutput,
encoding: .utf8
) ?? ""
}
}
/// Splits the SQL script into individual statements by semicolons.
///
/// This function preserves string literals (enclosed in single quotes),
/// and treats `BEGIN...END` blocks as single nested statements, preventing
/// splitting inside these blocks. Statements are split only at semicolons
/// outside string literals and `BEGIN...END` blocks.
///
/// The implementation of this function is generated using
/// [`re2swift`](https://re2c.org/manual/manual_swift.html)
/// from the following parsing template:
///
/// ```swift
/// @inline(__always)
/// func splitStatements() -> [String] {
/// withCString {
/// let yyinput = $0
/// let yylimit = strlen($0)
/// var yyranges = [Range<Int>]()
/// var yycursor = 0
/// var yymarker = 0
/// var yynesting = 0
/// var yystart = 0
/// var yyend = 0
/// loop: while yycursor < yylimit {/*!re2c
/// re2c:define:YYCTYPE = "CChar";
/// re2c:yyfill:enable = 0;
///
/// "'" ( [^'] | "''" )* "'" {
/// yyend = yycursor
/// continue loop }
///
/// 'BEGIN' {
/// yynesting += 1
/// yyend = yycursor
/// continue loop }
///
/// 'END' {
/// if yynesting > 0 {
/// yynesting -= 1
/// }
/// yyend = yycursor
/// continue loop }
///
/// ";" [ \t]* "\n"* {
/// if yynesting == 0 {
/// if yystart < yyend {
/// yyranges.append(yystart..<yyend)
/// }
/// yystart = yycursor
/// continue loop
/// } else {
/// continue loop
/// }}
///
/// [^] {
/// yyend = yycursor
/// continue loop }
/// */}
/// if yystart < yyend {
/// yyranges.append(yystart..<yyend)
/// }
/// return yyranges.map { range in
/// let buffer = UnsafeBufferPointer<CChar>(
/// start: yyinput.advanced(by: range.lowerBound),
/// count: range.count
/// )
/// let array = Array(buffer) + [0]
/// return String(cString: array, encoding: .utf8) ?? ""
/// }
/// }
/// }
/// ```
@inline(__always)
func splitStatements() -> [String] {
withCString {
let yyinput = $0
let yylimit = strlen($0)
var yyranges = [Range<Int>]()
var yycursor = 0
var yymarker = 0
var yynesting = 0
var yystart = 0
var yyend = 0
loop: while yycursor < yylimit {
var yych: CChar = 0
var yystate: UInt = 0
yyl: while true {
switch yystate {
case 0:
yych = yyinput[yycursor]
yycursor += 1
switch yych {
case 0x27:
yystate = 3
continue yyl
case 0x3B:
yystate = 4
continue yyl
case 0x42:
fallthrough
case 0x62:
yystate = 6
continue yyl
case 0x45:
fallthrough
case 0x65:
yystate = 7
continue yyl
default:
yystate = 1
continue yyl
}
case 1:
yystate = 2
continue yyl
case 2:
yyend = yycursor
continue loop
case 3:
yych = yyinput[yycursor]
yycursor += 1
switch yych {
case 0x27:
yystate = 8
continue yyl
default:
yystate = 3
continue yyl
}
case 4:
yych = yyinput[yycursor]
switch yych {
case 0x09:
fallthrough
case 0x20:
yycursor += 1
yystate = 4
continue yyl
case 0x0A:
yycursor += 1
yystate = 10
continue yyl
default:
yystate = 5
continue yyl
}
case 5:
if yynesting == 0 {
if yystart < yyend {
yyranges.append(yystart..<yyend)
}
yystart = yycursor
continue loop
} else {
continue loop
}
case 6:
yymarker = yycursor
yych = yyinput[yycursor]
switch yych {
case 0x45:
fallthrough
case 0x65:
yycursor += 1
yystate = 11
continue yyl
default:
yystate = 2
continue yyl
}
case 7:
yymarker = yycursor
yych = yyinput[yycursor]
switch yych {
case 0x4E:
fallthrough
case 0x6E:
yycursor += 1
yystate = 13
continue yyl
default:
yystate = 2
continue yyl
}
case 8:
yych = yyinput[yycursor]
switch yych {
case 0x27:
yycursor += 1
yystate = 3
continue yyl
default:
yystate = 9
continue yyl
}
case 9:
yyend = yycursor
continue loop
case 10:
yych = yyinput[yycursor]
switch yych {
case 0x0A:
yycursor += 1
yystate = 10
continue yyl
default:
yystate = 5
continue yyl
}
case 11:
yych = yyinput[yycursor]
switch yych {
case 0x47:
fallthrough
case 0x67:
yycursor += 1
yystate = 14
continue yyl
default:
yystate = 12
continue yyl
}
case 12:
yycursor = yymarker
yystate = 2
continue yyl
case 13:
yych = yyinput[yycursor]
switch yych {
case 0x44:
fallthrough
case 0x64:
yycursor += 1
yystate = 15
continue yyl
default:
yystate = 12
continue yyl
}
case 14:
yych = yyinput[yycursor]
switch yych {
case 0x49:
fallthrough
case 0x69:
yycursor += 1
yystate = 16
continue yyl
default:
yystate = 12
continue yyl
}
case 15:
if yynesting > 0 {
yynesting -= 1
}
yyend = yycursor
continue loop
case 16:
yych = yyinput[yycursor]
switch yych {
case 0x4E:
fallthrough
case 0x6E:
yycursor += 1
yystate = 17
continue yyl
default:
yystate = 12
continue yyl
}
case 17:
yynesting += 1
yyend = yycursor
continue loop
default: fatalError("internal lexer error")
}
}
}
if yystart < yyend {
yyranges.append(yystart..<yyend)
}
return yyranges.map { range in
let buffer = UnsafeBufferPointer<CChar>(
start: yyinput.advanced(by: range.lowerBound),
count: range.count
)
let array = Array(buffer) + [0]
return String(cString: array, encoding: .utf8) ?? ""
}
}
}
}

View File

@@ -0,0 +1,26 @@
import Foundation
extension String: SQLiteRawRepresentable {
/// Provides the `SQLiteRawValue` representation for `String` type.
///
/// This implementation converts the `String` value to an `SQLiteRawValue` of type `.text`.
///
/// - Returns: An `SQLiteRawValue` of type `.text`, containing the string value.
public var sqliteRawValue: SQLiteRawValue {
.text(self)
}
/// Initializes an instance of `String` from an `SQLiteRawValue`.
///
/// This initializer handles `SQLiteRawValue` of type `.text`, converting it to a `String` value.
///
/// - Parameter sqliteRawValue: The raw SQLite value used to initialize the instance.
public init?(_ sqliteRawValue: SQLiteRawValue) {
switch sqliteRawValue {
case .text(let value):
self = value
default:
return nil
}
}
}

View File

@@ -0,0 +1,26 @@
import Foundation
extension UUID: SQLiteRawRepresentable {
/// Provides the `SQLiteRawValue` representation for `UUID`.
///
/// This implementation converts the `UUID` value to an `SQLiteRawValue` of type `.text`.
///
/// - Returns: An `SQLiteRawValue` of type `.text`, containing the UUID string.
public var sqliteRawValue: SQLiteRawValue {
.text(self.uuidString)
}
/// Initializes an instance of `UUID` from an `SQLiteRawValue`.
///
/// This initializer handles `SQLiteRawValue` of type `.text`, converting it to a `UUID`.
///
/// - Parameter sqliteRawValue: The raw SQLite value used to initialize the instance.
public init?(_ sqliteRawValue: SQLiteRawValue) {
switch sqliteRawValue {
case .text(let value):
self.init(uuidString: value)
default:
return nil
}
}
}