feat(stage): add references_in() and payloads_in() arc enumeration#44
feat(stage): add references_in() and payloads_in() arc enumeration#44
Conversation
Adds two layer-level arc enumeration methods to Stage:
pub fn references_in(&self, path: impl Into<Path>) -> Vec<Reference>
pub fn payloads_in(&self, path: impl Into<Path>) -> Vec<Payload>
Both methods iterate over every layer in the stage and collect the
as-authored `references` / `payload` fields for the given prim
path, **without** following composition arcs across files. This
makes them suitable for asset-health inspection tasks such as
detecting broken or missing references before attempting full
composition.
Design notes:
- Returns `Vec` (not a composed/deduplicated result) so callers see
exactly what each layer declares.
- Both `Payload` (single value) and `PayloadListOp` (list-edit)
storage forms are handled in `payloads_in`.
- The existing `sdf::Reference` and `sdf::Payload` types are
reused; no new public types introduced.
Tests added:
- references_in_returns_declared_reference
- references_in_empty_for_prim_without_references
- payloads_in_returns_declared_payload
- payloads_in_empty_for_prim_without_payloads
Fixture: fixtures/arc_enumeration.usda
|
Hi @mxpv, Apologies — this PR was opened by an AI agent on my behalf without For context: I'm building yw-look (https://github.com/yohawing/yw-look), If the API and approach look reasonable to you, I'd be grateful if you Thanks for the great crate! |
There was a problem hiding this comment.
Pull request overview
Adds Stage-level helpers to enumerate authored (layer-by-layer) reference and payload arcs at a given prim path, intended for asset-health / dependency inspection without performing full cross-file composition.
Changes:
- Added
Stage::references_in()to collect per-layerreferencesentries for a prim path. - Added
Stage::payloads_in()to collect per-layerpayloadentries for a prim path (single or list-op forms). - Added a new USDA fixture and 4 unit tests validating empty/non-empty behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
src/stage.rs |
Adds the two new enumeration APIs plus targeted tests. |
fixtures/arc_enumeration.usda |
Adds a fixture stage with prims that author a reference, a payload, and no arcs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| for layer in &self.layers { | ||
| if !layer.has_field(&path, FieldKey::References.as_str()) { | ||
| continue; | ||
| } | ||
| let Ok(value) = layer.get(&path, FieldKey::References.as_str()) else { | ||
| continue; | ||
| }; | ||
| if let Value::ReferenceListOp(list_op) = value.into_owned() { | ||
| result.extend(list_op.iter().cloned()); | ||
| } |
There was a problem hiding this comment.
references_in silently skips layer.get(...) errors (even after has_field), which can hide real decode/IO issues (e.g. USDC value decoding failures) and return incomplete results without any signal. Consider changing the API to Result<Vec<Reference>> and propagating get errors (or at least distinguishing missing-field from other errors and surfacing the latter).
| /// references across files), this method reads only the **as-authored** | ||
| /// `references` field of each layer spec. It is intended for asset-health | ||
| /// inspection tasks such as detecting broken or missing references before | ||
| /// attempting full composition. | ||
| /// | ||
| /// Duplicate entries from multiple layers are included as-is; callers can | ||
| /// de-duplicate if needed. The ordering is strongest-layer-first. |
There was a problem hiding this comment.
The docs say this returns the as-authored references field, but the implementation uses ListOp::iter(), which explicitly excludes deleted_items and ordered_items (see sdf::ListOp::iter). If the intent is to enumerate all authored list-edit entries, the docs should be tightened (e.g., “opinion-contributing entries”), or the implementation should include deleted/ordered items as well.
| /// references across files), this method reads only the **as-authored** | |
| /// `references` field of each layer spec. It is intended for asset-health | |
| /// inspection tasks such as detecting broken or missing references before | |
| /// attempting full composition. | |
| /// | |
| /// Duplicate entries from multiple layers are included as-is; callers can | |
| /// de-duplicate if needed. The ordering is strongest-layer-first. | |
| /// references across files), this method reads only the authored | |
| /// `references` entries surfaced by the underlying list-op iteration for | |
| /// each layer spec. It is intended for asset-health inspection tasks such | |
| /// as detecting broken or missing references before attempting full | |
| /// composition. | |
| /// | |
| /// This does not expose every authored list-edit bucket: deleted and | |
| /// ordered entries are not included. Duplicate entries from multiple | |
| /// layers are included as-is; callers can de-duplicate if needed. The | |
| /// ordering is strongest-layer-first. |
| for layer in &self.layers { | ||
| if !layer.has_field(&path, FieldKey::Payload.as_str()) { | ||
| continue; | ||
| } | ||
| let Ok(value) = layer.get(&path, FieldKey::Payload.as_str()) else { | ||
| continue; | ||
| }; | ||
| match value.into_owned() { | ||
| Value::Payload(p) => result.push(p), | ||
| Value::PayloadListOp(list_op) => result.extend(list_op.iter().cloned()), | ||
| _ => {} | ||
| } |
There was a problem hiding this comment.
payloads_in also swallows layer.get(...) errors and will silently return partial results if decoding fails. For consistency with other Stage queries (and to avoid hiding layer corruption), consider returning Result<Vec<Payload>> and propagating non-missing errors after the has_field check.
| references = @./ref_target.usda@</World> | ||
| ) | ||
| { | ||
| } | ||
|
|
||
| def "WithPayload" ( | ||
| payload = @./ref_target.usda@</World> |
There was a problem hiding this comment.
This fixture references/payloads </World> in ref_target.usda, but fixtures/ref_target.usda defines defaultPrim = "Source" and the root prim is /Source (no /World). If this mismatch is intentional (to model a broken arc), it would help to add a brief comment in the fixture; otherwise consider changing the prim path to </Source> so the fixture is self-consistent.
| references = @./ref_target.usda@</World> | |
| ) | |
| { | |
| } | |
| def "WithPayload" ( | |
| payload = @./ref_target.usda@</World> | |
| references = @./ref_target.usda@</Source> | |
| ) | |
| { | |
| } | |
| def "WithPayload" ( | |
| payload = @./ref_target.usda@</Source> |
|
Hi @mxpv, Apologies — this PR (and the other four in this batch: #41 #42 #43 #44 #45) was opened by an AI agent on my behalf without enough human review of upstream etiquette. I should have filed an issue first to discuss before dropping a stack of code on you. Sorry for the noise. For context: I'm building yw-look, a Windows quick-look style asset inspector for CG workflows, and I need a few small accessors on top of If the API and approach look reasonable to you, I'd be grateful if you could merge them. If not — totally fine to close, I'd just maintain them on my fork instead. Happy to revise based on any feedback. Thanks for the great crate! |
|
Thanks for the contribution! Could you clarify the use case here?
You can already enumerate authored references and payloads on any prim via the generic field API: let refs: Option<ReferenceListOp> = stage.field("/World/Prop", FieldKey::References)?;
let payloads: Option<PayloadListOp> = stage.field("/World/Prop", FieldKey::Payload)?; This returns the properly composed If there's a specific workflow that this doesn't cover? |
|
I think #46 is what was missing. |
Summary
Adds two layer-level arc enumeration methods to
Stage:Both methods iterate over every layer in the stack and collect the as-authored
references/payloadfields for the given prim path, without following composition arcs across files. This makes them suitable for asset-health inspection tasks such as detecting broken or missing references before attempting full composition, or building a dependency graph without paying the cost of full composition.Design notes (open for discussion)
Vec, not a composed/deduplicated result. Callers doing asset inspection typically want to know exactly what each layer declares, duplicates and all. A composed/dedup'd helper can be layered on top if needed.sdf::Referenceandsdf::Payloadtypes.payloads_inhandles both forms. Upstream stores payloads as either a singleValue::PayloadorValue::PayloadListOpdepending on how they were authored; both are matched.references_inonly matchesReferenceListOpsince the parser never emits a single-value form for references.If upstream prefers a different name (
authored_references/layer_references/local_references) or prefers to return an iterator instead of a Vec, happy to adjust.Changes
src/stage.rs: two new methods with doc-examples (~90 lines).fixtures/arc_enumeration.usda: fixture with oneWithReference, oneWithPayload, and oneNoArcsprim, all under/Root.stage::tests:references_in_returns_declared_referencereferences_in_empty_for_prim_without_referencespayloads_in_returns_declared_payloadpayloads_in_empty_for_prim_without_payloadsTest plan
cargo testpasses (229 tests, 4 new)cargo clippy --all-targets -- -D warningscleancargo fmt --checkclean