BlueGit

golden-age paperdoll-renderer

Files

paperdoll-renderer / src / foundation.rs

foundation.rs

228e617a History
foundation.rs 9.2 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 PreviewFixtures,
33}
34
35/// Rejected command line. A model path is rejected on purpose: accepting one
36/// would look like import support.
37#[derive(Debug, Clone, PartialEq, Eq)]
38pub enum CliError {
39 UnknownArgument(String),
40}
41
42const CAPABILITIES: [Capability; 8] = [
43 Capability::ProjectShell,
44 Capability::ObjMtlTextures,
45 Capability::Fbx,
46 Capability::GltfGlb,
47 Capability::SkeletalAnimation,
48 Capability::AttachmentEditor,
49 Capability::PaperdollRender,
50 Capability::Preview2d,
51];
52
53pub fn capabilities() -> &'static [Capability] {
54 &CAPABILITIES
55}
56
57pub fn availability(capability: Capability) -> Availability {
58 match capability {
59 Capability::ProjectShell
60 | Capability::ObjMtlTextures
61 | Capability::GltfGlb
62 | Capability::Fbx
63 | Capability::SkeletalAnimation => Availability::Present,
64 Capability::AttachmentEditor | Capability::PaperdollRender | Capability::Preview2d => {
65 Availability::NotBuilt
66 }
67 }
68}
69
70pub fn machine_key(capability: Capability) -> &'static str {
71 match capability {
72 Capability::ProjectShell => "project_shell",
73 Capability::ObjMtlTextures => "obj_mtl_textures",
74 Capability::Fbx => "fbx",
75 Capability::GltfGlb => "gltf_glb",
76 Capability::SkeletalAnimation => "skeletal_animation",
77 Capability::AttachmentEditor => "attachment_editor",
78 Capability::PaperdollRender => "paperdoll_render",
79 Capability::Preview2d => "preview_2d",
80 }
81}
82
83pub fn machine_state(availability: Availability) -> &'static str {
84 match availability {
85 Availability::Present => "present",
86 Availability::NotBuilt => "not_built",
87 }
88}
89
90pub fn dutch_label(capability: Capability) -> &'static str {
91 match capability {
92 Capability::ProjectShell => "projectschil",
93 Capability::ObjMtlTextures => "OBJ + MTL + textures",
94 Capability::Fbx => "FBX",
95 Capability::GltfGlb => "glTF/GLB",
96 Capability::SkeletalAnimation => "skeletanimatie",
97 Capability::AttachmentEditor => "3D-attachmenteditor",
98 Capability::PaperdollRender => "paperdoll-render",
99 Capability::Preview2d => "2D-preview",
100 }
101}
102
103pub fn dutch_state(availability: Availability) -> &'static str {
104 match availability {
105 Availability::Present => "aanwezig",
106 Availability::NotBuilt => "niet gebouwd",
107 }
108}
109
110pub fn dutch_summary() -> &'static str {
111 "Deze build leest glTF/GLB, FBX en OBJ+MTL, en toont een live geskinde 3D-preview van glTF/GLB met walk en attack."
112}
113
114/// Stable status text shared by `--status` and the window.
115pub fn report() -> String {
116 let mut lines = Vec::with_capacity(2 + CAPABILITIES.len());
117 lines.push("name: Paperdoll Renderer".to_string());
118 lines.push("phase: live-3d-preview".to_string());
119 for capability in CAPABILITIES {
120 lines.push(format!(
121 "{}: {}",
122 machine_key(capability),
123 machine_state(availability(capability))
124 ));
125 }
126 lines.join("\n")
127}
128
129/// Shared libraries the X11 window loads. `ldd` does not list them: eframe
130/// opens them at runtime. A missing one panics inside that loader, so the
131/// shell checks first.
132pub const LINUX_WINDOW_LIBRARIES: &[&str] = &[
133 "libxkbcommon-x11.so.0",
134 "libxkbcommon.so.0",
135 "libxcb.so.1",
136 "libX11.so.6",
137 "libGL.so.1",
138];
139
140pub fn missing_library_message(missing: &[&str]) -> String {
141 format!(
142 "het statusvenster kan niet openen; deze bibliotheken ontbreken: {}. Import en render zijn niet gebouwd.",
143 missing.join(", ")
144 )
145}
146
147#[cfg(target_os = "linux")]
148pub fn missing_window_libraries() -> Vec<&'static str> {
149 LINUX_WINDOW_LIBRARIES
150 .iter()
151 .copied()
152 .filter(|name| {
153 // Safety: `name` is a nul-free soname literal. A missing library
154 // returns Err; no symbol from the library is called.
155 unsafe { libloading::Library::new(name).is_err() }
156 })
157 .collect()
158}
159
160pub fn help_text() -> &'static str {
161 "\
162Paperdoll Renderer — modelimport
163
164Gebruik:
165 paperdoll-renderer open het projectvenster
166 paperdoll-renderer --status print de status en sluit
167 paperdoll-renderer --help toon deze tekst
168
169 paperdoll-renderer --preview-fixture open de 3D-preview met de testlichamen
170
171Een los modelpad op de opdrachtregel opent geen bestand. Gebruik --preview-fixture of een .ppd-project."
172}
173
174pub fn parse_cli<'a, I>(args: I) -> Result<CliRequest, CliError>
175where
176 I: IntoIterator<Item = &'a str>,
177{
178 let mut request = CliRequest::OpenWindow;
179 for arg in args {
180 match arg {
181 "--help" | "-h" => request = CliRequest::PrintHelp,
182 "--preview-fixture" => {
183 if request != CliRequest::PrintHelp {
184 request = CliRequest::PreviewFixtures;
185 }
186 }
187 "--status" => {
188 if request != CliRequest::PrintHelp {
189 request = CliRequest::PrintStatus;
190 }
191 }
192 other => {
193 return Err(CliError::UnknownArgument(other.to_string()));
194 }
195 }
196 }
197 Ok(request)
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203
204 #[test]
205 fn import_formats_are_present_and_render_is_not() {
206 assert_eq!(
207 availability(Capability::ProjectShell),
208 Availability::Present
209 );
210 assert_eq!(availability(Capability::GltfGlb), Availability::Present);
211 assert_eq!(
212 availability(Capability::ObjMtlTextures),
213 Availability::Present
214 );
215 assert_eq!(availability(Capability::Fbx), Availability::Present);
216 assert_eq!(
217 availability(Capability::SkeletalAnimation),
218 Availability::Present
219 );
220 for capability in [
221 Capability::AttachmentEditor,
222 Capability::PaperdollRender,
223 Capability::Preview2d,
224 ] {
225 assert_eq!(
226 availability(capability),
227 Availability::NotBuilt,
228 "{capability:?} must stay not built"
229 );
230 }
231 }
232
233 #[test]
234 fn status_report_matches_the_import_build() {
235 let report = report();
236 assert!(report.starts_with("name: Paperdoll Renderer\nphase: live-3d-preview\n"));
237 for key in [
238 "project_shell",
239 "obj_mtl_textures",
240 "gltf_glb",
241 "fbx",
242 "skeletal_animation",
243 ] {
244 assert!(
245 report.contains(&format!("{key}: present")),
246 "missing present {key} in {report}"
247 );
248 }
249 for key in ["attachment_editor", "paperdoll_render", "preview_2d"] {
250 assert!(
251 report.contains(&format!("{key}: not_built")),
252 "missing {key} in {report}"
253 );
254 assert!(
255 !report.contains(&format!("{key}: present")),
256 "{key} must not be reported as present"
257 );
258 }
259 let summary = dutch_summary();
260 assert!(summary.contains("3D-preview"));
261 assert!(summary.contains("glTF/GLB"));
262 assert!(summary.contains("walk"));
263 }
264
265 #[test]
266 fn cli_opens_a_window_or_prints_status_and_rejects_model_paths() {
267 assert_eq!(parse_cli(std::iter::empty()), Ok(CliRequest::OpenWindow));
268 assert_eq!(parse_cli(["--status"]), Ok(CliRequest::PrintStatus));
269 assert_eq!(parse_cli(["--help"]), Ok(CliRequest::PrintHelp));
270 assert_eq!(parse_cli(["-h", "--status"]), Ok(CliRequest::PrintHelp));
271 assert_eq!(
272 parse_cli(["tests/fixtures/bodies/test-body-male.glb"]),
273 Err(CliError::UnknownArgument(
274 "tests/fixtures/bodies/test-body-male.glb".to_string()
275 ))
276 );
277 assert_eq!(
278 parse_cli(["--status", "--render"]),
279 Err(CliError::UnknownArgument("--render".to_string()))
280 );
281 assert!(help_text().contains("--preview-fixture"));
282 assert_eq!(
283 parse_cli(["--preview-fixture"]),
284 Ok(CliRequest::PreviewFixtures)
285 );
286 }
287
288 #[test]
289 fn missing_library_message_names_the_library_and_does_not_claim_import() {
290 let message = missing_library_message(&["libxkbcommon-x11.so.0"]);
291 assert!(message.contains("libxkbcommon-x11.so.0"));
292 assert!(message.contains("niet gebouwd"));
293 assert!(!message.to_lowercase().contains("import werkt"));
294 assert!(!LINUX_WINDOW_LIBRARIES.is_empty());
295 }
296}