diff --git a/CHANGELOG.md b/CHANGELOG.md index 10d0cb8..4200781 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 multiline content and exact quoted fallbacks. - Add downstream-shaped document fixtures for API-style documents, service configuration decoding, and unsupported merge-key fallback behavior. +- Add usage and migration documentation that states tested APIs, support + boundaries, unsupported YAML behavior, and cross-platform verification gates. ### Changed diff --git a/README.md b/README.md index e68507c..ab6024f 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,13 @@ PureYAML is informed by Yams and its bundled `CYaml` / libyaml-derived parser, but it does not copy their implementation into `Sources/`. See [ATTRIBUTION.md](ATTRIBUTION.md). +## Documentation + +- [Usage](docs/USAGE.md): public APIs, validation, emitter options, and + cross-platform gates. +- [Migration and support boundaries](docs/MIGRATION.md): what PureYAML can + replace today and which YAML features are deliberately unsupported. + ## Usage The examples in this section are covered by diff --git a/docs/MIGRATION.md b/docs/MIGRATION.md new file mode 100644 index 0000000..7ef59eb --- /dev/null +++ b/docs/MIGRATION.md @@ -0,0 +1,96 @@ +# Migration and YAML Support Boundaries + +PureYAML can replace a C-backed YAML parser when the caller needs dependency-free +Swift parsing, emitting, validation, and typed conversion for the currently +tested YAML subset. + +It should not be treated as a drop-in full YAML implementation yet. The current +contract is the behavior pinned by the Swift Testing corpus. + +## Attribution Boundary + +PureYAML is informed by the behavior and public API shape of Yams and its bundled +`CYaml` / libyaml-derived parser. The public implementation is original Swift +code in this repository. Do not copy Yams, CYaml, or libyaml implementation code +into `Sources/`. + +See [../ATTRIBUTION.md](../ATTRIBUTION.md) for the project attribution. + +## Migration Map + +| Existing need | PureYAML path | Notes | +|---|---|---| +| Compose YAML into a native YAML tree | `PureYAML.parse(_:)` | Returns `PureYAML.Model.Value`, preserving mapping order and duplicate keys. | +| Serialize a YAML tree | `PureYAML.dump(_:options:)` | Emits deterministic YAML with explicit block, flow, and scalar policies. | +| Decode a `Decodable` type from YAML | `PureYAML.decode(_:from:)` | Runs default validation before typed decoding. | +| Encode an `Encodable` value to YAML | `PureYAML.encodeToYAML(_:options:)` | Encodes through `PureYAML.Model.Value`, then dumps. | +| Validate document structure | `PureYAML.validate(_:using:strict:)` | Default rule reports duplicate mapping keys with exact paths. | +| Load arbitrary `Any` dictionaries and arrays | Use `Model.Value` or typed `Codable` | There is no arbitrary `Any` conversion API yet. | + +## Supported Today + +The supported list below is covered by tests. When adding a new migration case, +add a fixture beside the closest existing suite. + +| Area | Current behavior | Test coverage | +|---|---|---| +| Block mappings and sequences | Parsed into ordered mappings and sequences. | `ParsingTests`, `CollectionCompatibilityFixtures` | +| Common scalars | Null, booleans, integers, doubles, and strings resolve into model values. | `ParsingTests`, `ScalarCompatibilityFixtures` | +| Quoted strings | Single and double quoted strings keep string intent. | `ParsingTests`, `ScalarCompatibilityFixtures` | +| Comments | Comments outside quoted strings are ignored. Hash characters inside quoted strings remain content. | `ParsingTests` | +| Flow collections | Flow sequences and flow mappings compose into model values. | `ParsingTests`, `CollectionCompatibilityFixtures` | +| Literal and folded blocks | Block scalar text is preserved according to current parser rules. | `ParsingTests`, `DumperTests` | +| Anchors and aliases | Scalar and mapping anchors can be reused through aliases. Undefined aliases throw exact parse errors. | `ParsingTests`, `CollectionCompatibilityFixtures` | +| YAML directives and document markers | `%YAML 1.2`, selected `%TAG` expansion, `---`, and `...` are supported for single-document streams. | `ParsingCompatibilityTests` | +| Built-in scalar tags | `!!str`, `!!int`, `!!float`, `!!bool`, and `!!null` are applied when valid. | `ScalarCompatibilityFixtures` | +| Emitting | Deterministic block output by default, with opt-in flow collections and conservative literal blocks. | `DumperTests`, `EmitterCorpusTests` | +| Typed conversion | Scalar, keyed, unkeyed, nested, sequence, dynamic-key, and super-coder cases are covered. | `CodingTests`, `CodableCompatibilityTests` | +| Validation | Duplicate mapping keys are reported with exact issue paths. | `ValidationTests`, `ValidationModelTests` | +| Downstream-shaped documents | API-style and service-configuration documents parse and decode through representative paths. | `DownstreamDocumentTests` | + +## Current Unsupported Behavior + +Unsupported behavior is either rejected with an exact error or preserved as an +explicit fallback value tree. This avoids silent compatibility drift. + +| YAML pattern | Current PureYAML behavior | Test coverage | +|---|---|---| +| Multi-document streams | Throws `unsupportedMultiDocumentStream`. | `UnsupportedYAMLGapsFixtures.parseErrors` | +| Content after explicit document end | Throws `unsupportedMultiDocumentStream`; trailing comments after `...` remain non-content. | `UnsupportedYAMLGapsFixtures` | +| Complex mapping keys such as `? [a, b]` or `? {name: Example}` | Throws `expectedScalarKey`. | `UnsupportedYAMLGapsFixtures.parseErrors` | +| Unknown directives such as `%FOO` | Throws `unsupportedDirective`. | `UnsupportedYAMLGapsFixtures.parseErrors` | +| `%TAG` after content or after document start | Throws `unsupportedDirective`. | `UnsupportedYAMLGapsFixtures.parseErrors` | +| Undefined aliases | Throws `undefinedAlias`. | `CollectionCompatibilityFixtures.parseErrors` | +| Invalid explicit scalar tag values | Throws `invalidTaggedScalar`. | `ScalarCompatibilityFixtures.invalidExplicitScalarTags` | +| Merge keys | Keeps `<<` as an ordinary mapping entry; it does not flatten merged keys. | `UnsupportedYAMLGapsFixtures.fallbackValues`, `DownstreamDocumentTests` | +| Unsupported built-in tags such as `!!timestamp`, `!!binary`, `!!set`, and `!!omap` | Keeps parseable value trees without special tag semantics. | `UnsupportedYAMLGapsFixtures.fallbackValues` | +| Sexagesimal-style scalars and date-looking plain scalars | Keep string values for currently unsupported spellings such as `1:20`, `2002-04-28`, and `09`. | `ScalarCompatibilityFixtures.unsupportedScalars` | +| Custom constructors, custom resolvers, and environment expansion | Not implemented. Build project-specific conversion on top of `Model.Value` or typed `Codable`. | Planned future work | +| Foundation-specific scalar conversions such as `Data`, `Date`, or `URL` strategies | Not special-cased in the library target. Encode explicit strings or typed models instead. | Planned future work | + +## Migration Checklist + +1. Audit the YAML corpus the caller actually reads and writes. +2. Classify each document against the supported and unsupported tables above. +3. Add representative fixtures for project-specific documents before switching + call sites. +4. Parse through `PureYAML.parse(_:)` and run `PureYAML.validate(_:)` before + typed decoding when duplicate-key visibility matters. +5. Prefer typed `Codable` for known schemas and `PureYAML.Model.Value` for + inspection, migration tooling, or unsupported-shape triage. +6. Run macOS, Linux, and WASM gates before publishing the migration. + +## When Not to Migrate Yet + +Do not migrate a caller yet if it requires: + +- multi-document stream loading or dumping; +- YAML merge-key flattening; +- complex mapping keys as first-class keys; +- binary or timestamp conversion into Foundation types; +- arbitrary `Any` load and dump APIs; +- custom scalar constructors or resolvers; +- a full YAML 1.2 compliance claim. + +In those cases, add a fixture and a child issue before changing production call +sites. diff --git a/docs/USAGE.md b/docs/USAGE.md new file mode 100644 index 0000000..c7d6838 --- /dev/null +++ b/docs/USAGE.md @@ -0,0 +1,129 @@ +# PureYAML Usage + +PureYAML is a dependency-free Swift package for parsing, emitting, validating, +and typed-converting the YAML subset currently covered by the test corpus. + +## Installation + +Until the first tagged release, use the `main` branch: + +```swift +.package(url: "https://github.com/mihaelamj/PureYAML.git", branch: "main") +``` + +Add the product to the target that reads or writes YAML: + +```swift +.product(name: "PureYAML", package: "PureYAML") +``` + +Then import the library: + +```swift +import PureYAML +``` + +`Package.swift` for PureYAML itself keeps `dependencies: []`. Consumers do not +need C libraries, generated parser files, JavaScript tooling, or Foundation-only +support code to use the library target. + +## Public Entry Points + +The README examples are mirrored in `Tests/DocumentationExampleTests.swift`. +Those tests are the executable contract for the short-form examples. + +| Need | API | Result | +|---|---|---| +| Parse YAML into an ordered value tree | `PureYAML.parse(_:)` | `PureYAML.Model.Value` | +| Emit YAML from a value tree | `PureYAML.dump(_:options:)` | `String` | +| Validate a value tree | `PureYAML.validate(_:using:strict:)` | `[PureYAML.Validation.Issue]` or a thrown issue collection | +| Decode a typed value from YAML | `PureYAML.decode(_:from:)` | `Decodable` value | +| Encode a typed value to a value tree | `PureYAML.encode(_:)` | `PureYAML.Model.Value` | +| Encode a typed value to YAML | `PureYAML.encodeToYAML(_:options:)` | `String` | + +## Model Usage + +Use `PureYAML.Model.Value` when the caller needs to inspect YAML structure, +preserve mapping order, validate duplicate keys, or handle YAML that does not map +cleanly to a Swift `Codable` type. + +The model currently represents: + +- `null` +- booleans +- integers +- doubles +- strings +- ordered mappings +- sequences + +Mappings preserve insertion order and retain duplicate keys. That is deliberate: +validation can report duplicate-key diagnostics instead of losing information by +collapsing a mapping into a Swift dictionary too early. + +## Typed Conversion + +Use typed conversion when the YAML shape is already known. Current coverage +includes: + +- scalar `Decodable` and `Encodable` values; +- keyed mapping-backed structs; +- optional scalar fields; +- nested keyed structs; +- unkeyed sequences; +- nested sequences; +- keyed structs with sequence properties; +- dictionary-like string-keyed mappings where YAML semantics allow them; +- keyed super coders and default `super` coders. + +Typed decoding runs the default validator first. Duplicate mapping keys are +reported before Swift `Decodable` construction starts. + +## Emitting Options + +The default dumper emits deterministic block-style YAML with quoted strings. +Callers can opt into: + +- conservative plain strings with `.plainWhenSafe`; +- safe literal block scalars for multiline strings with + `.literalBlockWhenMultiline`; +- compact flow collections with `collectionStyle: .flow`. + +Literal block emission is conservative. If a multiline string would not +round-trip through the current parser, the dumper keeps it as a quoted scalar. + +## Validation + +The default validator rejects duplicate mapping keys anywhere in the value tree. +Validation issues include: + +- severity; +- reason; +- path. + +Strict validation treats warnings as failures. Non-strict validation returns +warnings but still throws when errors are present. + +Use `PureYAML.Validation.Validator.blank` when the caller wants only +project-specific validation rules. + +## Cross-Platform Verification + +Before relying on a migration, run the same gates used by the repository: + +```sh +bash scripts/check-all.sh +bash scripts/check-linux.sh +bash scripts/check-wasm.sh +``` + +`scripts/check-all.sh` includes style, namespacing, forbidden-pattern, +changelog, roadmap, SwiftFormat, SwiftLint, host build, host tests, Linux build +and test, and WASM build checks. The explicit Linux and WASM scripts are useful +when recording release or migration evidence. + +## Support Boundaries + +PureYAML is not yet a complete YAML 1.2 implementation. Before replacing another +YAML stack, read [MIGRATION.md](MIGRATION.md) and compare your input corpus with +the supported and unsupported behavior listed there.