1#![allow(non_snake_case)]
2
3use std::io::prelude::*;
4use std::fs::File;
5use std::path::PathBuf;
6use std::process::Command;
7
8#[macro_use]
9mod error;
10mod breed;
11
12use error::Error;
13
14fn runDot(dot_code: &str, output_format: &str) -> Result<Vec<u8>, Error>
15{
16 let mut proc = Command::new("dot")
17 .arg("-T")
18 .arg(output_format)
19 .stdin(std::process::Stdio::piped())
20 .stdout(std::process::Stdio::piped())
21 .spawn()
22 .map_err(|e| rterr!("Failed to run dot: {}", e))?;
23 proc.stdin.take().unwrap().write_all(dot_code.as_bytes()).map_err(
24 |e| rterr!("Failed to write input to dot: {}", e))?;
25 let output = proc.wait_with_output().map_err(
26 |e| rterr!("Failed to wait for dot to finish: {}", e))?;
27 if !output.status.success()
28 {
29 return Err(rterr!("Dot command failed"));
30 }
31
32 Ok(output.stdout)
33}
34
35fn getOutputFileName(input_file: &str, output_type: &str) ->
36 Result<String, Error>
37{
38 let path = PathBuf::from(input_file);
39 let dir = if let Some(d) = path.parent()
40 {
41 d.to_owned()
42 }
43 else
44 {
45 std::env::current_dir().map_err(
46 |e| rterr!("Failed to get current dir: {}", e))?
47 };
48 let basename: &str = path.file_stem().ok_or_else(
49 || rterr!("Failed to get basename of input file"))?.to_str()
50 .ok_or_else(|| rterr!("Input file name is too weird"))?;
51 let output_basename_with_ext = format!("{}.{}", basename, output_type);
52 let mut output_path = PathBuf::new();
53 output_path.push(dir);
54 output_path.push(output_basename_with_ext);
55 Ok(output_path.to_str().ok_or_else(
56 || rterr!("Output file name is too weird"))?.to_owned())
57}
58
59fn main() -> Result<(), Error>
60{
61 let options = clap::Command::new("DWM planner").arg(
62 clap::Arg::new("FILE").required(true)
63 .help("Input breed plan file")).arg(
64 clap::Arg::new("output").short('o').long("output")
65 .value_name("FILE")
66 .help("Output file")).arg(
67 clap::Arg::new("output_type").short('t').long("output-type")
68 .value_name("TYPE").default_value("pdf")
69 .help("Output file type. E.g. 'pdf', 'svg', etc. \
70 This argument is passed to the dot -T option."))
71 .get_matches();
72
73 let filename = options.get_one::<String>("FILE").unwrap();
74
75 let f = std::fs::File::open(filename).map_err(|e| rterr!("{}", e))?;
76 let output_file = if let Some(output) = options.get_one::<String>("output")
77 {
78 String::from(output)
79 }
80 else
81 {
82 getOutputFileName(
83 filename, options.get_one::<String>("output_type").unwrap())?
84 };
85
86 let plan = breed::BreedPlan::fromStream(&mut std::io::BufReader::new(f))?;
87 let output = runDot(
88 &plan.toDot(), options.get_one::<String>("output_type").unwrap())?;
89 let mut f = File::create(output_file).map_err(
90 |e| rterr!("Failed to open output file: {}", e))?;
91 f.write_all(&output).map_err(
92 |e| rterr!("Failed to write output file: {}", e))?;
93
94 Ok(())
95}