use openusd::{sdf, tf, usd};
use proc_macro2::TokenStream;
use quote::quote;
use super::{ident, items, lower, value};
use crate::error::Error;
pub(super) fn declarations(family: &usd::SchemaFamily<'_>) -> Result<TokenStream, Error> {
let schemas = family.schemas().iter().map(schema).collect::<Result<Vec<_>, Error>>()?;
let metadata = family
.declared_metadata()
.iter()
.map(|decl| metadata(family.name(), decl))
.collect::<Result<Vec<_>, Error>>()?;
let metadata = (!metadata.is_empty()).then(|| quote! { .metadata(&[#(#metadata),*]) });
let kinds = family.declared_kinds().iter().map(|decl| {
let name = decl.name();
let base = decl.declared_base().map(|base| quote! { .base(#base) });
quote! { ::openusd::kind::Decl::new(#name) #base }
});
let kinds = (!family.declared_kinds().is_empty()).then(|| quote! { .kinds(&[#(#kinds),*]) });
let schemas_const = ident(items::SCHEMAS);
let library_name = ident(items::LIBRARY_NAME);
Ok(quote! {
pub const #schemas_const: &::openusd::usd::SchemaFamily<'static> =
&::openusd::usd::SchemaFamily::new(#library_name, &[#(#schemas),*]) #metadata #kinds;
})
}
fn schema(decl: &usd::SchemaDecl<'_>) -> Result<TokenStream, Error> {
let identifier = decl.identifier();
let kind = lower::kind_constant(decl.kind());
let properties = decl
.declared_properties()
.iter()
.map(|property| self::property(identifier, property))
.collect::<Result<Vec<_>, Error>>()?;
let fields = fields(identifier, decl.declared_fields())?;
let list = |names: &[&str]| (!names.is_empty()).then(|| quote! { &[#(#names),*] });
let bases = list(decl.declared_bases()).map(|names| quote! { .bases(#names) });
let applied = list(decl.declared_applied_api_schemas()).map(|names| quote! { .applied_api_schemas(#names) });
let overrides =
list(decl.declared_override_property_names()).map(|names| quote! { .override_property_names(#names) });
let auto_apply = list(decl.declared_auto_apply_to()).map(|names| quote! { .auto_apply_to(#names) });
let can_only = list(decl.declared_can_only_apply_to()).map(|names| quote! { .can_only_apply_to(#names) });
let instance_names =
list(decl.declared_allowed_instance_names()).map(|names| quote! { .allowed_instance_names(#names) });
let fallbacks = list(decl.declared_fallback_types()).map(|names| quote! { .fallback_types(#names) });
let prefix = decl
.declared_property_namespace_prefix()
.map(|prefix| quote! { .property_namespace_prefix(#prefix) });
let restrictions = decl.declared_instance_restrictions();
let restrictions = (!restrictions.is_empty()).then(|| {
let entries = restrictions.iter().map(|(instance, allowed)| {
quote! { (#instance, &[#(#allowed),*]) }
});
quote! { .instance_restrictions(&[#(#entries),*]) }
});
let properties = (!properties.is_empty()).then(|| quote! { .properties(&[#(#properties),*]) });
let fields = (!fields.is_empty()).then(|| quote! { .fields(&[#(#fields),*]) });
Ok(quote! {
::openusd::usd::SchemaDecl::new(#identifier, #kind)
#bases
#prefix
#applied
#overrides
#auto_apply
#can_only
#restrictions
#instance_names
#fallbacks
#properties
#fields
})
}
fn metadata(library: &str, decl: &usd::MetadataDecl<'_>) -> Result<TokenStream, Error> {
let name = decl.name();
let type_name = decl.declared_type_name();
let targets = decl.declared_applies_to();
let applies_to = (targets != usd::MetadataTargets::all()).then(|| {
let flags = targets.iter_names().map(|(flag, _)| {
let flag = ident(flag);
quote! { ::openusd::usd::MetadataTargets::#flag }
});
let targets = flags
.reduce(|all, flag| quote! { #all.union(#flag) })
.unwrap_or_else(|| quote! { ::openusd::usd::MetadataTargets::empty() });
quote! { .applies_to(#targets) }
});
let fallback = match decl.declared_fallback() {
Some(value) => {
let built = value::value_expr(&value).map_err(|kind| Error::UnwritableValue {
schema: library.to_owned(),
field: name.to_owned(),
kind: kind.into(),
})?;
Some(quote! { .fallback(|| #built) })
}
None => None,
};
Ok(quote! {
::openusd::usd::MetadataDecl::new(#name, #type_name) #applies_to #fallback
})
}
fn property(schema: &str, decl: &usd::PropertyDecl<'_>) -> Result<TokenStream, Error> {
let name = decl.name();
let declaration = if let Some(type_name) = decl.type_name() {
quote! { ::openusd::usd::PropertyDecl::attribute(#name, #type_name) }
} else {
quote! { ::openusd::usd::PropertyDecl::relationship(#name) }
};
let variability = if decl.is_uniform() {
quote! { .uniform() }
} else {
quote! { .varying() }
};
let custom = decl.is_custom().then(|| quote! { .custom() });
let fields = fields(schema, decl.declared_fields())?;
let fields = (!fields.is_empty()).then(|| quote! { .fields(&[#(#fields),*]) });
Ok(quote! { #declaration #variability #custom #fields })
}
fn fields(schema: &str, fields: &[usd::Field<'_>]) -> Result<Vec<TokenStream>, Error> {
fields.iter().map(|field| self::field(schema, field)).collect()
}
fn field(schema: &str, field: &usd::Field<'_>) -> Result<TokenStream, Error> {
let name = field.name();
let value = field.value();
Ok(match &value {
sdf::Value::String(text) => quote! { ::openusd::usd::Field::string(#name, #text) },
sdf::Value::StringVec(texts) => {
let texts = texts.iter().map(String::as_str);
quote! { ::openusd::usd::Field::strings(#name, &[#(#texts),*]) }
}
sdf::Value::Token(token) => {
let token = token.as_str();
quote! { ::openusd::usd::Field::token(#name, #token) }
}
sdf::Value::TokenVec(tokens) => {
let tokens = tokens.iter().map(tf::Token::as_str);
quote! { ::openusd::usd::Field::tokens(#name, &[#(#tokens),*]) }
}
other => {
let built = value::value_expr(other).map_err(|kind| Error::UnwritableValue {
schema: schema.to_owned(),
field: name.to_owned(),
kind: kind.into(),
})?;
quote! { ::openusd::usd::Field::new(#name, || #built) }
}
})
}