BlueGit

golden-age paperdoll-renderer

Files

paperdoll-renderer / src / foundation.rs

foundation.rs

foundation.rs 8.5 KiB Download Raw
1//! Honest capability report for the foundation shell.
2//!
3//! Later features add import, animation, rendering and preview. Until those
4//! features land, this module must keep reporting them as not built.
5
6/// Product capability named by the design.
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum Capability {
9 ProjectShell,
10 ObjMtlTextures,
11 Fbx,
12 GltfGlb,
13 SkeletalAnimation,
14 AttachmentEditor,
15 PaperdollRender,
16 Preview2d,
17}
18
19/// Whether this build actually contains the capability.
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum Availability {
22 Present,
23 NotBuilt,
24}
25
26/// What the process should do with the command line it received.
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum CliRequest {
29 OpenWindow,
30 PrintStatus,
31 PrintHelp,
32}
33
34/// Rejected command line. A model path is rejected on purpose: accepting one
35/// would look like import support.
36#[derive(Debug, Clone, PartialEq, Eq)]
37pub enum CliError {
38 UnknownArgument(String),
39}
40
41const CAPABILITIES: [Capability; 8] = [
42 Capability::ProjectShell,
43 Capability::ObjMtlTextures,
44 Capability::Fbx,
45 Capability::GltfGlb,
46 Capability::SkeletalAnimation,
47 Capability::AttachmentEditor,
48 Capability::PaperdollRender,
49 Capability::Preview2d,
50];
51
52pub fn capabilities() -> &'static [Capability] {
53 &CAPABILITIES
54}
55
56pub fn availability(capability: Capability) -> Availability {
57 match capability {
58 Capability::ProjectShell => Availability::Present,
59 Capability::ObjMtlTextures
60 | Capability::Fbx
61 | Capability::GltfGlb
62 | Capability::SkeletalAnimation
63 | Capability::AttachmentEditor
64 | Capability::PaperdollRender
65 | Capability::Preview2d => Availability::NotBuilt,
66 }
67}
68
69pub fn machine_key(capability: Capability) -> &'static str {
70 match capability {
71 Capability::ProjectShell => "project_shell",
72 Capability::ObjMtlTextures => "obj_mtl_textures",
73 Capability::Fbx => "fbx",
74 Capability::GltfGlb => "gltf_glb",
75 Capability::SkeletalAnimation => "skeletal_animation",
76 Capability::AttachmentEditor => "attachment_editor",
77 Capability::PaperdollRender => "paperdoll_render",
78 Capability::Preview2d => "preview_2d",
79 }
80}
81
82pub fn machine_state(availability: Availability) -> &'static str {
83 match availability {
84 Availability::Present => "present",
85 Availability::NotBuilt => "not_built",
86 }
87}
88
89pub fn dutch_label(capability: Capability) -> &'static str {
90 match capability {
91 Capability::ProjectShell => "projectschil",
92 Capability::ObjMtlTextures => "OBJ + MTL + textures",
93 Capability::Fbx => "FBX",
94 Capability::GltfGlb => "glTF/GLB",
95 Capability::SkeletalAnimation => "skeletanimatie",
96 Capability::AttachmentEditor => "3D-attachmenteditor",
97 Capability::PaperdollRender => "paperdoll-render",
98 Capability::Preview2d => "2D-preview",
99 }
100}
101
102pub fn dutch_state(availability: Availability) -> &'static str {
103 match availability {
104 Availability::Present => "aanwezig",
105 Availability::NotBuilt => "niet gebouwd",
106 }
107}
108
109pub fn dutch_summary() -> &'static str {
110 "Deze build opent alleen dit statusvenster. OBJ+MTL+textures, FBX, glTF/GLB, skeletanimatie, de attachment-editor, paperdoll-render en de 2D-preview zijn niet gebouwd. Synthetische testassets, als ze in de repository staan, worden door dit programma niet geladen."
111}
112
113/// Stable status text shared by `--status` and the window.
114pub fn report() -> String {
115 let mut lines = Vec::with_capacity(2 + CAPABILITIES.len());
116 lines.push("name: Paperdoll Renderer".to_string());
117 lines.push("phase: foundation".to_string());
118 for capability in CAPABILITIES {
119 lines.push(format!(
120 "{}: {}",
121 machine_key(capability),
122 machine_state(availability(capability))
123 ));
124 }
125 lines.join("\n")
126}
127
128/// Shared libraries the X11 window loads. `ldd` does not list them: eframe
129/// opens them at runtime. A missing one panics inside that loader, so the
130/// shell checks first.
131pub const LINUX_WINDOW_LIBRARIES: &[&str] = &[
132 "libxkbcommon-x11.so.0",
133 "libxkbcommon.so.0",
134 "libxcb.so.1",
135 "libX11.so.6",
136 "libGL.so.1",
137];
138
139pub fn missing_library_message(missing: &[&str]) -> String {
140 format!(
141 "het statusvenster kan niet openen; deze bibliotheken ontbreken: {}. Import en render zijn niet gebouwd.",
142 missing.join(", ")
143 )
144}
145
146#[cfg(target_os = "linux")]
147pub fn missing_window_libraries() -> Vec<&'static str> {
148 LINUX_WINDOW_LIBRARIES
149 .iter()
150 .copied()
151 .filter(|name| {
152 // Safety: `name` is a nul-free soname literal. A missing library
153 // returns Err; no symbol from the library is called.
154 unsafe { libloading::Library::new(name).is_err() }
155 })
156 .collect()
157}
158
159pub fn help_text() -> &'static str {
160 "\
161Paperdoll Renderer — projectbasis
162
163Gebruik:
164 paperdoll-renderer open het statusvenster
165 paperdoll-renderer --status print de status en sluit
166 paperdoll-renderer --help toon deze tekst
167
168Een modelpad wordt niet geaccepteerd. Import en render zijn niet gebouwd."
169}
170
171pub fn parse_cli<'a, I>(args: I) -> Result<CliRequest, CliError>
172where
173 I: IntoIterator<Item = &'a str>,
174{
175 let mut request = CliRequest::OpenWindow;
176 for arg in args {
177 match arg {
178 "--help" | "-h" => request = CliRequest::PrintHelp,
179 "--status" => {
180 if request != CliRequest::PrintHelp {
181 request = CliRequest::PrintStatus;
182 }
183 }
184 other => {
185 return Err(CliError::UnknownArgument(other.to_string()));
186 }
187 }
188 }
189 Ok(request)
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195
196 #[test]
197 fn only_the_project_shell_is_present() {
198 assert_eq!(
199 availability(Capability::ProjectShell),
200 Availability::Present
201 );
202 for capability in capabilities() {
203 if *capability == Capability::ProjectShell {
204 continue;
205 }
206 assert_eq!(
207 availability(*capability),
208 Availability::NotBuilt,
209 "{capability:?} must stay not built in the foundation"
210 );
211 }
212 }
213
214 #[test]
215 fn status_report_names_every_format_as_not_built() {
216 let report = report();
217 assert!(report.starts_with("name: Paperdoll Renderer\nphase: foundation\n"));
218 for key in [
219 "obj_mtl_textures",
220 "fbx",
221 "gltf_glb",
222 "skeletal_animation",
223 "attachment_editor",
224 "paperdoll_render",
225 "preview_2d",
226 ] {
227 assert!(
228 report.contains(&format!("{key}: not_built")),
229 "missing {key} in {report}"
230 );
231 assert!(
232 !report.contains(&format!("{key}: present")),
233 "{key} must not be reported as present"
234 );
235 }
236 assert!(report.contains("project_shell: present"));
237 let summary = dutch_summary();
238 assert!(summary.contains("niet gebouwd"));
239 assert!(summary.contains("niet geladen"));
240 let lower = summary.to_lowercase();
241 assert!(!lower.contains("import werkt"));
242 assert!(!lower.contains("render werkt"));
243 }
244
245 #[test]
246 fn cli_opens_a_window_or_prints_status_and_rejects_model_paths() {
247 assert_eq!(parse_cli(std::iter::empty()), Ok(CliRequest::OpenWindow));
248 assert_eq!(parse_cli(["--status"]), Ok(CliRequest::PrintStatus));
249 assert_eq!(parse_cli(["--help"]), Ok(CliRequest::PrintHelp));
250 assert_eq!(parse_cli(["-h", "--status"]), Ok(CliRequest::PrintHelp));
251 assert_eq!(
252 parse_cli(["tests/fixtures/bodies/test-body-male.glb"]),
253 Err(CliError::UnknownArgument(
254 "tests/fixtures/bodies/test-body-male.glb".to_string()
255 ))
256 );
257 assert_eq!(
258 parse_cli(["--status", "--render"]),
259 Err(CliError::UnknownArgument("--render".to_string()))
260 );
261 assert!(help_text().contains("niet gebouwd"));
262 }
263
264 #[test]
265 fn missing_library_message_names_the_library_and_does_not_claim_import() {
266 let message = missing_library_message(&["libxkbcommon-x11.so.0"]);
267 assert!(message.contains("libxkbcommon-x11.so.0"));
268 assert!(message.contains("niet gebouwd"));
269 assert!(!message.to_lowercase().contains("import werkt"));
270 assert!(!LINUX_WINDOW_LIBRARIES.is_empty());
271 }
272}