soothfast-spec API reference#
Generated by
cargo soothfast docs referenceMeasurements and verified claims come from the latest baseline; CI gates them.
soothfast_spec#
DeclaredOp struct#
pub struct DeclaredOp
One operation a spec declares.
RouteDecl struct#
pub struct RouteDecl
One #[soothfast::route] annotation from the code.
SpecKind enum#
pub enum SpecKind
Spec dialects soothfast reads and writes.
sniff_kind function#
pub fn sniff_kind(filename: &str, text: &str) -> Option<SpecKind>
Sniff the dialect from filename + content.
soothfast_spec::SpecKind#
render function#
pub fn render(self, path: &str, value: &Value) -> String
Serialize a document for writing to path.
GraphQL ignores the extension because SDL is its only serialization;
the rest honour .json and default to the dialect's usual form.
soothfast_spec::asyncapi#
document function#
pub fn document(info: &Info, ops: &[Operation]) -> Document
Assemble an AsyncAPI 3.0 document.
soothfast_spec::asyncapi::diff#
diff function#
pub fn diff(old: &Value, new: &Value) -> Vec<Change>
Compare two generated documents, oldest first.
soothfast_spec::compat#
Change struct#
pub struct Change
One difference between two versions of a spec.
Direction enum#
pub enum Direction
Which way the data flows, which decides what "required" costs.
SchemaDiff struct#
pub struct SchemaDiff<'a>
Compares schemas belonging to two revisions of a document.
Both documents are held because a $ref only means anything relative to
the document it came from: the same pointer can name different shapes on
either side of the comparison.
Severity enum#
pub enum Severity
Whether a change can break an existing consumer.
compare_keys function#
pub fn compare_keys<F>( changes: &mut Vec<Change>, old: &BTreeMap<String, Value>, new: &BTreeMap<String, Value>, noun: &str, mut both: F, ) where F: FnMut(&mut Vec<Change>, &str, &Value, &Value),
Compare two maps of named entries, reporting bare presence changes and
handing matched pairs to both.
Added and removed entries mean the same thing in every dialect — a tool, a channel, an operation or a field that appears or disappears — so only the wording and the severity of a removal differ between callers.
deref function#
pub fn deref<'a>(doc: &'a Value, schema: &'a Value) -> &'a Value
Follow a local $ref to the schema it names, once.
Resolution is by JSON Pointer rather than by a fixed components/schemas
prefix: dialects disagree on where shared schemas live (#/$defs/X inside
one tool's schema, #/components/schemas/X at the document root), and the
pointer already says which.
is_compatible function#
pub fn is_compatible(changes: &[Change]) -> bool
True when nothing in the diff would break an existing consumer.
render function#
pub fn render(v: &Value) -> String
A JSON value as it should read inside a diff message.
required_set function#
pub fn required_set(schema: &Value) -> BTreeSet<&str>
The required names of an object schema.
sort function#
pub fn sort(changes: &mut [Change])
Order a diff for reporting: breaking first, then by location, so output is stable between runs.
soothfast_spec::dialect#
Document struct#
pub struct Document
A rendered document plus anything that had to be reconciled to build it.
Info struct#
pub struct Info
Document metadata that no amount of code reading can derive.
Operation struct#
pub struct Operation
One operation to emit, pairing spec identity with the inferred shape.
soothfast_spec::graphql#
document function#
pub fn document(info: &Info, ops: &[Operation]) -> Document
Assemble a GraphQL type graph.
from_sdl function#
pub fn from_sdl(text: &str) -> Result<Value, String>
to_sdl function#
pub fn to_sdl(doc: &Value) -> String
soothfast_spec::graphql::diff#
diff function#
pub fn diff(old: &Value, new: &Value) -> Vec<Change>
Compare two generated type graphs, oldest first.
soothfast_spec::mcp#
diff function#
pub fn diff(old: &Value, new: &Value) -> Vec<Change>
Compare two generated tool manifests, oldest first.
A tool's arguments flow inward and its result flows outward, so the two schemas are compared in opposite directions — the same asymmetry OpenAPI has between a request body and a response.
document function#
pub fn document(_info: &Info, ops: &[Operation]) -> Document
Assemble an MCP tool manifest.
info is unused: a tool manifest has no document-level metadata to carry,
and inventing a wrapper object for it would break every client that reads
the standard { "tools": [...] } shape.
soothfast_spec::openapi#
document function#
pub fn document(info: &Info, ops: &[Operation]) -> Document
Assemble an OpenAPI 3.1 document.
soothfast_spec::openapi::diff#
diff function#
pub fn diff(old: &Value, new: &Value) -> Vec<Change>
Compare two generated documents, oldest first.
Returns every difference found, breaking ones first, then by location so output is stable between runs.
soothfast_spec::probe#
soothfast_spec::probe::assert#
Assertion struct#
pub struct Assertion
One parsed assertion, kept with its source line for reporting.
soothfast_spec::probe::baseline#
Baseline struct#
pub struct Baseline
The lock file: probe name → locked state.
Class enum#
pub enum Class
A field's population class across accepted runs.
Findings struct#
pub struct Findings
One probe's gate findings against the lock.
ProbeLock struct#
pub struct ProbeLock
One probe's locked state.
soothfast_spec::probe::coverage#
declared_paths function#
pub fn declared_paths(schema: &Value, spec: &Value) -> BTreeSet<String>
Enumerate the field paths schema declares, resolving $refs through
the spec's components/schemas. allOf branches all apply and are
merged; oneOf/anyOf children are conditional on which branch the
value takes, so nothing below a union counts as declared. Recursive
schemas stop at the repeated $ref; an open schema ({}) declares
nothing below its own path.
soothfast_spec::probe::population#
populate function#
pub fn populate(value: &Value) -> BTreeMap<String, bool>
Flatten value into path → populated. Paths are dotted, arrays
collapse to [], and a path already seen stays populated once any
occurrence carried data.
soothfast_spec::probe::shape#
Violation struct#
pub struct Violation
One structural disagreement between response and schema.
response_schema function#
pub fn response_schema<'a>( spec: &'a Value, method: &str, path: &str, status: u16, ) -> Option<&'a Value>
Find the JSON response schema the spec declares for method on the
concrete path (path template segments {like_this} match anything),
for the given status code.
template_matches function#
pub fn template_matches(template: &str, concrete: &[&str]) -> bool
Whether a path template matches concrete path segments, treating any
{seg} as a wildcard. Segment counts must agree.
validate function#
pub fn validate(value: &Value, schema: &Value, spec: &Value) -> Vec<Violation>
Validate value against schema, resolving $refs through the
spec's components/schemas. Returns every violation found.
soothfast_spec::proto#
ProtoField struct#
pub struct ProtoField
One field, from either a .proto message or a struct's #[prost(..)]
attributes. ty is the bare type token ("string", "sint64", ...).
ProtoReconciliation struct#
pub struct ProtoReconciliation
Outcome of matching struct fields to a .proto message by tag number.
parse_proto_message function#
pub fn parse_proto_message(text: &str, message: &str) -> Result<Vec<ProtoField>, String>
Parse the scalar fields of one message NAME { ... } block from .proto
text. Nested message/oneof/enum bodies are skipped by brace depth, not
descended into — this is a flat wire-format check, not a full parser.
parse_struct_prost_fields function#
pub fn parse_struct_prost_fields( source: &str, struct_name: &str, ) -> Result<Vec<ProtoField>, String>
Extract #[prost(..)] field declarations from one struct in Rust source
text, by scanning forward from each #[prost(..)] occurrence within the
struct's brace-matched body to the field name it decorates. Deliberately
not a full Rust parser (no syn) — same tradeoff as [parse_proto_message].
reconcile_proto function#
pub fn reconcile_proto( spec_fields: &[ProtoField], code_fields: &[ProtoField], ) -> ProtoReconciliation
Match struct fields to declared .proto fields by tag number, then
check that type (wire-compatibly) and name agree.
soothfast_spec::providers#
parse function#
pub fn parse(kind: SpecKind, text: &str) -> Result<Vec<DeclaredOp>, String>
Parse one spec document into the operations it declares, dispatching on dialect: OpenAPI, AsyncAPI, and MCP tool schemas are YAML-or-JSON; GraphQL is SDL text.
soothfast_spec::reconcile#
Reconciliation struct#
pub struct Reconciliation
Outcome of matching #[route] declarations against a spec's operations:
what's missing on each side, what disagrees, and what lined up.
reconcile function#
pub fn reconcile(declared: &[DeclaredOp], routes: &[RouteDecl]) -> Reconciliation
Match routes to declared operations by operation id, then check that method and path agree where the route states them.
soothfast_spec::schema#
Docs struct#
pub struct Docs<'a>
Extraction struct#
pub struct Extraction
A schema plus everything it referenced and everything it could not see.
Extractors struct#
pub struct Extractors
Gap enum#
pub enum Gap
A place the extractor could not derive a shape, and why.
Every gap names the exact type or field so the report is actionable, and carries an open schema rather than a guessed one.
Overrides struct#
pub struct Overrides
Resolver struct#
pub struct Resolver<'a>
RouteShape struct#
pub struct RouteShape
TypeMapping enum#
pub enum TypeMapping
TypeTable struct#
pub struct TypeTable
extract_named function#
pub fn extract_named<'a>( docs: impl Into<Docs<'a>>, table: &'a TypeTable, name: &str, ) -> Result<Extraction, String>
Extract the schema for a named type from a rustdoc JSON document.
Takes one document, or a [Docs] carrying the workspace-local crates the
type may actually be defined in. Used for the
#[route(response = "...")] override; signature-driven extraction goes
through [Resolver::resolve] directly.
soothfast_spec::schema::graphql_attrs#
ContainerAttrs struct#
pub struct ContainerAttrs
Container-level #[graphql(...)] options that change wire names.
FieldAttrs struct#
pub struct FieldAttrs
Field-level #[graphql(...)] options.
Rename enum#
pub enum Rename
How a container renames every field or enum item beneath it.
These are the six rules async-graphql accepts — notably not kebab-case, which serde has and async-graphql does not.
container function#
pub fn container(raw: &[Value]) -> ContainerAttrs
Parse container-level #[graphql(...)] attributes.
field function#
pub fn field(raw: &[Value]) -> FieldAttrs
Parse field-level #[graphql(...)] attributes.
variant function#
pub fn variant(raw: &[Value]) -> FieldAttrs
Parse variant-level #[graphql(...)] attributes. Enum items take name
but have no skip, so the field parser's answer is reused as-is.
wire_name_field function#
pub fn wire_name_field(rust_name: &str, f: &FieldAttrs, c: &ContainerAttrs) -> String
The wire name of a field: name if given, else the container's rule.
wire_name_variant function#
pub fn wire_name_variant(rust_name: &str, v: &FieldAttrs, c: &ContainerAttrs) -> String
The wire name of an enum item: name if given, else the container's rule.
soothfast_spec::schema::route_sig#
Parameter struct#
pub struct Parameter
One query, path or header parameter.
RequestBody struct#
pub struct RequestBody
A request body and the content type it arrives as.
Response struct#
pub struct Response
One response, keyed by status code in [RouteShape].
Role enum#
pub enum Role
What a handler parameter contributes to the wire contract.
infer function#
pub fn infer<'a>( docs: impl Into<Docs<'a>>, table: &'a TypeTable, extractors: &Extractors, handler_path: &str, route_path: &str, overrides: &Overrides, ) -> Result<RouteShape, String>
Infer the wire contract of one handler.
handler_path is the #[route] id (app::routes::create_item) and
route_path the URL template, whose {placeholders} name tuple path
parameters that the Rust signature leaves positional.
soothfast_spec::schema::serde_attrs#
ContainerAttrs struct#
pub struct ContainerAttrs
Container-level serde options that change the emitted schema.
FieldAttrs struct#
pub struct FieldAttrs
Field-level serde options that change the emitted schema.
Rename enum#
pub enum Rename
How a container renames every field or variant beneath it.
container function#
pub fn container(raw: &[serde_json::Value]) -> ContainerAttrs
Parse container-level #[serde(...)] attributes.
field function#
pub fn field(raw: &[serde_json::Value]) -> FieldAttrs
Parse field-level #[serde(...)] attributes.
serde_args function#
pub fn serde_args(raw: &[serde_json::Value]) -> Vec<String>
Pull the serde(...) argument text out of rustdoc's attribute entries.
wire_name_field function#
pub fn wire_name_field(rust_name: &str, f: &FieldAttrs, c: &ContainerAttrs) -> String
The wire name of a field, applying rename then the container rule.
wire_name_variant function#
pub fn wire_name_variant(rust_name: &str, v: &ContainerAttrs, c: &ContainerAttrs) -> String
The wire name of a variant, applying rename then the container rule.
soothfast_spec::schema::types#
Subst type_alias#
pub type Subst = BTreeMap<String, (Value, String)>
Generic parameter bindings captured at a concrete use site: parameter name → (resolved schema, display name used for component naming).
soothfast_spec::schema::types::Resolver#
resolve function#
pub fn resolve(&mut self, ty: &Value, subst: &Subst, at: &str) -> Value
Resolve one rustdoc type node into a JSON Schema.
at is a human-readable location (Item.when) used for gap reports.
soothfast_spec::serialize#
from_text function#
pub fn from_text(text: &str) -> Result<Value, String>
Parse YAML-or-JSON text into a JSON value (JSON is valid YAML, but JSON is tried first for exactness).
to_json function#
pub fn to_json(value: &Value) -> String
Render a document as pretty JSON, for .json spec files.
Hand-rolled rather than serde_json::to_string_pretty for one reason: the
same key order the YAML path uses. A tool manifest whose description
sorts above its name is valid and unreadable, and the two serializations
of one document should not disagree about what comes first.
to_yaml function#
pub fn to_yaml(value: &Value) -> String
Render a document as YAML, with well-known keys in conventional order.