1use crate::owner::{AssetOwner, DropMessage};
6use fixstr::FixStr;
7use message_channel::Sender;
8use owo_colors::OwoColorize;
9use std::any::{TypeId, type_name};
10use std::cmp::Ordering;
11use std::fmt;
12use std::fmt::{Debug, Display, Formatter};
13use std::marker::PhantomData;
14use std::path::PathBuf;
15use std::sync::Arc;
16pub mod owner;
17
18const FIXED_CAPACITY_SIZE: usize = 32;
19
20pub trait Asset: 'static + Debug + Send + Sync {}
21#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Copy)]
22pub struct RawAssetId {
23 pub generation: u8,
24 pub index: u16,
25}
26
27impl RawAssetId {
28 #[must_use]
29 pub const fn new(generation: u8, index: u16) -> Self {
30 Self { generation, index }
31 }
32}
33
34impl From<RawWeakId> for RawAssetId {
35 fn from(value: RawWeakId) -> Self {
36 value.raw_id
37 }
38}
39
40impl Display for RawAssetId {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 write!(
43 f,
44 "{}-{}",
45 self.index.to_string().bright_green(),
46 self.generation.to_string().green()
47 )
48 }
49}
50
51fn short_type_name<T>() -> &'static str {
52 type_name::<T>().split("::").last().unwrap()
53}
54
55#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Copy)]
56pub struct RawWeakId {
57 raw_id: RawAssetId,
58 type_id: TypeId,
59 debug_asset_name: AssetName,
60 debug_type_id: FixStr<32>,
61}
62
63impl<A: Asset> From<&Id<A>> for RawWeakId {
64 fn from(id: &Id<A>) -> Self {
65 Self {
66 raw_id: id.owner.raw_id().raw_id,
67 type_id: TypeId::of::<A>(),
68 debug_type_id: FixStr::new_unchecked(short_type_name::<A>()),
69 debug_asset_name: id.owner.asset_name().unwrap(),
70 }
71 }
72}
73
74impl RawWeakId {
75 #[must_use]
76 pub fn with_asset_type<A: Asset>(id: RawAssetId, asset_name: AssetName) -> Self {
77 Self {
78 raw_id: id,
79 type_id: TypeId::of::<A>(),
80 debug_asset_name: asset_name,
81 debug_type_id: FixStr::new_unchecked(short_type_name::<A>()),
82 }
83 }
84
85 #[must_use]
86 pub const fn type_id(&self) -> TypeId {
87 self.type_id
88 }
89}
90
91impl Display for RawWeakId {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
93 write!(
94 f,
95 "({} {} {})",
96 self.raw_id,
97 self.debug_asset_name,
98 self.debug_type_id.as_str().yellow()
99 )
100 }
101}
102
103#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
105pub struct WeakId<A: Asset> {
106 raw_id: RawWeakId,
107 phantom_data: PhantomData<A>,
108}
109
110impl<A: Asset> Copy for WeakId<A> {} impl<A: Asset> Clone for WeakId<A> {
115 fn clone(&self) -> Self {
116 *self
117 }
118}
119
120impl<A: Asset> WeakId<A> {
121 #[must_use]
122 pub const fn new(raw_id: RawWeakId) -> Self {
123 Self {
124 raw_id,
125 phantom_data: PhantomData,
126 }
127 }
128
129 #[must_use]
130 pub const fn raw_id(&self) -> RawWeakId {
131 self.raw_id
132 }
133}
134
135impl<A: Asset> Display for WeakId<A> {
136 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137 write!(f, "weak {}", self.raw_id)
138 }
139}
140
141impl<A: Asset> From<WeakId<A>> for RawWeakId {
142 fn from(id: WeakId<A>) -> Self {
143 id.raw_id
144 }
145}
146
147pub struct Id<A: Asset> {
149 owner: Arc<AssetOwner>,
150 _phantom_data: PhantomData<A>,
151}
152
153impl<A: Asset> Debug for Id<A> {
154 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
155 write!(f, "{:?}", self.owner)
156 }
157}
158
159impl<A: Asset> Display for Id<A> {
160 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161 write!(f, "{}", self.owner)
162 }
163}
164
165impl<A: Asset> PartialEq<Self> for Id<A> {
166 fn eq(&self, other: &Self) -> bool {
167 self.owner.eq(&other.owner)
168 }
169}
170
171impl<A: Asset> Eq for Id<A> {}
172
173impl<A: Asset> PartialOrd<Self> for Id<A> {
174 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
175 Some(self.cmp(other))
176 }
177}
178
179impl<A: Asset> Ord for Id<A> {
180 fn cmp(&self, other: &Self) -> Ordering {
181 self.owner.cmp(&other.owner)
182 }
183}
184
185impl<A: Asset> Clone for Id<A> {
186 fn clone(&self) -> Self {
187 Self {
188 owner: self.owner.clone(),
189 _phantom_data: PhantomData,
190 }
191 }
192}
193
194impl<A: Asset> From<&Id<A>> for WeakId<A> {
195 fn from(id: &Id<A>) -> Self {
196 Self::new(id.owner.raw_id())
197 }
198}
199
200impl<A: Asset> Id<A> {
201 #[must_use]
202 pub fn new(raw_id: RawAssetId, sender: Sender<DropMessage>, asset_name: AssetName) -> Self {
203 let raw_id_type = RawWeakId::with_asset_type::<A>(raw_id, asset_name);
204 Self {
205 owner: Arc::new(AssetOwner::new(raw_id_type, Some(asset_name), sender)),
206 _phantom_data: PhantomData,
207 }
208 }
209
210 #[must_use]
211 pub fn asset_name(&self) -> Option<AssetName> {
212 self.owner.asset_name()
213 }
214}
215
216#[must_use]
238pub fn is_valid_asset_name(s: &str) -> bool {
239 if s.len() > FIXED_CAPACITY_SIZE {
240 return false;
241 }
242 let mut chars = s.chars();
243
244 matches!(chars.next(), Some(_c @ 'a'..='z'))
245 && !s.ends_with(['/', '-', '_', '.'])
246 && !s.contains("//")
247 && !s.contains("__")
248 && !s.contains("--")
249 && !s.contains("..")
250 && chars.all(|c| {
251 c.is_ascii_lowercase() || c.is_ascii_digit() || matches!(c, '_' | '-' | '/' | '.')
252 })
253}
254
255#[derive(Debug, Copy, Clone, Eq, Ord, PartialOrd, PartialEq, Hash)]
256pub struct AssetName {
257 value: FixStr<FIXED_CAPACITY_SIZE>,
258}
259
260impl AssetName {
261 #[must_use]
262 pub fn with_extension(&self, extension: &str) -> impl Into<Self> + use<> {
263 let added = format!("{}.{}", self.value.as_str(), extension);
264 Self {
265 value: FixStr::new_unchecked(added.as_str()),
266 }
267 }
268}
269
270impl AssetName {
272 #[must_use]
275 pub fn new(value: &str) -> Self {
276 assert!(is_valid_asset_name(value), "invalid asset name: {value}");
277 Self {
278 value: FixStr::new_unchecked(value),
279 }
280 }
281
282 #[must_use]
283 pub fn value(&self) -> &str {
284 self.value.as_str()
285 }
286}
287
288impl Display for AssetName {
289 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
290 let v = format!("{}", self.value);
291 write!(f, "{}", v.cyan())
292 }
293}
294
295impl From<&str> for AssetName {
296 fn from(value: &str) -> Self {
297 Self::new(value)
298 }
299}
300
301impl From<AssetName> for PathBuf {
302 fn from(value: AssetName) -> Self {
303 value.value().into()
304 }
305}
306
307impl<A: Asset> From<&Id<A>> for RawAssetId {
308 fn from(value: &Id<A>) -> Self {
309 value.owner.raw_id().raw_id
310 }
311}