Skip to content

Repository files navigation

Requisite

Requisite puts data-handling requirements in Swift signatures. It provides three focused tools:

Type Purpose
Tainted<Value, State> routes external input through an application policy
Confident<Value> validates a probability and produces an exhaustive action gate
Fresh<Value> checks an individual TTL and preserves stale values for recovery

The package has no dependencies and supports Swift 5.9 or later. Swift 5.9 is the first stable release with the ownership model evaluated for Certain, and it remains available as an official Linux toolchain; the public API does not require Swift 6 language mode.

Add the package

dependencies: [
    .package(url: "https://github.com/slepp/requisite-swift", from: "0.1.0")
]

Add Requisite to the target that uses it.

Trust transitions

Tag data where it enters the application. A sink states the trust state it accepts:

import Requisite

enum InputError: Error { case invalidID }

func loadCustomer(_ id: Tainted<Int, Trusted>) { /* ... */ }

let raw = Tainted<String, Untrusted>.fromInput(requestField)
let customerID = try raw.sanitized { value in
    guard let id = Int(value) else { throw InputError.invalidID }
    return id
}
loadCustomer(customerID)

The policy closure defines Trusted for that destination. asUntrusted() lowers a trusted value when an API accepts a weaker state. There is no general map operation that could alter a trusted value while retaining its tag.

Confidence gates

Confidence accepts only finite values in 0...1. Confident keeps its value private until gate(using:) classifies it:

let forecast = try Confident(event, confidence: 0.97)

switch forecast.gate() {
case .highConfidence(let proof, let event):
    execute(proof, event)
case .likely(let event):
    requestReview(event)
case .unsure(let event):
    record(event)
}

Gate is @frozen: its three cases are an API commitment, and external clients can switch exhaustively without @unknown default. Adding another case would be a breaking change. Certain has no public initializer; safe client code receives it only from the highest tier. Custom Thresholds may raise its boundary but cannot lower it below 0.95.

Certain remains copyable. Swift 5.9 can express it as ~Copyable, but the associated Gate would then also be noncopyable. Clients would need ownership-aware pattern matching, and the oldest supported compiler could not use the gate with ordinary generic and protocol-based utilities. That design would enforce one-shot use, not stronger construction control. This capability records a completed check rather than ownership of a consumable resource, so the interoperability cost is not justified.

Once issued, a Certain value may be copied and reused for more than one call. It does not provide one-shot authorization. Applications that need that property should add a domain-specific consumable resource rather than infer it from confidence.

Freshness

Fresh records a monotonic fetch instant and a per-value TTL:

let quote = try Fresh(price, timeToLive: .seconds(30))

switch quote.result() {
case .success(let current):
    charge(current)
case .failure(let expired):
    archive(expired.value)
    refresh(after: expired.stale)
}

value() throws Stale while retaining the wrapper. result() returns StaleValue<Value> in its typed failure branch, preserving both the expired value and its timing information. The explicit-time initializer supports controlled fetch instants and validates them against the actual monotonic clock; callers cannot substitute a later now to admit a future timestamp. Negative TTLs are rejected.

ContinuousClock.Instant is meaningful only within the current process and clock epoch; do not persist it.

Error presentation

Requisite does not import Foundation. Errors conform to Error and CustomStringConvertible, not LocalizedError; presentation layers can map their typed payloads to localized messages.

Deliberate omissions

The Rust package's Live/with_live API is not included. Swift 5.9 has no generative lifetime or higher-ranked closure mechanism that prevents a wrapper from being returned or stored outside one specific closure invocation. Nonescaping closures alone do not establish that guarantee. Swift 6.2's ~Escapable constrains function-scope escape, but does not create a fresh per-call brand for this API. A closure helper would therefore express a convention rather than the Rust contract.

The trust wrapper is native rather than based on swift-tagged. Requisite needs restricted construction and state-specific transitions; a small local type makes those rules visible and avoids adding a dependency.

Validate

The Apple Swift 6 toolchain used for formatting includes swift format; the official Swift 5.9 Linux image does not, so formatting is gated separately from 5.9 compatibility. Linux Swift 6 language-mode builds, examples, and compiler contracts run in CI, while runtime tests use the Apple job because Swift 6.2's generated Linux XCTest discovery shim crashes before executing package tests. Swift 5 language-mode tests continue to run on Linux 5.9 and 6.2.

swift format format --configuration .swift-format --in-place --recursive Package.swift Sources Tests Examples
swift format lint --configuration .swift-format --recursive --strict Package.swift Sources Tests Examples
swift build -c release -Xswiftc -swift-version -Xswiftc 5 -Xswiftc -warnings-as-errors
swift test -Xswiftc -swift-version -Xswiftc 5 -Xswiftc -warnings-as-errors
swift build -c release -Xswiftc -swift-version -Xswiftc 6 -Xswiftc -warnings-as-errors
swift test -Xswiftc -swift-version -Xswiftc 6 -Xswiftc -warnings-as-errors
swift run RequisiteExample
SWIFT_LANGUAGE_VERSION=5 Scripts/check-api-contracts.sh
SWIFT_LANGUAGE_VERSION=6 Scripts/check-api-contracts.sh
Scripts/check-documentation.sh

The contract command type-checks a valid external client, then verifies that Swift rejects direct Certain and Trusted construction, upward trust widening, untrusted sinks, clock substitution, and use of a confident value without a gate. SwiftPM does not provide a built-in compile-fail test target, so these checks are a separate CI step. Documentation validation uses the package symbol graph and the DocC compiler from an Apple toolchain. .spi.yml selects Requisite for Swift Package Index documentation.

License

Licensed under Apache-2.0 or MIT, at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

About

Typed validated values, explicit trust transitions, exhaustive confidence levels, and per-value TTL freshness for Swift.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages