Skip to main content

keket_graph/
node.rs

1use crate::protocol::AssetTree;
2use keket::{
3    database::{
4        AssetDatabase,
5        handle::AssetHandle,
6        path::AssetPathStatic,
7        reference::{AssetRef, AssetResolved},
8    },
9    third_party::anput::component::Component,
10};
11use serde::{Deserialize, Serialize};
12use std::{
13    error::Error,
14    marker::PhantomData,
15    ops::{Deref, DerefMut},
16};
17
18/// AssetNode represents a node in the asset graph, which is a reference to an
19/// asset that can be resolved to a specific component in asset.
20#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
21#[serde(from = "AssetPathStatic", into = "AssetPathStatic")]
22pub struct AssetNode<T: AssetTree> {
23    inner: AssetRef,
24    #[serde(skip)]
25    _phantom: PhantomData<fn() -> T>,
26}
27
28impl<T: AssetTree> AssetNode<T> {
29    /// Creates a new AssetNode from a static asset path.
30    ///
31    /// # Arguments
32    /// - `path`: The path to the asset, which can be any type that can be
33    ///   converted into `AssetPathStatic`.
34    ///
35    /// # Returns
36    /// A new `AssetNode` instance that references the asset at the given path.
37    pub fn new(path: impl Into<AssetPathStatic>) -> Self {
38        Self::from_ref(AssetRef::new(path))
39    }
40
41    /// Creates a new AssetNode from an existing AssetRef.
42    ///
43    /// # Arguments
44    /// - `inner`: The `AssetRef` that this node will reference.
45    ///
46    /// # Returns
47    /// A new `AssetNode` instance that wraps the provided `AssetRef`.
48    pub fn from_ref(inner: AssetRef) -> Self {
49        Self {
50            inner,
51            _phantom: PhantomData,
52        }
53    }
54
55    /// Returns a reference to the inner `AssetRef` of this node.
56    pub fn as_ref(&self) -> AssetRef {
57        self.inner.clone()
58    }
59
60    /// Invalidates the asset node, making internal `AssetRef` unresolved.
61    ///
62    /// # Returns
63    /// A `Result` indicating success or failure. If successful, the asset node
64    /// is marked as invalidated, and any future attempts to resolve it will
65    /// require re-fetching or re-resolving the asset.
66    pub fn invalidate(&self) -> Result<(), Box<dyn Error>> {
67        self.inner.invalidate()
68    }
69
70    /// Returns the path of the asset node.
71    ///
72    /// # Returns
73    /// A reference to the `AssetPathStatic` associated with this node.
74    pub fn path(&self) -> &AssetPathStatic {
75        self.inner.path()
76    }
77
78    /// Returns the handle of the asset node.
79    ///
80    /// # Returns
81    /// A `Result` containing the `AssetHandle` if successful, or an error if
82    /// the handle cannot be retrieved.
83    pub fn handle(&self) -> Result<AssetHandle, Box<dyn Error>> {
84        self.inner.handle()
85    }
86
87    /// Resolves the asset node to a specific component in the asset database.
88    ///
89    /// # Arguments
90    /// - `database`: A reference to the `AssetDatabase` where the asset is stored.
91    ///
92    /// # Returns
93    /// A `Result` containing an `AssetNodeResolved` instance if successful, or an
94    /// error if the resolution fails. The `AssetNodeResolved` provides access to
95    /// the resolved component, allowing read and write operations.
96    pub fn resolve<'a>(
97        &'a self,
98        database: &'a AssetDatabase,
99    ) -> Result<AssetNodeResolved<'a, T>, Box<dyn Error>> {
100        self.inner
101            .resolve(database)
102            .map(|resolved| AssetNodeResolved {
103                inner: resolved,
104                _phantom: PhantomData,
105            })
106    }
107
108    /// Ensures that the asset node is resolved and available in the asset database.
109    ///
110    /// # Arguments
111    /// - `database`: A mutable reference to the `AssetDatabase` where the asset is stored.
112    ///
113    /// # Returns
114    /// A `Result` containing an `AssetNodeResolved` instance if successful, or an
115    /// error if the resolution fails. The `AssetNodeResolved` provides access to
116    /// the resolved component, allowing read and write operations.
117    pub fn ensure<'a>(
118        &'a self,
119        database: &'a mut AssetDatabase,
120    ) -> Result<AssetNodeResolved<'a, T>, Box<dyn Error>> {
121        self.inner
122            .ensure(database)
123            .map(|resolved| AssetNodeResolved {
124                inner: resolved,
125                _phantom: PhantomData,
126            })
127    }
128}
129
130impl<T: AssetTree> Clone for AssetNode<T> {
131    fn clone(&self) -> Self {
132        Self {
133            inner: self.inner.clone(),
134            _phantom: PhantomData,
135        }
136    }
137}
138
139impl<T: AssetTree> From<AssetPathStatic> for AssetNode<T> {
140    fn from(path: AssetPathStatic) -> Self {
141        Self::new(path)
142    }
143}
144
145impl<T: AssetTree> From<AssetNode<T>> for AssetPathStatic {
146    fn from(value: AssetNode<T>) -> Self {
147        value.path().clone()
148    }
149}
150
151impl<T: AssetTree> AssetTree for AssetNode<T> {
152    fn asset_dependencies(&self) -> impl IntoIterator<Item = AssetPathStatic> {
153        std::iter::once(self.inner.path().clone().into_static())
154    }
155}
156
157/// AssetNodeResolved represents a resolved asset node, providing access to the
158/// component associated with the asset. It allows both read and write access
159/// to the component, ensuring that the asset is properly resolved before
160/// accessing its data.
161pub struct AssetNodeResolved<'a, T: Component> {
162    inner: AssetResolved<'a>,
163    _phantom: PhantomData<fn() -> T>,
164}
165
166impl<'a, T: Component> AssetNodeResolved<'a, T> {
167    /// Gives read access to component of the asset.
168    ///
169    /// # Returns
170    /// An `Option` containing a reference to the component if it is accessible,
171    /// or `None` if the component is not accessible.
172    pub fn read(&self) -> Option<&T> {
173        self.inner.access_checked::<&T>()
174    }
175
176    /// Gives write access to component of the asset.
177    ///
178    /// # Returns
179    /// An `Option` containing a mutable reference to the component if it is
180    /// accessible, or `None` if the component is not accessible.
181    pub fn write(&self) -> Option<&mut T> {
182        self.inner.access_checked::<&mut T>()
183    }
184
185    /// Gives unchecked read access to component of the asset.
186    ///
187    /// # Returns
188    /// A reference to the component, allowing read access without checking
189    /// if the component is accessible. This method can panic if the component
190    /// is not accessible, so it should be used with caution.
191    pub fn read_unchecked(&self) -> &T {
192        self.inner.access::<&T>()
193    }
194
195    /// Gives unchecked write access to component of the asset.
196    ///
197    /// # Returns
198    /// A mutable reference to the component, allowing write access without
199    /// checking if the component is accessible. This method can panic if the
200    /// component is not accessible, so it should be used with caution.
201    pub fn write_unchecked(&self) -> &mut T {
202        self.inner.access::<&mut T>()
203    }
204}
205
206impl<'a, T: Component> Deref for AssetNodeResolved<'a, T> {
207    type Target = AssetResolved<'a>;
208
209    fn deref(&self) -> &Self::Target {
210        &self.inner
211    }
212}
213
214impl<'a, T: Component> DerefMut for AssetNodeResolved<'a, T> {
215    fn deref_mut(&mut self) -> &mut Self::Target {
216        &mut self.inner
217    }
218}