unity_asset/lib.rs
1//! Unity Asset Parser
2//!
3//! A comprehensive Rust library for parsing Unity asset files, supporting both YAML and binary formats.
4//!
5//! This crate provides high-performance, memory-safe parsing of Unity files
6//! while maintaining exact compatibility with Unity's formats.
7//!
8//! # Features
9//!
10//! - **YAML Processing**: Complete Unity YAML format support with multi-document parsing
11//! - **Binary Assets**: AssetBundle and SerializedFile parsing with compression support
12//! - **Async Support**: Optional async/await API for concurrent processing (enable with `async` feature)
13//! - **Type Safety**: Rust's type system prevents common parsing vulnerabilities
14//! - **Performance**: Zero-cost abstractions and memory-efficient parsing
15//!
16//! # Examples
17//!
18//! ## Basic YAML Processing
19//!
20//! ```rust,no_run
21//! use unity_asset::{YamlDocument, UnityDocument};
22//!
23//! // Load a Unity YAML file
24//! let doc = YamlDocument::load_yaml("ProjectSettings.asset", false)?;
25//!
26//! // Access and filter objects
27//! let settings = doc.get(Some("PlayerSettings"), None)?;
28//! println!("Product name: {:?}", settings.get("productName"));
29//!
30//! # Ok::<(), unity_asset::UnityAssetError>(())
31//! ```
32//!
33//! ## Binary Asset Processing
34//!
35//! ```rust,no_run
36//! use unity_asset::load_bundle_from_memory;
37//!
38//! // Load and parse AssetBundle
39//! let data = std::fs::read("game.bundle")?;
40//! let bundle = load_bundle_from_memory(data)?;
41//!
42//! // Process assets
43//! for asset in &bundle.assets {
44//! println!("Found asset with {} objects", asset.object_count());
45//! }
46//!
47//! # Ok::<(), Box<dyn std::error::Error>>(())
48//! ```
49//!
50//! ## Async Processing (requires `async` feature)
51//!
52//! ```rust,no_run
53//! # #[cfg(feature = "async")]
54//! # {
55//! use unity_asset::{YamlDocument, AsyncUnityDocument};
56//!
57//! #[tokio::main]
58//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
59//! // Load file asynchronously
60//! let doc = YamlDocument::load_yaml_async("ProjectSettings.asset", false).await?;
61//!
62//! // Same API as sync version
63//! let settings = doc.get(Some("PlayerSettings"), None)?;
64//! println!("Product name: {:?}", settings.get("productName"));
65//!
66//! Ok(())
67//! }
68//! # }
69//! ```
70
71// Re-export from core crate
72pub use unity_asset_core::{
73 DocumentFormat, Result, UnityAssetError, UnityClass, UnityClassRegistry, UnityDocument,
74 UnityValue, constants::*,
75};
76
77// Re-export from YAML crate
78pub use unity_asset_yaml::YamlDocument;
79
80// Re-export from binary crate
81pub use unity_asset_binary::{
82 AssetBundle, SerializedFile, load_bundle, load_bundle_from_memory, load_bundle_with_options,
83};
84
85// Re-export async traits when async feature is enabled
86#[cfg(feature = "async")]
87pub use unity_asset_core::document::AsyncUnityDocument;
88
89/// Environment for managing multiple Unity assets
90pub mod environment {
91 use crate::{Result, UnityClass, YamlDocument};
92 use std::collections::HashMap;
93 use std::path::{Path, PathBuf};
94 use unity_asset_core::{UnityAssetError, UnityDocument};
95
96 /// Unified environment for managing Unity assets
97 pub struct Environment {
98 /// Loaded YAML documents
99 yaml_documents: HashMap<PathBuf, YamlDocument>,
100 /// Base path for relative file resolution
101 #[allow(dead_code)]
102 base_path: PathBuf,
103 }
104
105 impl Environment {
106 /// Create a new environment
107 pub fn new() -> Self {
108 Self {
109 yaml_documents: HashMap::new(),
110 base_path: std::env::current_dir().unwrap_or_default(),
111 }
112 }
113
114 /// Load assets from a path (file or directory)
115 pub fn load<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
116 let path = path.as_ref();
117
118 if path.is_file() {
119 self.load_file(path)?;
120 } else if path.is_dir() {
121 self.load_directory(path)?;
122 }
123
124 Ok(())
125 }
126
127 /// Load a single file
128 pub fn load_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
129 let path = path.as_ref();
130
131 // Check file extension to determine type
132 if let Some(ext) = path.extension() {
133 match ext.to_str() {
134 Some("asset") | Some("prefab") | Some("unity") | Some("meta") => {
135 let doc = YamlDocument::load_yaml(path, false)?;
136 self.yaml_documents.insert(path.to_path_buf(), doc);
137 }
138 _ => {
139 // For now, skip unknown file types
140 // Future: Add binary asset support (.bundle, .assets, etc.)
141 }
142 }
143 }
144
145 Ok(())
146 }
147
148 /// Load all supported files from a directory
149 pub fn load_directory<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
150 let path = path.as_ref();
151
152 if !path.exists() {
153 return Err(UnityAssetError::format(format!(
154 "Directory does not exist: {:?}",
155 path
156 )));
157 }
158
159 if !path.is_dir() {
160 return Err(UnityAssetError::format(format!(
161 "Path is not a directory: {:?}",
162 path
163 )));
164 }
165
166 // Recursively traverse directory
167 self.traverse_directory(path)?;
168
169 Ok(())
170 }
171
172 /// Recursively traverse directory and load Unity files
173 fn traverse_directory(&mut self, dir: &Path) -> Result<()> {
174 let entries = std::fs::read_dir(dir).map_err(|e| {
175 UnityAssetError::format(format!("Failed to read directory {:?}: {}", dir, e))
176 })?;
177
178 for entry in entries {
179 let entry = entry.map_err(|e| {
180 UnityAssetError::format(format!("Failed to read directory entry: {}", e))
181 })?;
182 let path = entry.path();
183
184 if path.is_dir() {
185 // Skip common Unity directories that don't contain assets
186 if let Some(dir_name) = path.file_name().and_then(|n| n.to_str()) {
187 match dir_name {
188 "Library" | "Temp" | "Logs" | ".git" | ".vs" | "obj" | "bin" => {
189 continue; // Skip these directories
190 }
191 _ => {
192 // Recursively process subdirectory
193 self.traverse_directory(&path)?;
194 }
195 }
196 }
197 } else if path.is_file() {
198 // Try to load the file
199 if let Err(e) = self.load_file(&path) {
200 // Log error but continue processing other files
201 eprintln!("Warning: Failed to load {:?}: {}", path, e);
202 }
203 }
204 }
205
206 Ok(())
207 }
208
209 /// Get all Unity objects from all loaded documents
210 pub fn objects(&self) -> impl Iterator<Item = &UnityClass> {
211 self.yaml_documents.values().flat_map(|doc| doc.entries())
212 }
213
214 /// Filter objects by class name
215 pub fn filter_by_class(&self, class_name: &str) -> Vec<&UnityClass> {
216 self.objects()
217 .filter(|obj| obj.class_name == class_name)
218 .collect()
219 }
220
221 /// Get loaded YAML documents
222 pub fn yaml_documents(&self) -> &HashMap<PathBuf, YamlDocument> {
223 &self.yaml_documents
224 }
225 }
226
227 impl Default for Environment {
228 fn default() -> Self {
229 Self::new()
230 }
231 }
232}