use std::io::Cursor;
use anyhow::Result;
use super::schema::{ChildrenKey, FieldKey};
use super::{
AbstractData, AttributeSpec, AttributeSpecMut, Data, LayerData, Path, PrimSpec, PrimSpecMut, PseudoRootSpec,
PseudoRootSpecMut, RelationshipSpec, RelationshipSpecMut, Spec, SpecError, SpecType, Specifier, Value, Variability,
};
use crate::{usda, usdc};
pub struct Layer {
pub identifier: String,
pub(crate) data: LayerData,
}
impl Layer {
pub(crate) fn new(identifier: impl Into<String>, data: LayerData) -> Self {
Self {
identifier: identifier.into(),
data,
}
}
pub fn data(&self) -> &dyn AbstractData {
self.data.as_ref()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LayerFormat {
Usda,
Usdc,
Usdz,
}
impl LayerFormat {
pub fn from_extension(ext: &str) -> Option<Self> {
match ext.to_ascii_lowercase().as_str() {
"usda" => Some(Self::Usda),
"usdc" | "usd" => Some(Self::Usdc),
"usdz" => Some(Self::Usdz),
_ => None,
}
}
}
impl Layer {
pub fn save(&self, path: impl AsRef<std::path::Path>) -> Result<()> {
let path = path.as_ref();
let ext = path.extension().and_then(|e| e.to_str()).unwrap_or_default();
let format = LayerFormat::from_extension(ext).ok_or_else(|| match ext {
"" => anyhow::anyhow!("layer path {} has no extension; cannot choose format", path.display()),
other => anyhow::anyhow!("unsupported layer extension {other:?} for save (expected usda/usdc/usd/usdz)"),
})?;
self.save_as(path, format)
}
pub fn save_as(&self, path: impl AsRef<std::path::Path>, format: LayerFormat) -> Result<()> {
let path = path.as_ref();
match format {
LayerFormat::Usda => usda::TextWriter::write_to_file(self.data.as_ref(), path),
LayerFormat::Usdc => usdc::CrateWriter::write_to_file(self.data.as_ref(), path),
LayerFormat::Usdz => {
let stem = path.file_stem().and_then(|s| s.to_str()).unwrap_or("layer");
let mut buf = Vec::new();
usdc::CrateWriter::write(self.data.as_ref(), &mut Cursor::new(&mut buf))?;
let mut archive = crate::usdz::ArchiveWriter::create(path)?;
archive.add_layer(&format!("{stem}.usdc"), &buf)?;
archive.finish()?;
Ok(())
}
}
}
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum AuthoringError {
#[error(transparent)]
Spec(#[from] SpecError),
#[error("layer {identifier} is read-only; authoring is not supported")]
ReadOnly {
identifier: String,
},
#[error("path {path} is not valid here: {reason}")]
InvalidPath {
path: Path,
reason: &'static str,
},
}
impl Layer {
pub fn new_anonymous(identifier: impl Into<String>) -> Self {
let mut data = Data::new();
data.create_spec(Path::abs_root(), SpecType::PseudoRoot);
Self::new(identifier, Box::new(data))
}
pub fn create_prim(
&mut self,
path: impl Into<Path>,
specifier: Specifier,
type_name: impl Into<String>,
) -> Result<PrimSpecMut<'_>, AuthoringError> {
let path: Path = path.into();
let type_name: String = type_name.into();
require_prim_path(&path)?;
let data = self.writable_data_mut()?;
ensure_prim_chain(data, &path)?;
let spec = data.spec_mut(&path).expect("just ensured");
spec.add(FieldKey::Specifier, Value::Specifier(specifier));
if !type_name.is_empty() {
spec.add(FieldKey::TypeName, Value::Token(type_name));
}
Ok(spec.as_prim_mut().expect("type guaranteed by ensure_prim_chain"))
}
pub fn override_prim(&mut self, path: impl Into<Path>) -> Result<PrimSpecMut<'_>, AuthoringError> {
let path: Path = path.into();
require_prim_path(&path)?;
let data = self.writable_data_mut()?;
ensure_prim_chain(data, &path)?;
let spec = data.spec_mut(&path).expect("just ensured");
Ok(spec.as_prim_mut().expect("type guaranteed by ensure_prim_chain"))
}
pub fn create_attribute(
&mut self,
path: impl Into<Path>,
type_name: impl Into<String>,
variability: Variability,
custom: bool,
) -> Result<AttributeSpecMut<'_>, AuthoringError> {
let path: Path = path.into();
let type_name: String = type_name.into();
let (prim_path, property_name) = split_property_path(&path)?;
let data = self.writable_data_mut()?;
require_spec_type_or_absent(data, &path, SpecType::Attribute)?;
validate_token_vec(data, &prim_path, ChildrenKey::PropertyChildren)?;
ensure_prim_chain(data, &prim_path)?;
add_to_token_vec(
data.spec_mut(&prim_path).expect("ensure_prim_chain created it"),
&prim_path,
ChildrenKey::PropertyChildren,
&property_name,
)?;
if !data.has_spec(&path) {
data.create_spec(path.clone(), SpecType::Attribute);
}
let spec = data.spec_mut(&path).expect("just ensured");
spec.add(FieldKey::TypeName, Value::Token(type_name));
if variability != Variability::Varying {
spec.add(FieldKey::Variability, Value::Variability(variability));
} else {
spec.remove(FieldKey::Variability.as_str());
}
if custom {
spec.add(FieldKey::Custom, Value::Bool(true));
} else {
spec.remove(FieldKey::Custom.as_str());
}
Ok(spec
.as_attr_mut()
.expect("type guaranteed by require_spec_type_or_absent"))
}
pub fn create_relationship(
&mut self,
path: impl Into<Path>,
variability: Variability,
custom: bool,
) -> Result<RelationshipSpecMut<'_>, AuthoringError> {
let path: Path = path.into();
let (prim_path, property_name) = split_property_path(&path)?;
let data = self.writable_data_mut()?;
require_spec_type_or_absent(data, &path, SpecType::Relationship)?;
validate_token_vec(data, &prim_path, ChildrenKey::PropertyChildren)?;
ensure_prim_chain(data, &prim_path)?;
add_to_token_vec(
data.spec_mut(&prim_path).expect("ensure_prim_chain created it"),
&prim_path,
ChildrenKey::PropertyChildren,
&property_name,
)?;
if !data.has_spec(&path) {
data.create_spec(path.clone(), SpecType::Relationship);
}
let spec = data.spec_mut(&path).expect("just ensured");
if variability != Variability::Varying {
spec.add(FieldKey::Variability, Value::Variability(variability));
} else {
spec.remove(FieldKey::Variability.as_str());
}
if custom {
spec.add(FieldKey::Custom, Value::Bool(true));
} else {
spec.remove(FieldKey::Custom.as_str());
}
Ok(spec
.as_relationship_mut()
.expect("type guaranteed by require_spec_type_or_absent"))
}
pub fn prim(&self, path: impl Into<Path>) -> Option<PrimSpec<'_>> {
let path: Path = path.into();
self.data.as_data()?.spec(&path)?.as_prim()
}
pub fn prim_mut(&mut self, path: impl Into<Path>) -> Option<PrimSpecMut<'_>> {
let path: Path = path.into();
self.data.as_data_mut()?.spec_mut(&path)?.as_prim_mut()
}
pub fn attribute(&self, path: impl Into<Path>) -> Option<AttributeSpec<'_>> {
let path: Path = path.into();
self.data.as_data()?.spec(&path)?.as_attr()
}
pub fn attribute_mut(&mut self, path: impl Into<Path>) -> Option<AttributeSpecMut<'_>> {
let path: Path = path.into();
self.data.as_data_mut()?.spec_mut(&path)?.as_attr_mut()
}
pub fn relationship(&self, path: impl Into<Path>) -> Option<RelationshipSpec<'_>> {
let path: Path = path.into();
self.data.as_data()?.spec(&path)?.as_relationship()
}
pub fn relationship_mut(&mut self, path: impl Into<Path>) -> Option<RelationshipSpecMut<'_>> {
let path: Path = path.into();
self.data.as_data_mut()?.spec_mut(&path)?.as_relationship_mut()
}
pub fn pseudo_root(&self) -> Option<PseudoRootSpec<'_>> {
self.data.as_data()?.spec(&Path::abs_root())?.as_pseudo_root()
}
pub fn set_default_prim(&mut self, name: impl Into<String>) -> Result<(), AuthoringError> {
let name = name.into();
if name.is_empty() || name.starts_with('/') || Path::new(&format!("/{name}")).is_err() {
return Err(AuthoringError::InvalidPath {
path: Path::abs_root(),
reason: "defaultPrim must be a relative prim identifier or nested prim path",
});
}
self.pseudo_root_mut()?.set_default_prim(name);
Ok(())
}
pub fn missing_prim_ancestors(&self, target: &Path) -> Vec<Path> {
match target.parent() {
Some(p) => self.missing_prim_chain_inclusive(&p),
None => Vec::new(),
}
}
pub fn missing_prim_chain_inclusive(&self, target: &Path) -> Vec<Path> {
std::iter::successors(Some(target.clone()), Path::parent)
.take_while(|p| !p.is_abs_root())
.filter(|p| !self.data.has_spec(p))
.collect()
}
pub fn clear_default_prim(&mut self) -> Result<(), AuthoringError> {
let data = self.writable_data_mut()?;
if let Some(spec) = data.spec_mut(&Path::abs_root()) {
spec.remove(FieldKey::DefaultPrim.as_str());
}
Ok(())
}
pub fn pseudo_root_mut(&mut self) -> Result<PseudoRootSpecMut<'_>, AuthoringError> {
let data = self.writable_data_mut()?;
let root = Path::abs_root();
match data.spec_type(&root) {
Some(SpecType::PseudoRoot) => {}
Some(_) => {
return Err(AuthoringError::InvalidPath {
path: root,
reason: "root spec exists with non-PseudoRoot SpecType",
})
}
None => {
data.create_spec(root.clone(), SpecType::PseudoRoot);
}
}
Ok(data
.spec_mut(&root)
.expect("just ensured")
.as_pseudo_root_mut()
.expect("type guaranteed above"))
}
pub(crate) fn writable_data_mut(&mut self) -> Result<&mut Data, AuthoringError> {
let identifier = self.identifier.clone();
self.data.as_data_mut().ok_or(AuthoringError::ReadOnly { identifier })
}
}
impl std::fmt::Debug for Layer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Layer")
.field("identifier", &self.identifier)
.finish_non_exhaustive()
}
}
impl AbstractData for Layer {
fn has_spec(&self, path: &Path) -> bool {
self.data.has_spec(path)
}
fn has_field(&self, path: &Path, field: &str) -> bool {
self.data.has_field(path, field)
}
fn spec_type(&self, path: &Path) -> Option<SpecType> {
self.data.spec_type(path)
}
fn try_get(&self, path: &Path, field: &str) -> Result<Option<std::borrow::Cow<'_, Value>>> {
self.data.try_get(path, field)
}
fn list(&self, path: &Path) -> Option<Vec<String>> {
self.data.list(path)
}
fn paths(&self) -> Vec<Path> {
self.data.paths()
}
fn as_data(&self) -> Option<&Data> {
self.data.as_data()
}
fn as_data_mut(&mut self) -> Option<&mut Data> {
None
}
}
fn ensure_prim_chain(data: &mut Data, target: &Path) -> Result<(), AuthoringError> {
let mut chain: Vec<Path> = Vec::new();
let mut cursor: Path = target.clone();
while !cursor.is_abs_root() {
chain.push(cursor.clone());
cursor = cursor.parent().expect("non-root has a parent (validated upstream)");
}
chain.reverse();
for (i, child) in chain.iter().enumerate() {
let parent_path = if i == 0 { Path::abs_root() } else { chain[i - 1].clone() };
let parent_ty = if parent_path.is_abs_root() {
SpecType::PseudoRoot
} else {
SpecType::Prim
};
if let Some(existing) = data.spec_type(&parent_path) {
if existing != parent_ty {
return Err(AuthoringError::InvalidPath {
path: parent_path,
reason: "ancestor spec exists with non-prim SpecType",
});
}
}
validate_token_vec(data, &parent_path, ChildrenKey::PrimChildren)?;
if let Some(existing) = data.spec_type(child) {
if existing != SpecType::Prim {
return Err(AuthoringError::InvalidPath {
path: child.clone(),
reason: "spec exists with non-prim SpecType",
});
}
}
}
for (i, child) in chain.iter().enumerate() {
let parent_path = if i == 0 { Path::abs_root() } else { chain[i - 1].clone() };
let child_name = child.name().expect("non-root has a name").to_owned();
let parent_ty = if parent_path.is_abs_root() {
SpecType::PseudoRoot
} else {
SpecType::Prim
};
if data.spec_type(&parent_path).is_none() {
data.create_spec(parent_path.clone(), parent_ty);
}
let parent_spec = data.spec_mut(&parent_path).expect("just ensured");
add_to_token_vec(parent_spec, &parent_path, ChildrenKey::PrimChildren, &child_name)?;
if data.spec_type(child).is_none() {
let spec = data.create_spec(child.clone(), SpecType::Prim);
spec.add(FieldKey::Specifier, Value::Specifier(Specifier::Over));
}
}
Ok(())
}
fn add_to_token_vec(spec: &mut Spec, owner_path: &Path, key: ChildrenKey, name: &str) -> Result<(), AuthoringError> {
match spec.get_mut(key.as_str()) {
Some(Value::TokenVec(v)) => {
if !v.iter().any(|n| n == name) {
v.push(name.to_owned());
}
}
Some(_) => {
return Err(AuthoringError::InvalidPath {
path: owner_path.clone(),
reason: "child-list field exists with non-TokenVec value",
});
}
None => {
spec.add(key, Value::TokenVec(vec![name.to_owned()]));
}
}
Ok(())
}
fn validate_token_vec(data: &Data, path: &Path, key: ChildrenKey) -> Result<(), AuthoringError> {
match data.spec(path).and_then(|spec| spec.get(key.as_str())) {
Some(Value::TokenVec(_)) | None => Ok(()),
Some(_) => Err(AuthoringError::InvalidPath {
path: path.clone(),
reason: "child-list field exists with non-TokenVec value",
}),
}
}
fn require_spec_type_or_absent(data: &Data, path: &Path, expected: SpecType) -> Result<(), AuthoringError> {
match data.spec_type(path) {
Some(existing) if existing != expected => Err(AuthoringError::InvalidPath {
path: path.clone(),
reason: "spec exists with the wrong SpecType",
}),
_ => Ok(()),
}
}
fn require_prim_path(path: &Path) -> Result<(), AuthoringError> {
if !path.is_abs() || path.is_abs_root() {
return Err(AuthoringError::InvalidPath {
path: path.clone(),
reason: "expected absolute non-root prim path",
});
}
if path.is_property_path() {
return Err(AuthoringError::InvalidPath {
path: path.clone(),
reason: "expected prim path, got property path",
});
}
for segment in path.as_str()[1..].split('/') {
if !Path::is_valid_identifier(segment) {
return Err(AuthoringError::InvalidPath {
path: path.clone(),
reason: "prim path component is not a USD identifier",
});
}
}
Ok(())
}
fn split_property_path(path: &Path) -> Result<(Path, String), AuthoringError> {
if !path.is_property_path() {
return Err(AuthoringError::InvalidPath {
path: path.clone(),
reason: "expected property path",
});
}
let prim_path = path.prim_path();
require_prim_path(&prim_path)?;
let suffix = path
.as_str()
.strip_prefix(prim_path.as_str())
.and_then(|t| t.strip_prefix('.'))
.ok_or(AuthoringError::InvalidPath {
path: path.clone(),
reason: "malformed property path",
})?;
if suffix.is_empty() || !suffix.split(':').all(Path::is_valid_identifier) {
return Err(AuthoringError::InvalidPath {
path: path.clone(),
reason: "property name must be a colon-separated identifier",
});
}
Ok((prim_path, suffix.to_owned()))
}