···2929 " splined in.png --nth 50 -o frames/",
3030 " splined images/ -o results/ -n 5000 -b 64 -s 42 --nth 50",
3131 "",
3232- ]
3333- .join("\n")
3434-}
3232+ ].join("\n") }
35333634#[derive(Clone, Debug)]
3737-pub struct Config {
3838- pub input: String,
3939- pub number: Option<u32>,
4040- pub batch: u32,
4141- pub min_accept_ratio: f32,
4242- pub seed: u64,
4343- pub max_gpu: f32,
4444- pub log: u8,
4545- pub output: String,
4646- pub output_provided: bool,
4747- pub current: Option<String>,
4848- pub nth: Option<u32>,
4949- pub bg: Bg,
5050- pub alpha: f32,
5151- pub max_stagnant_batches: u32,
5252-}
3535+pub struct Config { pub input : String
3636+ , pub number : Option<u32>
3737+ , pub batch : u32
3838+ , pub min_accept_ratio : f32
3939+ , pub seed : u64
4040+ , pub max_gpu : f32
4141+ , pub log : u8
4242+ , pub output : String
4343+ , pub output_provided : bool
4444+ , pub current : Option<String>
4545+ , pub nth : Option<u32>
4646+ , pub bg : Bg
4747+ , pub alpha : f32
4848+ , pub max_stagnant_batches : u32 }
53495450#[derive(Clone, Copy, Debug)]
5555-pub enum Bg {
5656- Avg,
5757- RgbU8 { r: u8, g: u8, b: u8 },
5858-}
5151+pub enum Bg { Avg
5252+ , RgbU8 { r: u8, g: u8, b: u8 } }
59536054impl Config {
6161- pub fn parse(argv: &[String]) -> Result<Self, String> {
6262- if argv.len() < 2 {
6363- return Err(usage());
6464- }
5555+ pub fn parse(argv: &[String]) -> Result<Self, String> {
5656+ if argv.len() < 2 { return Err(usage()); }
65576666- let mut input: Option<String> = None;
6767- let mut number: Option<u32> = None;
6868- let mut batch: u32 = 32;
6969- let mut min_accept_ratio: Option<f32> = None;
7070- let mut seed: u64 = 0;
7171- let mut max_gpu: f32 = 1.0;
7272- let mut log: u8 = 1;
7373- let mut output: String = "output.png".to_string();
7474- let mut output_provided: bool = false;
7575- let mut current: Option<String> = None;
7676- let mut nth: Option<u32> = None;
7777- let mut bg: Bg = Bg::Avg;
7878- let mut alpha: f32 = 1.0;
7979- let mut max_stagnant_batches: u32 = 10;
5858+ let mut input : Option<String> = None;
5959+ let mut number : Option<u32> = None;
6060+ let mut batch : u32 = 32;
6161+ let mut min_accept_ratio : Option<f32> = None;
6262+ let mut seed : u64 = 0;
6363+ let mut max_gpu : f32 = 1.0;
6464+ let mut log : u8 = 1;
6565+ let mut output : String = "output.png".to_string();
6666+ let mut output_provided : bool = false;
6767+ let mut current : Option<String> = None;
6868+ let mut nth : Option<u32> = None;
6969+ let mut bg : Bg = Bg::Avg;
7070+ let mut alpha : f32 = 1.0;
7171+ let mut max_stagnant_batches : u32 = 10;
80728181- let mut i = 1usize;
8282- while i < argv.len() {
8383- let a = argv[i].as_str();
8484- if !a.starts_with('-') && input.is_none() {
8585- input = Some(argv[i].clone());
8686- i += 1;
8787- continue;
8888- }
7373+ let mut i = 1usize;
7474+ while i < argv.len() {
7575+ let a = argv[i].as_str();
7676+ if !a.starts_with('-') && input.is_none() { input = Some(argv[i].clone()); i += 1; continue; }
89779078 match a {
9191- "-n" | "--number" => {
9292- i += 1;
9393- number = Some(parse_val(argv, i, a)?);
9494- i += 1;
9595- }
9696- "-b" | "--batch" => {
9797- i += 1;
9898- batch = parse_val(argv, i, a)?;
9999- i += 1;
100100- }
101101- "--min-accept-ratio" => {
102102- i += 1;
103103- min_accept_ratio = Some(parse_val(argv, i, a)?);
104104- i += 1;
105105- }
106106- "-s" | "--seed" => {
107107- i += 1;
108108- seed = parse_val(argv, i, a)?;
109109- i += 1;
110110- }
111111- "--max-gpu" => {
112112- i += 1;
113113- max_gpu = parse_val(argv, i, a)?;
114114- i += 1;
115115- }
116116- "-l" | "--log" => {
117117- i += 1;
118118- log = parse_val(argv, i, a)?;
119119- i += 1;
120120- }
121121- "-o" | "--output" => {
122122- i += 1;
123123- output = parse_val(argv, i, a)?;
124124- output_provided = true;
125125- i += 1;
126126- }
127127- "-c" | "--current" => {
128128- i += 1;
129129- current = Some(parse_val(argv, i, a)?);
130130- i += 1;
131131- }
132132- "--nth" => {
133133- i += 1;
134134- nth = Some(parse_val(argv, i, a)?);
135135- i += 1;
136136- }
137137- "--bg" => {
138138- i += 1;
139139- bg = parse_bg(argv, i, a)?;
140140- i += 1;
141141- }
142142- "-a" | "--alpha" => {
143143- i += 1;
144144- alpha = parse_val(argv, i, a)?;
145145- i += 1;
146146- }
147147- "--max-stagnant-batches" => {
148148- i += 1;
149149- max_stagnant_batches = parse_val(argv, i, a)?;
150150- i += 1;
151151- }
152152- "-h" | "--help" => return Err(usage()),
153153- _ => return Err(format!("unknown arg: {a}\n\n{}", usage())),
154154- }
155155- }
7979+ "-n" | "--number" => { i += 1; number = Some(parse_val(argv, i, a)?); i += 1; }
8080+ "-b" | "--batch" => { i += 1; batch = parse_val(argv, i, a)?; i += 1; }
8181+ "--min-accept-ratio" => { i += 1; min_accept_ratio = Some(parse_val(argv, i, a)?); i += 1; }
8282+ "-s" | "--seed" => { i += 1; seed = parse_val(argv, i, a)?; i += 1; }
8383+ "--max-gpu" => { i += 1; max_gpu = parse_val(argv, i, a)?; i += 1; }
8484+ "-l" | "--log" => { i += 1; log = parse_val(argv, i, a)?; i += 1; }
8585+ "-o" | "--output" => { i += 1; output = parse_val(argv, i, a)?; output_provided = true; i += 1; }
8686+ "-c" | "--current" => { i += 1; current = Some(parse_val(argv, i, a)?); i += 1; }
8787+ "--nth" => { i += 1; nth = Some(parse_val(argv, i, a)?); i += 1; }
8888+ "--bg" => { i += 1; bg = parse_bg(argv, i, a)?; i += 1; }
8989+ "-a" | "--alpha" => { i += 1; alpha = parse_val(argv, i, a)?; i += 1; }
9090+ "--max-stagnant-batches" => { i += 1; max_stagnant_batches = parse_val(argv, i, a)?; i += 1; }
9191+ "-h" | "--help" => return Err(usage()),
9292+ _ => return Err(format!("unknown arg: {a}\n\n{}", usage())), } }
15693157157- let Some(input) = input else {
158158- return Err(usage());
159159- };
9494+ let Some(input) = input else { return Err(usage()); };
16095161161- if batch == 0 {
162162- return Err("batch must be > 0".to_string());
163163- }
164164- let min_accept_ratio = min_accept_ratio.unwrap_or(0.02);
165165- if !(0.0..=1.0).contains(&min_accept_ratio) {
166166- return Err("min-accept-ratio must be in [0, 1]".to_string());
167167- }
168168- if log > 3 {
169169- return Err("log must be in [0, 3]".to_string());
170170- }
171171- if !(0.0 < max_gpu && max_gpu <= 1.0) {
172172- return Err("max-gpu must be in (0, 1]".to_string());
173173- }
174174- if let Some(nth) = nth {
175175- if nth == 0 {
176176- return Err("nth must be > 0".to_string());
177177- }
178178- }
179179- if !(0.0..=1.0).contains(&alpha) {
180180- return Err("alpha must be in [0, 1]".to_string());
181181- }
182182- if max_stagnant_batches == 0 {
183183- return Err("max-stagnant-batches must be > 0".to_string());
184184- }
9696+ if batch == 0 { return Err("batch must be > 0".to_string()); }
9797+9898+ let min_accept_ratio = min_accept_ratio.unwrap_or(0.02);
9999+100100+ if !(0.0..=1.0).contains(&min_accept_ratio) { return Err("min-accept-ratio must be in [0, 1]".to_string()); }
101101+ if log > 3 { return Err("log must be in [0, 3]".to_string()); }
102102+ if !(0.0 < max_gpu && max_gpu <= 1.0) { return Err("max-gpu must be in (0, 1]".to_string()); }
103103+ if let Some(nth) = nth { if nth == 0 { return Err("nth must be > 0".to_string()); } }
104104+ if !(0.0..=1.0).contains(&alpha) { return Err("alpha must be in [0, 1]".to_string()); }
105105+ if max_stagnant_batches == 0 { return Err("max-stagnant-batches must be > 0".to_string()); }
185106186186- Ok(Self {
187187- input,
188188- number,
189189- batch,
190190- min_accept_ratio,
191191- seed,
192192- max_gpu,
193193- log,
194194- output,
195195- output_provided,
196196- current,
197197- nth,
198198- bg,
199199- alpha,
200200- max_stagnant_batches,
201201- })
202202- }
203203-}
107107+ Ok(Self { input, number, batch, min_accept_ratio, seed, max_gpu, log, output, output_provided, current, nth, bg, alpha, max_stagnant_batches, }) } }
204108205109fn parse_val<T: std::str::FromStr>(argv: &[String], idx: usize, flag: &str) -> Result<T, String> {
206110 let s = argv.get(idx).ok_or_else(|| format!("missing value for {flag}"))?;
207207- s.parse::<T>().map_err(|_| format!("invalid value for {flag}: {s}"))
208208-}
111111+ s.parse::<T>().map_err(|_| format!("invalid value for {flag}: {s}")) }
209112210113fn parse_bg(argv: &[String], idx: usize, flag: &str) -> Result<Bg, String> {
211114 let s = argv.get(idx).ok_or_else(|| format!("missing value for {flag}"))?;
212212- if s == "avg" {
213213- return Ok(Bg::Avg);
214214- }
115115+ if s == "avg" { return Ok(Bg::Avg); }
215116 let parts: Vec<_> = s.split(',').collect();
216216- if parts.len() != 3 {
217217- return Err(format!("invalid bg, expected avg or r,g,b: {s}"));
218218- }
117117+ if parts.len() != 3 { return Err(format!("invalid bg, expected avg or r,g,b: {s}")); }
219118 let parse = |i: usize| parts[i].parse::<u8>().map_err(|_| format!("invalid bg component: {s}"));
220220- Ok(Bg::RgbU8 { r: parse(0)?, g: parse(1)?, b: parse(2)? })
221221-}119119+ Ok(Bg::RgbU8 { r: parse(0)?, g: parse(1)?, b: parse(2)? }) }
+15-30
src/fs.rs
···22use std::path::{Path, PathBuf};
3344pub fn validate_and_prepare_dir_output(cfg: &args::Config) -> Result<PathBuf, String> {
55- if !cfg.output_provided {
66- return Err("when input is a directory, -o/--output must be provided".into());
77- }
55+ if !cfg.output_provided { return Err("when input is a directory, -o/--output must be provided".into()); }
8699- let out_root = PathBuf::from(&cfg.output);
1010- if out_root.exists() && !out_root.is_dir() {
1111- return Err(format!("output must be a directory, got file: {}", out_root.display()));
1212- }
1313- std::fs::create_dir_all(&out_root)
1414- .map_err(|e| format!("failed to create output dir {}: {e}", out_root.display()))?;
1515- Ok(out_root)
1616-}
77+ let out_root = PathBuf::from(&cfg.output);
88+ if out_root.exists() && !out_root.is_dir() { return Err(format!("output must be a directory, got file: {}", out_root.display())); }
99+ std::fs::create_dir_all(&out_root).map_err(|e| format!("failed to create output dir {}: {e}", out_root.display()))?;
1010+ Ok(out_root) }
17111812pub fn walk_files_recursive(root: &Path) -> Result<Vec<PathBuf>, String> {
1919- let mut out = Vec::new();
2020- let mut stack = vec![root.to_path_buf()];
1313+ let mut out = Vec::new();
1414+ let mut stack = vec![root.to_path_buf()];
1515+1616+ while let Some(dir) = stack.pop() {
1717+ let rd = std::fs::read_dir(&dir).map_err(|e| format!("failed to read dir {}: {e}", dir.display()))?;
21182222- while let Some(dir) = stack.pop() {
2323- let rd = std::fs::read_dir(&dir)
2424- .map_err(|e| format!("failed to read dir {}: {e}", dir.display()))?;
2525- for entry in rd {
2626- let entry = entry.map_err(|e| format!("failed to read dir entry: {e}"))?;
2727- let ty = entry
2828- .file_type()
2929- .map_err(|e| format!("failed to stat {}: {e}", entry.path().display()))?;
3030- if ty.is_dir() {
3131- stack.push(entry.path());
3232- } else if ty.is_file() {
3333- out.push(entry.path());
3434- }
3535- }
3636- }
1919+ for entry in rd {
2020+ let entry = entry.map_err(|e| format!("failed to read dir entry: {e}"))?;
2121+ let ty = entry.file_type().map_err(|e| format!("failed to stat {}: {e}", entry.path().display()))?;
2222+ if ty.is_dir() { stack.push(entry.path()); } else if ty.is_file() { out.push(entry.path()); } } }
37233838- Ok(out)
3939-}2424+ Ok(out) }