plottery_project/
project_main.rs

1use bincode::serialize;
2use clap::{Parser, Subcommand};
3use plottery_lib::Layer;
4use std::error::Error;
5use std::io::{self, Read, Write};
6use std::path::PathBuf;
7
8use crate::project_param::ProjectParam;
9use crate::project_params_definition::PlotteryParams;
10use crate::project_params_list_wrapper::ProjectParamsListWrapper;
11
12#[derive(Debug, Clone, Subcommand)]
13enum RunCommand {
14    Svg {
15        path: Option<String>,
16        scale: Option<f32>,
17    },
18    StdOut {
19        #[arg(long, short)]
20        piped_params: Option<bool>,
21    },
22    NamedPipe {
23        #[arg(long, short)]
24        piped_params: Option<bool>,
25        pipe_path: String,
26    },
27    Dry {},
28    Params {},
29}
30
31#[derive(Parser, Debug)]
32#[command(
33    about = "Plotter-project executable.",
34    long_about = "Can be used to run the project and e.g. directly open as .svg."
35)]
36struct Args {
37    #[command(subcommand)]
38    command: RunCommand,
39}
40
41fn read_params_from_stdin() -> Result<Vec<ProjectParam>, Box<dyn Error>> {
42    let mut buffer = Vec::new();
43    io::stdin().read_to_end(&mut buffer)?;
44    let params_list_wrapper: ProjectParamsListWrapper = bincode::deserialize(&buffer)?;
45    Ok(params_list_wrapper.list)
46}
47
48pub fn run_project<P, F>(generate_function: F) -> Result<(), Box<dyn Error>>
49where
50    P: PlotteryParams,
51    F: Fn(P) -> Layer,
52{
53    let args = Args::parse();
54    match args.command {
55        RunCommand::Svg { path, scale } => {
56            let scale = scale.unwrap_or(10.0);
57
58            match path {
59                Some(path_string) => {
60                    let path = PathBuf::from(&path_string);
61                    if path.is_dir() {
62                        panic!(
63                            "Failed to write .svg - Path '{}' is a directory.",
64                            path_string
65                        );
66                    }
67                    let art = generate_function(P::new_with_defaults());
68                    art.write_svg(path, scale)?;
69                }
70                None => {
71                    let path = std::env::temp_dir().join("temp_cli.svg");
72                    let art = generate_function(P::new_with_defaults());
73                    art.write_svg(path.clone(), scale)?;
74
75                    open::that_in_background(path)
76                        .join()
77                        .expect("Failed to join background process for opening SVG")
78                        .expect("Failed to open SVG file in default viewer");
79                }
80            }
81        }
82        RunCommand::StdOut { piped_params } => {
83            let wait_for_stdin = piped_params.unwrap_or(false);
84
85            let params = if wait_for_stdin {
86                let list = read_params_from_stdin()?;
87                P::new_from_list(list)
88            } else {
89                P::new_with_defaults()
90            };
91
92            let mut stdout = io::stdout().lock();
93            let art = generate_function(params);
94            let binary = art
95                .to_binary()
96                .expect("Failed to convert layer to binary format");
97            stdout.write_all(&binary)?;
98        }
99        RunCommand::NamedPipe {
100            piped_params,
101            pipe_path,
102        } => {
103            let wait_for_stdin = piped_params.unwrap_or(false);
104
105            let params = if wait_for_stdin {
106                let list = read_params_from_stdin()?;
107                P::new_from_list(list)
108            } else {
109                P::new_with_defaults()
110            };
111
112            let art = generate_function(params);
113            let binary = art
114                .to_binary()
115                .expect("Failed to convert layer to binary format");
116
117            let mut file = std::fs::OpenOptions::new()
118                .write(true)
119                .create(true)
120                .truncate(true)
121                .open(&pipe_path)
122                .expect("Failed to open pipe.");
123            file.write_all(&binary)?;
124        }
125        RunCommand::Dry {} => {
126            generate_function(P::new_with_defaults());
127        }
128        RunCommand::Params {} => {
129            let mut stdout = io::stdout().lock();
130            let params_list = ProjectParamsListWrapper::new(P::param_defaults_list());
131            let binary = serialize(&params_list)?;
132            stdout.write_all(&binary)?;
133        }
134    }
135    Ok(())
136}