concinnity_core/components/
file.rs1use alloc::string::String;
7
8use concinnity_asset::cook;
9
10use crate::components::FileKind;
11use crate::ecs::asset_id::AssetId;
12use crate::ecs::{Component, PayloadLocator};
13
14#[derive(Debug, serde::Serialize, serde::Deserialize)]
19pub struct File {
20 pub asset_id: AssetId,
22 pub path: String,
24 pub kind: Option<FileKind>,
26 pub locator: Option<PayloadLocator>,
28}
29
30impl File {
31 pub fn bake(args: cook::File) -> Self {
35 let kind = args
36 .kind
37 .clone()
38 .or_else(|| super::path_extension(&args.path).and_then(FileKind::from_ext));
39 Self {
40 asset_id: AssetId::default(),
41 path: args.path,
42 kind,
43 locator: None,
44 }
45 }
46}
47
48impl Component for File {
49 const NAME: &'static str = "File";
50
51 fn from_baked(bytes: &[u8]) -> Result<Self, crate::result::CnResult> {
52 Ok(crate::blob::decode_exact(bytes)?)
53 }
54
55 fn inject_locator(&mut self, locator: PayloadLocator) {
56 self.locator = Some(locator);
57 }
58
59 fn inject_name(&mut self, id: AssetId) {
60 self.asset_id = id;
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn from_ext_maps_every_known_extension() {
70 let cases = [
71 ("obj", FileKind::Obj),
72 ("png", FileKind::Png),
73 ("jpg", FileKind::Jpg),
74 ("jpeg", FileKind::Jpeg),
75 ("bmp", FileKind::Bmp),
76 ("tga", FileKind::Tga),
77 ("gif", FileKind::Gif),
78 ("ttf", FileKind::Ttf),
79 ("otf", FileKind::Otf),
80 ("txt", FileKind::Txt),
81 ("md", FileKind::Md),
82 ("mtl", FileKind::Mtl),
83 ];
84 for (ext, want) in cases {
85 assert_eq!(FileKind::from_ext(ext), Some(want.clone()));
86 assert_eq!(FileKind::from_ext(&ext.to_uppercase()), Some(want));
88 }
89 assert_eq!(FileKind::from_ext("zzz"), None);
90 }
91
92 #[test]
93 fn from_args_infers_kind_from_the_extension() {
94 let f = File::bake(cook::File {
96 path: "models/box.obj".into(),
97 kind: None,
98 });
99 assert_eq!(f.kind, Some(FileKind::Obj));
100 assert_eq!(f.path, "models/box.obj");
101 let g = File::bake(cook::File {
103 path: "data.obj".into(),
104 kind: Some(FileKind::Txt),
105 });
106 assert_eq!(g.kind, Some(FileKind::Txt));
107 let h = File::bake(cook::File {
109 path: "notes.zzz".into(),
110 kind: None,
111 });
112 assert_eq!(h.kind, None);
113 }
114
115 #[test]
116 fn is_mesh_is_true_only_for_obj() {
117 assert!(FileKind::Obj.is_mesh());
118 assert!(!FileKind::Png.is_mesh());
119 assert!(!FileKind::Ttf.is_mesh());
120 }
121
122 #[test]
123 fn file_args_and_kind_round_trip_through_json() {
124 let args = cook::File {
125 path: "x.png".into(),
126 kind: Some(FileKind::Png),
127 };
128 let value = serde_json::to_value(&args).unwrap();
129 let back: cook::File = serde_json::from_value(value).unwrap();
130 assert_eq!(back.path, "x.png");
131 assert_eq!(back.kind, Some(FileKind::Png));
132 assert_eq!(serde_json::to_string(&FileKind::Jpeg).unwrap(), "\"jpeg\"");
134 assert_eq!(File::bake(args).kind, Some(FileKind::Png));
136 }
137}