distill_core/
lib.rs

1use std::fmt;
2#[cfg(feature = "serde-1")]
3use std::str::FromStr;
4
5#[cfg(feature = "serde-1")]
6use serde::{
7    de::{self, Visitor},
8    Deserialize, Deserializer, Serialize, Serializer,
9};
10pub use uuid;
11use uuid::Uuid;
12
13pub mod importer_context;
14pub mod utils;
15
16/// A universally unique identifier for an asset.
17/// An asset can be a value of any Rust type that implements
18/// [`TypeUuidDynamic`] + [serde::Serialize] + [Send].
19///
20/// If using a human-readable format, serializes to a hyphenated UUID format and deserializes from
21/// any format supported by the `uuid` crate. Otherwise, serializes to and from a `[u8; 16]`.
22#[derive(PartialEq, Eq, Clone, Copy, Default, Hash, Ord, PartialOrd)]
23pub struct AssetUuid(pub [u8; 16]);
24
25impl<S: AsRef<str>> From<S> for AssetUuid {
26    fn from(s: S) -> Self {
27        AssetUuid(
28            *Uuid::parse_str(s.as_ref())
29                .expect("Macro input is not a UUID string")
30                .as_bytes(),
31        )
32    }
33}
34
35impl AsMut<[u8]> for AssetUuid {
36    fn as_mut(&mut self) -> &mut [u8] {
37        &mut self.0
38    }
39}
40
41impl AsRef<[u8]> for AssetUuid {
42    fn as_ref(&self) -> &[u8] {
43        &self.0
44    }
45}
46
47impl fmt::Debug for AssetUuid {
48    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
49        f.debug_tuple("AssetUuid")
50            .field(&uuid::Uuid::from_bytes(self.0))
51            .finish()
52    }
53}
54
55impl fmt::Display for AssetUuid {
56    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
57        uuid::Uuid::from_bytes(self.0).fmt(f)
58    }
59}
60
61#[cfg(feature = "serde-1")]
62impl Serialize for AssetUuid {
63    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
64        if serializer.is_human_readable() {
65            serializer.serialize_str(&self.to_string())
66        } else {
67            self.0.serialize(serializer)
68        }
69    }
70}
71
72#[cfg(feature = "serde-1")]
73struct AssetUuidVisitor;
74
75#[cfg(feature = "serde-1")]
76impl<'a> Visitor<'a> for AssetUuidVisitor {
77    type Value = AssetUuid;
78
79    fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
80        write!(fmt, "a UUID-formatted string")
81    }
82
83    fn visit_str<E: de::Error>(self, s: &str) -> Result<Self::Value, E> {
84        uuid::Uuid::from_str(s)
85            .map(|id| AssetUuid(*id.as_bytes()))
86            .map_err(|_| de::Error::invalid_value(de::Unexpected::Str(s), &self))
87    }
88}
89
90#[cfg(feature = "serde-1")]
91impl<'de> Deserialize<'de> for AssetUuid {
92    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
93        if deserializer.is_human_readable() {
94            deserializer.deserialize_string(AssetUuidVisitor)
95        } else {
96            Ok(AssetUuid(<[u8; 16]>::deserialize(deserializer)?))
97        }
98    }
99}
100
101/// UUID of an asset's Rust type. Produced by [`TypeUuidDynamic::uuid`].
102///
103/// If using a human-readable format, serializes to a hyphenated UUID format and deserializes from
104/// any format supported by the `uuid` crate. Otherwise, serializes to and from a `[u8; 16]`.
105#[derive(PartialEq, Eq, Debug, Clone, Copy, Default, Hash)]
106pub struct AssetTypeId(pub [u8; 16]);
107
108impl AsMut<[u8]> for AssetTypeId {
109    fn as_mut(&mut self) -> &mut [u8] {
110        &mut self.0
111    }
112}
113
114impl AsRef<[u8]> for AssetTypeId {
115    fn as_ref(&self) -> &[u8] {
116        &self.0
117    }
118}
119
120impl fmt::Display for AssetTypeId {
121    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
122        uuid::Uuid::from_bytes(self.0).fmt(f)
123    }
124}
125
126#[cfg(feature = "serde-1")]
127impl Serialize for AssetTypeId {
128    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
129        if serializer.is_human_readable() {
130            serializer.serialize_str(&self.to_string())
131        } else {
132            self.0.serialize(serializer)
133        }
134    }
135}
136
137#[cfg(feature = "serde-1")]
138struct AssetTypeIdVisitor;
139
140#[cfg(feature = "serde-1")]
141impl<'a> Visitor<'a> for AssetTypeIdVisitor {
142    type Value = AssetTypeId;
143
144    fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
145        write!(fmt, "a UUID-formatted string")
146    }
147
148    fn visit_str<E: de::Error>(self, s: &str) -> Result<Self::Value, E> {
149        uuid::Uuid::parse_str(s)
150            .map(|id| AssetTypeId(*id.as_bytes()))
151            .map_err(|_| de::Error::invalid_value(de::Unexpected::Str(s), &self))
152    }
153}
154
155#[cfg(feature = "serde-1")]
156impl<'de> Deserialize<'de> for AssetTypeId {
157    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
158        if deserializer.is_human_readable() {
159            deserializer.deserialize_string(AssetTypeIdVisitor)
160        } else {
161            Ok(AssetTypeId(<[u8; 16]>::deserialize(deserializer)?))
162        }
163    }
164}
165
166/// A potentially unresolved reference to an asset
167#[derive(Debug, Hash, PartialEq, Eq, Clone, Ord, PartialOrd)]
168#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
169pub enum AssetRef {
170    Uuid(AssetUuid),
171    Path(std::path::PathBuf),
172}
173impl AssetRef {
174    pub fn expect_uuid(&self) -> &AssetUuid {
175        if let AssetRef::Uuid(uuid) = self {
176            uuid
177        } else {
178            panic!("Expected AssetRef::Uuid, got {:?}", self)
179        }
180    }
181
182    pub fn is_path(&self) -> bool {
183        matches!(self, AssetRef::Path(_))
184    }
185
186    pub fn is_uuid(&self) -> bool {
187        matches!(self, AssetRef::Uuid(_))
188    }
189}
190
191#[derive(Debug, Hash, PartialEq, Copy, Clone)]
192#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
193pub enum CompressionType {
194    None,
195    Lz4,
196}
197
198impl Default for CompressionType {
199    fn default() -> Self {
200        Self::None
201    }
202}
203
204/// Serializable metadata for an asset.
205/// Stored in .meta files and metadata DB.
206#[derive(Debug, Clone, Hash, Default)]
207#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
208pub struct AssetMetadata {
209    /// UUID for the asset to uniquely identify it
210    pub id: AssetUuid,
211    /// Search tags are used by asset tooling to search for the imported asset
212    pub search_tags: Vec<(String, Option<String>)>,
213    /// The referenced build pipeline is invoked when a build artifact is requested for the imported asset
214    pub build_pipeline: Option<AssetUuid>,
215    /// The latest artifact produced when importing this asset
216    pub artifact: Option<ArtifactMetadata>,
217}
218
219/// 64-bit hash of the inputs that would produce a given asset artifact
220#[derive(Debug, Copy, Clone, Hash, Default)]
221#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
222#[cfg_attr(feature = "serde", serde(transparent))]
223pub struct ArtifactId(pub u64);
224
225/// Serializable metadata for an artifact.
226/// Stored in .meta files and metadata DB.
227#[derive(Debug, Clone, Hash, Default)]
228#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
229pub struct ArtifactMetadata {
230    /// Hash that identifies this artifact
231    pub id: ArtifactId,
232    /// UUID for this artifact's asset
233    pub asset_id: AssetUuid,
234    /// Build dependencies will be included in the Builder arguments when building an asset
235    pub build_deps: Vec<AssetRef>,
236    /// Load dependencies are guaranteed to load before this asset by the Loader
237    pub load_deps: Vec<AssetRef>,
238    /// Type of compression used to compress this artifact
239    pub compression: CompressionType,
240    /// Size of this artifact in bytes when compressed
241    pub compressed_size: Option<u64>,
242    /// Size of this artifact in bytes when serialized and uncompressed
243    pub uncompressed_size: Option<u64>,
244    /// The UUID of the artifact's Rust type
245    pub type_id: AssetTypeId,
246}
247
248/// Provides a unique 16-byte ID for a value's type.
249pub trait TypeUuidDynamic {
250    fn uuid(&self) -> [u8; 16];
251}
252
253#[cfg(feature = "type_uuid")]
254impl<T: type_uuid::TypeUuidDynamic> TypeUuidDynamic for T {
255    fn uuid(&self) -> [u8; 16] {
256        <Self as type_uuid::TypeUuidDynamic>::uuid(self)
257    }
258}
259
260#[cfg(feature = "type_uuid")]
261pub use type_uuid;