Skip to content

Perfect-MongoDB 4.0: Swift 6, libmongoc 2, Codable and async/await - #30

Merged
taplin merged 12 commits into
mainfrom
swift6-modernization
Sep 29, 2026
Merged

taplin merged 12 commits into
mainfrom
swift6-modernization

Conversation

@taplin

@taplin taplin commented Sep 29, 2026

Copy link
Copy Markdown
Collaborator

Modernizes Perfect-MongoDB for Swift 6 and libmongoc 2. The pre-4.0 code is preserved on the legacy branch.

Summary

  • Swift 6: swift-tools-version: 6.0, Swift 6 language mode, macOS 12+ and Linux. PerfectLib and the PerfectSideRepos C wrappers are no longer dependencies; the module maps now live in this repo, keeping their old names (PerfectCMongo, PerfectCBSON).
  • libmongoc 2.x only. Every call removed in 2.x is replaced, and the legacy query translation lives in Sources/PerfectCMongo/shim.h. Writes use the current *_one/*_many/*_with_opts APIs. The build has no warnings.
  • Existing API kept. A few behaviour changes are forced by libmongoc 2: getLastError() is deprecated and returns an empty document, command() returns its reply as a one-document cursor, count() is exact, and slaveOk is renamed secondaryOk. Details are in Documentation/modernization-plan.md.
  • Fixes:
    • MongoClientPool.tryPopClient(), which passed a client where the pool was expected.
    • A leaked URI in the pool initializer.
    • A leak in distinct().
    • Crashes when a collection, cursor or GridFS file outlived its client; objects now keep their parent alive.
    • Pooled clients are returned to the pool instead of destroyed.
    • The package didn't compile on Linux (glibc FILE*).
    • An escaped buffer pointer in GridFS download(to:).
  • New API:
    • BSONEncoder/BSONDecoder (Codable, with native BSON dates, binary, UUIDs and ObjectIds).
    • Typed collection methods (insert, find, findOne, updateOne/Many, replaceOne, deleteOne/Many, countDocuments) that throw MongoError.
    • A Sendable MongoClientPool with withClient { } async and MongoClientPool(validatingURI:).
    • pool.find(...) as an AsyncSequence.
  • CI: GitHub Actions on Linux (Swift 6.4, libmongoc 2.5.5 built from source, MongoDB 8) and macOS. Scripts/test-linux.sh runs the Linux suite locally with Apple's container tool.

Testing

36 tests pass against MongoDB 8.3 on macOS and on Linux (CI and Scripts/test-linux.sh), and the async tests are clean under Thread Sanitizer. Not yet tested: mongodb+srv:// and TLS against Atlas.

Linux distributions that ship only libmongoc 1.x need libmongoc 2 built from source; the README has the steps.

🤖 Generated with Claude Code

taplin and others added 12 commits September 27, 2026 18:02
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- swift-tools-version 6.0, Swift 6 language mode, macOS 12+
- Vendor the PerfectCMongo/PerfectCBSON module maps as system-library
  targets (same module names), linking mongoc2/bson2
- Replace APIs removed in libmongoc 2: find, count, save, command,
  stats, validate, create_index, create_bulk_operation,
  get_server_status, get_collection_names, gridfs_find, bson_as_json.
  Legacy query translation lives in the PerfectCMongo shim
- Drop PerfectLib; Date.jsonEncodedString() stays public
- Deprecate getLastError() and MongoQueryFlag.slaveOk
- Remove checked-in jazzy docs and Linux test manifests
- Record progress and behaviour changes in the modernization plan

Public API unchanged. BSON tests pass; server-backed tests not yet run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
libmongoc 2 dropped automatic initialization, so MongoClient(uri:)
returned nil for every URI. Initialize once via a lazy global before
creating a client or pool.

Full suite (21 tests) passes against MongoDB 8.3.11 / libmongoc 2.5.5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- insert/update/remove/findAndModify and bulk writes use the
  *_one/*_many/*_with_opts APIs; legacy update still accepts both
  update-operator and replacement documents
- get_database_names_with_opts, bson_append_array_unsafe_begin,
  String(validatingCString:); build is warning-free
- MongoClientPool: fix tryPopClient, free the parsed URI, report an
  invalid URI clearly; released pooled clients return to the pool
- Databases, collections, cursors, GridFS and GridFiles keep the object
  they came from alive, so dropping the client no longer crashes
- Fix the document leak in distinct()
- Tests read MONGODB_URI; add tests for update forms, bulk writes,
  findAndModify, the pool and object lifetimes (26 pass locally)
- README rewritten for Swift 6 / libmongoc 2
- GitHub Actions: Linux (libmongoc 2 from source, mongo:8 service) and
  macOS (Homebrew, BSON tests)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
glibc's fwrite/fclose take a non-optional FILE*, so the optional from
fopen didn't compile on Linux. download(to:) now throws when the
destination can't be opened, and uses an allocated transfer buffer
instead of a pointer escaped from withUnsafeMutableBufferPointer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- BSONEncoder/BSONDecoder over libbson: Date, Data, UUID and BSON.OID
  keep native BSON types; exact numeric conversions; descriptive errors
- Typed, throwing collection methods: insert, insert(contentsOf:), find,
  findOne, replaceOne, updateOne/Many, deleteOne/Many, countDocuments
- MongoError for the throwing API
- MongoClientPool is final and Sendable; withClient runs blocking work
  on a dedicated queue (same design as Perfect-CRUD's withCRUDExecutor)
- pool.find returns MongoFindSequence, an AsyncSequence that holds one
  pooled client per iteration and decodes in batches
- 9 new tests (35 total); Phase 4 tests pass under Thread Sanitizer
- README and modernization plan updated

Existing API unchanged; nothing deprecated yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Runs the suite in swift:6.4-noble against a mongo:8 container, like CI.
libmongoc 2.5.5 is built once into a cached volume; Linux build products
live in their own volume. Uses plain `container run`, not `container
build`, whose builder VM hung while compiling libmongoc.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- MongoClientPool(validatingURI:) throws MongoError for an invalid URI
  or rejected options (mongoc_client_pool_new_with_error); init(uri:)
  keeps its signature and traps with that error
- README.zh_CN.md translated from the current README; the two link to
  each other; README example uses the throwing initializer
- macOS CI job also runs the server-independent Codable tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@taplin
taplin merged commit 7008bc5 into main Sep 29, 2026
4 checks passed
@taplin
taplin deleted the swift6-modernization branch September 29, 2026 13:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant