Skip to main content

khora_core/asset/
uuid.rs

1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use bincode::{Decode, Encode};
16use serde::{Deserialize, Serialize};
17use uuid::Uuid;
18
19/// A constant, randomly generated namespace for our asset UUIDs.
20/// This ensures that UUIDs generated from the same path are always the same.
21const ASSET_NAMESPACE_UUID: Uuid = Uuid::from_u128(0x4a6a81e9_f0d1_4b8f_91a8_7e7a5e0b6b4a);
22
23/// A globally unique, persistent identifier for a logical asset.
24///
25/// This UUID represents the "idea" of an asset, completely decoupled from its
26/// physical file path. It is the primary key used by the Virtual File System (VFS)
27/// to track and retrieve asset metadata.
28///
29/// By using a stable UUID, assets can be moved, renamed, or have their source
30/// data modified without breaking references to them in scenes or other assets.
31#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
32pub struct AssetUUID(Uuid);
33
34impl Encode for AssetUUID {
35    fn encode<E: bincode::enc::Encoder>(
36        &self,
37        encoder: &mut E,
38    ) -> Result<(), bincode::error::EncodeError> {
39        let bytes = self.0.as_bytes();
40        Encode::encode(bytes, encoder)
41    }
42}
43
44impl<Context> Decode<Context> for AssetUUID {
45    fn decode<D: bincode::de::Decoder<Context = Context>>(
46        decoder: &mut D,
47    ) -> Result<Self, bincode::error::DecodeError> {
48        let bytes: [u8; 16] = Decode::decode(decoder)?;
49        Ok(Self(Uuid::from_bytes(bytes)))
50    }
51}
52
53impl<'de, Context> bincode::BorrowDecode<'de, Context> for AssetUUID {
54    fn borrow_decode<D: bincode::de::BorrowDecoder<'de, Context = Context>>(
55        decoder: &mut D,
56    ) -> Result<Self, bincode::error::DecodeError> {
57        let bytes: [u8; 16] = Decode::decode(decoder)?;
58        Ok(Self(Uuid::from_bytes(bytes)))
59    }
60}
61
62impl AssetUUID {
63    /// Creates a new, random (version 4) `AssetUUID`.
64    pub fn new() -> Self {
65        Self(Uuid::new_v4())
66    }
67
68    /// Creates a new, stable AssetUUID (version 5) from a given path.
69    ///
70    /// This is the preferred method for generating UUIDs for assets on disk,
71    /// as it guarantees that the UUID will be the same every time the asset
72    /// pipeline is run for the same file.
73    pub fn new_v5(path_str: &str) -> Self {
74        Self(Uuid::new_v5(&ASSET_NAMESPACE_UUID, path_str.as_bytes()))
75    }
76}
77
78impl Default for AssetUUID {
79    /// Creates a new, random (version 4) `AssetUUID`.
80    fn default() -> Self {
81        Self::new()
82    }
83}