#![allow(dead_code)]
use openusd::Result;
use openusd::sdf::{self, FieldKey, Path};
use openusd::tf;
use openusd::usd::{Prim, Stage, VersionFilter};
pub(crate) fn read_token(stage: &Stage, prim: &Path, name: &str) -> Result<Option<tf::Token>> {
Ok(stage.field::<tf::Token>(prim.append_property(name)?, FieldKey::Default)?)
}
pub(crate) fn get_typed(
stage: &Stage,
path: impl sdf::IntoPath,
type_name: impl Into<tf::Token>,
) -> Result<Option<Prim>> {
let prim = stage.prim(path)?;
Ok(is_typed(&prim, type_name)?.then_some(prim))
}
pub(crate) fn is_typed(prim: &Prim, type_name: impl Into<tf::Token>) -> Result<bool> {
let type_name = type_name.into();
is_any_typed(prim, &[type_name.as_str()])
}
pub(crate) fn is_any_typed(prim: &Prim, type_names: &[&str]) -> Result<bool> {
if let Some(schema_type) = prim.schema_type()? {
let registry = prim.stage().schema_registry();
return Ok(type_names
.iter()
.any(|name| registry.is_a(&schema_type, &tf::Token::from(*name))));
}
let authored = prim.type_name()?;
Ok(type_names.iter().any(|name| authored.as_deref() == Some(*name)))
}
pub(crate) fn get_typed_in_family(
stage: &Stage,
path: impl sdf::IntoPath,
family: impl Into<tf::Token>,
) -> Result<Option<Prim>> {
let prim = stage.prim(path)?;
Ok(prim.is_in_family(family, VersionFilter::All)?.then_some(prim))
}
pub(crate) fn get_with_api(stage: &Stage, path: impl sdf::IntoPath, apis: &[impl AsRef<str>]) -> Result<Option<Prim>> {
let prim = stage.prim(path)?;
let applied = prim.api_schemas()?;
if apis.iter().any(|a| applied.iter().any(|s| s.as_str() == a.as_ref())) {
Ok(Some(prim))
} else {
Ok(None)
}
}
macro_rules! impl_token_value {
($($ty:ty),+ $(,)?) => {$(
impl From<$ty> for $crate::openusd::sdf::Value {
fn from(value: $ty) -> Self {
$crate::openusd::sdf::Value::Token(value.as_token().into())
}
}
impl TryFrom<$crate::openusd::sdf::Value> for $ty {
type Error = $crate::openusd::sdf::CastError;
fn try_from(value: $crate::openusd::sdf::Value) -> Result<Self, Self::Error> {
match &value {
$crate::openusd::sdf::Value::Token(s) => <$ty>::from_token(s.as_str()),
_ => None,
}
.ok_or_else(|| $crate::openusd::sdf::CastError::TypeMismatch {
target: stringify!($ty),
actual: (&value).into(),
})
}
}
)+};
}
pub(crate) use impl_token_value;
#[cfg(test)]
mod tests {
use super::*;
use openusd::Result;
use openusd::usd::{FamilySource, SchemaRegistry};
const MANIFEST: &str = r#"#usda 1.0
def "NonboundableLightBase"
{
uniform token schemaKind = "abstractTyped"
}
def "DistantLight"
{
uniform token schemaKind = "concreteTyped"
uniform token[] bases = ["NonboundableLightBase"]
}
def "DomeLight"
{
uniform token schemaKind = "concreteTyped"
uniform token[] bases = ["NonboundableLightBase"]
}
def "DomeLight_1"
{
uniform token schemaKind = "concreteTyped"
uniform token[] bases = ["NonboundableLightBase"]
}
"#;
const SCHEMATICS: &str = r#"#usda 1.0
class "NonboundableLightBase"
{
}
class DistantLight "DistantLight"
{
}
class DomeLight "DomeLight"
{
}
class DomeLight_1 "DomeLight_1"
{
}
"#;
fn schema_stage() -> Result<Stage> {
let registry = SchemaRegistry::builder()
.family(FamilySource {
name: "test",
manifest: MANIFEST,
schematics: SCHEMATICS,
resolved_location: None,
})
.expect("test family registers")
.build()
.expect("test registry builds");
Stage::builder().schema_registry(registry).in_memory("anon.usda")
}
#[test]
fn typed_gate_subtype() -> Result<()> {
let stage = schema_stage()?;
stage.define_prim("/Sun")?.set_type_name("DistantLight")?;
assert!(get_typed(&stage, "/Sun", "NonboundableLightBase")?.is_some());
assert!(get_typed(&stage, "/Sun", "DistantLight")?.is_some());
assert!(get_typed(&stage, "/Sun", "DomeLight_1")?.is_none());
stage.define_prim("/Base")?.set_type_name("NonboundableLightBase")?;
assert!(get_typed(&stage, "/Base", "DistantLight")?.is_none());
Ok(())
}
#[test]
fn family_gate_versions() -> Result<()> {
let stage = schema_stage()?;
stage.define_prim("/Old")?.set_type_name("DomeLight")?;
stage.define_prim("/New")?.set_type_name("DomeLight_1")?;
stage.define_prim("/Sun")?.set_type_name("DistantLight")?;
assert!(get_typed_in_family(&stage, "/Old", "DomeLight")?.is_some());
assert!(get_typed_in_family(&stage, "/New", "DomeLight")?.is_some());
assert!(get_typed_in_family(&stage, "/Sun", "DomeLight")?.is_none());
Ok(())
}
#[test]
fn family_gate_no_schema_data() -> Result<()> {
let stage = Stage::builder().in_memory("anon.usda")?;
stage.define_prim("/New")?.set_type_name("DomeLight_1")?;
stage.define_prim("/Sun")?.set_type_name("DistantLight")?;
assert!(get_typed_in_family(&stage, "/New", "DomeLight")?.is_some());
assert!(get_typed_in_family(&stage, "/Sun", "DomeLight")?.is_none());
stage.define_prim("/Odd")?.set_type_name("DomeLight_01")?;
assert!(get_typed_in_family(&stage, "/Odd", "DomeLight")?.is_none());
Ok(())
}
#[test]
fn typed_gate_no_schema_data() -> Result<()> {
let stage = Stage::builder().in_memory("anon.usda")?;
stage.define_prim("/Sun")?.set_type_name("DistantLight")?;
assert!(get_typed(&stage, "/Sun", "DistantLight")?.is_some());
assert!(get_typed(&stage, "/Sun", "NonboundableLightBase")?.is_none());
Ok(())
}
}