thot_core/db/resources/
asset.rs

1//! Asset
2use super::object::{Object, StandardObject};
3use super::standard_properties::StandardProperties;
4use crate::project::asset::Asset as PrjAsset;
5use crate::result::{AssetError, Error, Result};
6use crate::types::{ResourceId, ResourcePath};
7use std::hash::Hash;
8use std::path::{Path, PathBuf};
9
10#[cfg(feature = "serde")]
11use serde::Deserialize;
12
13// @todo: Ensure that if `parent` is set it's id matches that of `parent_id`.
14/// Asset
15#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
16#[cfg_attr(feature = "serde", derive(Deserialize))]
17#[derive(Hash, Clone, PartialEq, Eq, Debug)]
18pub struct Asset {
19    pub properties: StandardProperties,
20    pub path: Option<ResourcePath>,
21
22    #[cfg_attr(feature = "serde", serde(skip))]
23    pub parent: Option<ResourceId>,
24}
25
26impl Asset {
27    /// Converts a project Asset into a database Asset.
28    pub fn from(asset: PrjAsset, container: ResourceId) -> Self {
29        Asset {
30            properties: asset.properties.into(),
31            path: asset.path,
32            parent: Some(container),
33        }
34    }
35
36    // @todo: Container could be thought of as a root bucket.
37    // If this is the case, should possibly return `std::path::Component::RootDir`
38    // or `std::path::component::CurDir` instead of `None`.
39    /// Returns the bucket of the `Asset`, or `None` if it is in the root.
40    ///
41    /// # Errors
42    /// + If the Asset's path is not set.
43    ///
44    /// # See also
45    /// + [`Self.of_root`]
46    /// + [`Self.in_bucket`]
47    /// + [`Self.of_bucket`]
48    pub fn bucket(&self) -> Result<Option<PathBuf>> {
49        if self.path.is_none() {
50            return Err(Error::AssetError(AssetError::PathNotSet));
51        }
52
53        let path = self.path.clone().unwrap();
54        let parent = path.as_path().parent().expect("invalid path");
55        let parent_str = parent.as_os_str();
56        match parent_str.is_empty() {
57            true => Ok(None),
58            false => Ok(Some(parent.to_path_buf())),
59        }
60    }
61
62    /// Returns if the Asset is in the Container root.
63    ///
64    /// # Errors
65    /// + If the Asset's path is not set.
66    ///
67    /// # See also
68    /// + [`Self.bucket`]
69    /// + [`Self.in_bucket`]
70    /// + [`Self.of_bucket`]
71    pub fn of_root(&self) -> Result<bool> {
72        let b = self.bucket()?;
73        Ok(b.is_none())
74    }
75
76    /// Returns if the Asset is in the given bucket or one of its descendents.
77    ///
78    /// # Errors
79    /// + If the Asset's path is not set.
80    ///
81    /// # See also
82    /// + [`Self.bucket`]
83    /// + [`Self.of_root`]
84    /// + [`Self.of_bucket`]
85    pub fn in_bucket(&self, bucket: &Path) -> Result<bool> {
86        let my_bucket = self.bucket()?;
87        if my_bucket.is_none() {
88            return Ok(false);
89        }
90
91        let my_bucket = my_bucket.unwrap();
92        Ok(my_bucket.starts_with(bucket))
93    }
94
95    /// Returns if the Asset is in the given bucket directly.
96    ///
97    /// # Errors
98    /// + If the Asset's path is not set.
99    ///
100    /// # See also
101    /// + [`Self.bucket`]
102    /// + [`Self.of_root`]
103    /// + [`Self.in_bucket`]
104    pub fn of_bucket(&self, bucket: &Path) -> Result<bool> {
105        let my_bucket = self.bucket()?;
106        if my_bucket.is_none() {
107            return Ok(false);
108        }
109
110        let my_bucket = my_bucket.unwrap();
111        Ok(my_bucket == bucket)
112    }
113}
114
115impl Object for Asset {}
116
117impl StandardObject for Asset {
118    fn properties(&self) -> &StandardProperties {
119        &self.properties
120    }
121
122    fn properties_mut(&mut self) -> &mut StandardProperties {
123        &mut self.properties
124    }
125}
126
127#[cfg(test)]
128#[path = "./asset_test.rs"]
129mod asset_test;