Skip to main content

oxihuman_export/
nuke_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Nuke .nk script export stub.
6
7/// A Nuke node in the script.
8#[derive(Debug, Clone)]
9pub struct NukeNode {
10    pub class: String,
11    pub name: String,
12    pub knobs: Vec<(String, String)>,
13    pub xpos: i32,
14    pub ypos: i32,
15}
16
17/// A Nuke script export.
18#[derive(Debug, Clone)]
19pub struct NukeScriptExport {
20    pub version: String,
21    pub nodes: Vec<NukeNode>,
22}
23
24/// Create a new Nuke script export.
25pub fn new_nuke_export(version: &str) -> NukeScriptExport {
26    NukeScriptExport {
27        version: version.to_string(),
28        nodes: Vec::new(),
29    }
30}
31
32/// Add a node to the script.
33pub fn nuke_add_node(export: &mut NukeScriptExport, class: &str, name: &str) {
34    export.nodes.push(NukeNode {
35        class: class.to_string(),
36        name: name.to_string(),
37        knobs: Vec::new(),
38        xpos: 0,
39        ypos: 0,
40    });
41}
42
43/// Set a knob on the last node.
44pub fn nuke_set_knob(export: &mut NukeScriptExport, key: &str, value: &str) {
45    if let Some(node) = export.nodes.last_mut() {
46        node.knobs.push((key.to_string(), value.to_string()));
47    }
48}
49
50/// Set the node position.
51pub fn nuke_set_position(export: &mut NukeScriptExport, x: i32, y: i32) {
52    if let Some(node) = export.nodes.last_mut() {
53        node.xpos = x;
54        node.ypos = y;
55    }
56}
57
58/// Return the node count.
59pub fn nuke_node_count(export: &NukeScriptExport) -> usize {
60    export.nodes.len()
61}
62
63/// Serialize the script to a .nk string.
64pub fn nuke_to_string(export: &NukeScriptExport) -> String {
65    let mut out = format!("# Nuke {}\n", export.version);
66    for node in &export.nodes {
67        out.push_str(&format!("{} {{\n", node.class));
68        out.push_str(&format!(" name {}\n", node.name));
69        out.push_str(&format!(" xpos {}\n ypos {}\n", node.xpos, node.ypos));
70        for (k, v) in &node.knobs {
71            out.push_str(&format!(" {} {}\n", k, v));
72        }
73        out.push_str("}\n");
74    }
75    out
76}
77
78/// Estimate the .nk file size.
79pub fn nuke_size_estimate(export: &NukeScriptExport) -> usize {
80    nuke_to_string(export).len()
81}
82
83/// Find a node by name.
84pub fn nuke_find_node<'a>(export: &'a NukeScriptExport, name: &str) -> Option<&'a NukeNode> {
85    export.nodes.iter().find(|n| n.name == name)
86}
87
88/// Validate the export (at least a version string).
89pub fn validate_nuke(export: &NukeScriptExport) -> bool {
90    !export.version.is_empty()
91}
92
93/// Count nodes of a specific class.
94pub fn nuke_count_by_class(export: &NukeScriptExport, class: &str) -> usize {
95    export.nodes.iter().filter(|n| n.class == class).count()
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    fn sample() -> NukeScriptExport {
103        let mut exp = new_nuke_export("15.0");
104        nuke_add_node(&mut exp, "Read", "Read1");
105        nuke_set_knob(&mut exp, "file", "/path/to/file.exr");
106        exp
107    }
108
109    #[test]
110    fn test_node_count() {
111        let exp = sample();
112        assert_eq!(nuke_node_count(&exp), 1);
113    }
114
115    #[test]
116    fn test_to_string_contains_class() {
117        let exp = sample();
118        assert!(nuke_to_string(&exp).contains("Read"));
119    }
120
121    #[test]
122    fn test_validate() {
123        let exp = sample();
124        assert!(validate_nuke(&exp));
125    }
126
127    #[test]
128    fn test_find_node() {
129        let exp = sample();
130        assert!(nuke_find_node(&exp, "Read1").is_some());
131        assert!(nuke_find_node(&exp, "NoSuch").is_none());
132    }
133
134    #[test]
135    fn test_count_by_class() {
136        let exp = sample();
137        assert_eq!(nuke_count_by_class(&exp, "Read"), 1);
138        assert_eq!(nuke_count_by_class(&exp, "Write"), 0);
139    }
140
141    #[test]
142    fn test_set_position() {
143        let mut exp = new_nuke_export("15.0");
144        nuke_add_node(&mut exp, "Dot", "Dot1");
145        nuke_set_position(&mut exp, 100, 200);
146        assert_eq!(exp.nodes[0].xpos, 100);
147        assert_eq!(exp.nodes[0].ypos, 200);
148    }
149
150    #[test]
151    fn test_size_estimate_positive() {
152        let exp = sample();
153        assert!(nuke_size_estimate(&exp) > 0);
154    }
155
156    #[test]
157    fn test_knob_count() {
158        let exp = sample();
159        assert_eq!(exp.nodes[0].knobs.len(), 1);
160    }
161
162    #[test]
163    fn test_empty_export() {
164        let exp = new_nuke_export("15.0");
165        assert_eq!(nuke_node_count(&exp), 0);
166    }
167}