feat: add standalone WIP Text View signatures

Render one Object or paired Interface entity without path/context, lookups or wire-schema changes. Preserve declarations, summaries, optional/unit and empty distinctions; validate protocol semantics and bounded input before rendering. Add five spec/nested goldens and 13 external API tests plus a standalone doctest.

Validation: cargo fmt --all -- --check; cargo check --workspace --all-targets; cargo test --workspace --all-targets; cargo test --workspace --doc; nix develop -c bash (cargo clippy --workspace --all-targets -- -D warnings, RUSTDOCFLAGS='-D warnings' cargo doc --workspace --no-deps, cargo run --package wip-public-api-example); git diff --check. All passed. Default shell lacks clippy; declared Nix dev shell supplied it.
This commit is contained in:
2026-10-08 17:50:16 +09:00
parent d58809bdbc
commit 26cba3398f
16 changed files with 1570 additions and 4 deletions
+103
View File
@@ -0,0 +1,103 @@
# wip-text-view
Standalone Object and Interface signature rendering for WIP Text View, following
`4.2-text-view.md` at reference revision `6086f3c` and the Protocol semantics in
`3-protocol.md`. Only `wip-protocol` and the Rust standard library are dependencies.
No Client, Host, HTTP I/O, cache, UI framework or retrieval service is required.
## Single-entity API
```rust
use wip_protocol::{Object, InterfaceReference, InterfaceDescriptor, INTERFACE_FORMAT_V1};
use wip_text_view::{Interface, render_object, render_interface};
let reference = InterfaceReference { scope: "/".into(), name: "example".into() };
let object = Object {
name: "item".into(), description: None, interfaces: vec![reference.clone()],
r#ref: None, validator: None,
};
let object_signature = render_object(&object)?;
assert_eq!(object_signature,
"object {\n name: \"item\";\n interfaces: [\"/::example\"];\n}\n");
let descriptor = InterfaceDescriptor {
format: INTERFACE_FORMAT_V1.into(), documentation: None,
types: vec![], operations: vec![],
};
let interface = Interface { reference: &reference, descriptor: &descriptor };
let interface_signature = render_interface(&interface)?;
assert_eq!(interface_signature, "interface \"/::example\" {\n}\n");
# Ok::<(), wip_text_view::RenderError>(())
```
`Interface<'a>` is one borrowed SDK input entity containing a complete structured
reference and its corresponding descriptor, not a wire schema extension. The
caller pairs them from a coherent observation or publication. The descriptor has
no identity, so a renderer cannot prove the pairing's provenance or freshness.
For example, a caller with a validated `FetchInterfaceResponse` can borrow its
`interface` and `descriptor`; it does not pass `scope_ref` or validators.
An Object has no placement path. This API always emits `object { ... }`, never
requests a path and never infers one from name or ref. It requires no descriptor.
An Interface needs no Object, separate name/scope/context arguments or lookup.
There is no document aggregator, configuration or details selector.
## Output and validation
- UTF-8 plain text with LF, two-space indentation and a final LF, without ANSI,
Markdown fences, syntax highlighting or provider envelopes. Nonempty composites
and parameter lists use multiple lines; layout is deterministic here, but not a
byte-canonical protocol format.
- All Object references remain in Host order. All types, then all operations,
fields, parameters and cases remain in declaration order, without elision.
- Missing vs empty description/documentation, optional vs unit, no payload vs
unit payload, and empty record/enum/union/declaration sets remain distinct.
- Full absolute references use component quoting **and** outer JSON-string quoting.
Identifiers use keywords/backtick rules. All Host strings use standard JSON
escapes plus DEL/C1, Unicode line/paragraph separator and bidi-control escapes.
No normalization, case conversion, URL encoding or input mutation occurs.
- Documentation summaries are displayed in their prescribed positions, including
return documentation between `)` and `->`. Details are not displayed. Host text
is data, not renderer instructions or markup; quoting alone is not a prompt
injection defense. AI adapters must preserve the trust boundary.
- Object ref/validator, children, domain data and Interface scope_ref/validator
are not signature content. No annotations are inferred from operation names.
Both functions return `Result<String, RenderError>` and never return partial
signatures. Resource checks happen first, followed by existing Protocol validators:
Object shape and reference uniqueness, or Interface reference shape plus descriptor
structure, duplicate names, unknown Named references and cycles. Descriptor format
is checked after semantics: valid but unsupported formats return
`UnsupportedDescriptorFormat`, distinct from `InvalidDescriptor`. Supported format
is exactly `wip-interface/1`. Other validation errors preserve the Protocol's typed
`ValidationError` (also exposed as the error source).
Bounded input limits are **1 MiB** of total string/opaque byte data, **16,384**
declarations/fields/parameters/cases/type expressions/references, **256** named type
declarations, and **64** TypeExpr nesting levels. Details and Object runtime
metadata count toward input bytes even though they are not rendered. A limit
returns `RenderError::ResourceLimit`, never truncates or supplies `...`. Counts
bound allocation and recursive validation before those validators run; these are
library safety limits, not Protocol restrictions.
Without path/context, this library cannot attest Object scope ancestry, placement,
observation consistency, identity, membership, authorization or currentness.
Callers validate those at their observation boundary. Callers also own retrieval,
resolution, cache, selection, multiple-signature arrangement and AI/UI adaptation.
Text is display-only, not serialization, a digest, identity/cache key or call
target. No parser or round-trip API is supplied.
## Validation
`tests/signatures.rs` is an external consumer of the public APIs. Goldens cover
the spec's Object, issue.read, CloseResult, search/return documentation and
identifier examples (with permitted multiline layout), plus nested documentation.
The suite checks all TypeExpr variants/positions, ordering and empty distinctions,
keyword and Unicode names, both reference quoting layers, all required control
escapes, input immutability, metadata exclusion and typed rejection cases.
```console
cargo test -p wip-text-view
cargo clippy -p wip-text-view --all-targets -- -D warnings
RUSTDOCFLAGS='-D warnings' cargo doc -p wip-text-view --no-deps
```