Expand description
obj-derive — procedural macros for obj.
This crate provides #[derive(obj::Document)], which emits the
obj_core::Document
implementation for a user struct. The derive is intentionally
small — it fills in the trait’s associated constants
(COLLECTION, VERSION) from optional #[obj(...)] attributes
and emits an indexes() override whenever any field carries an
#[obj(index ...)] attribute.
§Supported attributes
Struct-level (#[obj(...)] directly above the struct keyword):
version = N(integer ≥ 0) — setsDocument::VERSION.collection = "name"(non-empty string literal) — setsDocument::COLLECTION.
Multiple #[obj(...)] attributes compose; the same scalar key
(version, collection) declared twice is a compile error.
Struct-level composite (one or more occurrences compose, each
adding one Composite IndexSpec):
-
index_composite(fields = ("a", "b"), name = "by_a_b")— emit aCompositeIndexSpecspanning the listed fields.namedefaults to the field names joined with__. The referenced fields must exist on the struct; fewer than two is a compile error.This is a deliberate rename from the syntax shown in
design.md(#[obj(index = ("a", "b"))]). The tuple-on-the- right form is ambiguous to parse alongside the field-levelindex = unique/index = eachkeyword variants — putting composite indexes under their ownindex_compositekey keeps each attribute’s grammar locally unambiguous. The format-docs reflect the actual syntax; design.md prose will be aligned in a future doc pass.
Field-level (#[obj(...)] on a struct field):
index— emit aStandardIndexSpecfor this field.index = unique— emit aUniqueIndexSpecfor this field.index = each— emit anEachIndexSpecfor this field. The field type must syntactically beVec<...>— otherwise the derive errors at compile time.name = "..."— alongside anyindex = ..., overrides the default index name (which is the field name).
Struct-level historical schema registry (M10 #82):
history(v1 = OldType1, v2 = OldType2)— emit aDocument::historical_schemas()override that lifts each version into a(version, DynamicSchema)pair via<OldType as ::obj::Schema>::schema(). The keys (v1,v2, …) must bevNfor integerN≥ 1; the values are arbitrary type paths. Each named type must implement::obj::Schema(hand-impls are accepted; the derive auto-implementsSchemafor types that opt in via#[obj(history(...))]or#[obj(schema)]). Entries are emitted in ascending version order.schema— explicitly opt the current type into a derivedSchemaimpl WITHOUT declaring any history. Useful when the type is referenced from a future version’shistory(...).
When either history(...) or schema is declared, the derive
emits a companion impl ::obj::Schema block whose schema()
body maps each field to a DynamicSchema variant. Scalar
primitives (bool, u*, i*, f*, String) map directly; Vec<T>
maps to DynamicSchema::seq(<T as Schema>::schema()); anything
else delegates via <T as ::obj::Schema>::schema(), which fails
to compile if T lacks a Schema impl.
§Serde requirements
The derive does NOT emit serde::Serialize or
serde::Deserialize for you. Users still write
#[derive(serde::Serialize, serde::Deserialize)] on the struct
alongside #[derive(obj::Document)].
§Power-of-ten posture
- Rule 4 — every function in this crate is ≤ 60 lines.
- Rule 7 — every fallible path returns
syn::Result<...>;unwrap/expectappear only on infallible primitives. - Rule 9 — generated code is minimal and inspectable. Every
emitted item is prefixed by a
// auto-generated by #[derive(Document)]marker socargo expandoutput is easy to spot.
Derive Macros§
- Document
- Derive macro for
obj::Document.