BlueGit

golden-age paperdoll-renderer

Files

paperdoll-renderer / src / foundation.rs

foundation.rs

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