Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@

## Testing direction
- Prefer Swift Testing over XCTest for new or updated tests. Existing XCTest cases are legacy and should be migrated opportunistically. Keep tests runnable across Apple and non-Apple platforms.
- Follow the Swift Testing style used in the blob tests: group by feature under `Tests/LSQLiteTests`, use `@Suite("...")`, and give `@Test` cases descriptive names.
- Test files mirror source files with a `Tests` suffix, and each test file defines exactly one `@Suite` named after the original file (without the `Tests` suffix).
- Use in-memory databases for isolation, set up shared fixtures in `init()` with `#require` on result codes and optional unwrapping, and tear down with `deinit` or `defer` when a handle must be closed.
- Assert SQLite semantics via `ResultCode` with `#expect` (for example, `.ok`, `.error`, `.busy`, `.abort`) instead of introducing throwing flows.
- Keep tests focused and readable by extracting repeated buffer or pointer helpers into file-private functions.
- Keep test control flow linear with no branching; use `try #require` to unwrap optionals or verify prerequisites before continuing.
- Focus tests on validating the wrapper behavior and surface (rawValue round trips, ResultCode mapping, handle lifecycle), not SQLite's own functionality.

## Platform expectations
- Non-Apple platforms are fully supported. Gate Apple-only constants and behaviors with the appropriate `canImport` checks, and rely on the `MissedSwiftSQLite` target to expose any SQLite constants or helpers that the Swift importer misses on Linux.
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ let package = Package(
),
.testTarget(
name: "LSQLiteTests",
dependencies: ["LSQLite"]
dependencies: ["LSQLite", "MissedSwiftSQLite"]
),
]
)
19 changes: 13 additions & 6 deletions Sources/LSQLite/Blob/Blob+Access.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,38 @@ import MissedSwiftSQLite
extension Blob {
/// Size of this open BLOB in bytes.
///
/// Related SQLite: `sqlite3_blob_bytes`, `sqlite3_blob_open`, `sqlite3_blob_close`
/// Returns 0 if the handle is aborted.
///
/// Related SQLite: `sqlite3_blob_bytes`
@inlinable public var byteCount: Int32 {
sqlite3_blob_bytes(rawValue)
}

/// Reads `length` bytes from the BLOB starting at `offset` into `buffer`.
///
/// Use `byteCount` to validate bounds before reading.
/// - Parameters:
/// - buffer: Destination buffer.
/// - length: Number of bytes to copy.
/// - offset: Byte offset within the BLOB.
/// - Returns: Result of `sqlite3_blob_read`.
/// - Returns: Result code for the read.
///
/// Related SQLite: `sqlite3_blob_read`, `sqlite3_blob_bytes`, `sqlite3_blob_open`
/// Related SQLite: `sqlite3_blob_read`
@inlinable public func read(into buffer: UnsafeMutableRawPointer, length: Int32, offset: Int32) -> ResultCode {
sqlite3_blob_read(rawValue, buffer, length, offset).resultCode
}

/// Writes `length` bytes from `buffer` into the BLOB starting at `offset`; handle must be opened for writing.
/// Writes `length` bytes from `buffer` into the BLOB starting at `offset`.
///
/// The handle must be opened for writing and the write does not change the BLOB size.
/// Use `byteCount` to validate bounds before writing.
/// - Parameters:
/// - buffer: Source bytes to write.
/// - length: Number of bytes to write.
/// - offset: Byte offset within the BLOB.
/// - Returns: Result of `sqlite3_blob_write`.
/// - Returns: Result code for the write.
///
/// Related SQLite: `sqlite3_blob_write`, `sqlite3_blob_bytes`, `sqlite3_blob_open`
/// Related SQLite: `sqlite3_blob_write`
@inlinable public func write(_ buffer: UnsafeRawPointer, length: Int32, offset: Int32) -> ResultCode {
sqlite3_blob_write(rawValue, buffer, length, offset).resultCode
}
Expand Down
17 changes: 11 additions & 6 deletions Sources/LSQLite/Blob/Blob+Lifecycle.swift
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
import MissedSwiftSQLite

extension Blob {
/// Moves this BLOB handle to a different row of the same table.
/// Moves this handle to a different row of the same table.
///
/// The database, table, and column stay the same. On failure, the handle becomes
/// aborted and `read` or `write` return `.abort`.
/// - Parameter rowID: Target rowid.
/// - Returns: Result of `sqlite3_blob_reopen`.
/// - Returns: Result code for the operation.
///
/// Related SQLite: `sqlite3_blob_reopen`, `sqlite3_blob_read`, `sqlite3_blob_write`, `sqlite3_blob_bytes`
/// Related SQLite: `sqlite3_blob_reopen`
@inlinable public func reopen(at rowID: RowID) -> ResultCode {
sqlite3_blob_reopen(rawValue, rowID.rawValue).resultCode
}

/// Closes this BLOB handle; auto-commit transactions may finalize if no other writers remain.
/// - Returns: Result of `sqlite3_blob_close`.
/// Closes this handle.
///
/// Closing always releases the handle, even if an error is returned.
/// - Returns: Result code for the close.
///
/// Related SQLite: `sqlite3_blob_close`, `sqlite3_errcode`, `sqlite3_errmsg`
/// Related SQLite: `sqlite3_blob_close`
@inlinable public func close() -> ResultCode {
sqlite3_blob_close(rawValue).resultCode
}
Expand Down
7 changes: 5 additions & 2 deletions Sources/LSQLite/Blob/Blob.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
/// Wrapper around an open SQLite BLOB handle for incremental I/O.
/// Wrapper around an open BLOB handle for incremental I/O.
///
/// Related SQLite: `sqlite3_blob_open`, `sqlite3_blob_close`, `sqlite3_blob_read`, `sqlite3_blob_write`, `sqlite3_blob_bytes`
/// Create a handle with `Database.openBlob(_:databaseName:tableName:columnName:rowID:flags:)`
/// and close it when finished. Do not use the handle after closing.
///
/// Related SQLite: `sqlite3_blob`, `sqlite3_blob_open`, `sqlite3_blob_close`, `sqlite3_blob_read`, `sqlite3_blob_write`, `sqlite3_blob_bytes`
@frozen public struct Blob: RawRepresentable {
public let rawValue: OpaquePointer

Expand Down
Loading