1use anyhow::Result;
2use clap::Parser;
3use std::path::PathBuf;
4
5use draw_core::document::Document;
6
7#[derive(Parser, Debug)]
8#[command(name = "draw")]
9#[command(about = "Local-first drawing tool")]
10#[command(version)]
11pub struct Args {
12 #[cfg(feature = "app")]
14 #[arg(short = 'a', long)]
15 pub app: bool,
16
17 #[cfg(feature = "webapp")]
19 #[arg(short = 'w', long)]
20 pub webapp: bool,
21
22 #[command(subcommand)]
23 pub command: Option<Command>,
24}
25
26#[derive(clap::Subcommand, Debug)]
27pub enum Command {
28 New {
30 #[arg(default_value = "untitled")]
32 name: String,
33 },
34 Open {
36 file: PathBuf,
38 },
39 List,
41 ExportSvg {
43 file: PathBuf,
45 #[arg(short, long)]
47 output: Option<PathBuf>,
48 },
49 ExportPng {
51 file: PathBuf,
53 #[arg(short, long)]
55 output: Option<PathBuf>,
56 #[arg(short, long, default_value_t = 2.0)]
58 scale: f32,
59 },
60}
61
62pub fn run_cli(argv: impl IntoIterator<Item = impl Into<String>>) -> Result<()> {
63 let argv: Vec<String> = argv.into_iter().map(Into::into).collect();
64
65 if argv.len() <= 1 {
67 Args::parse_from(["draw", "--help"]);
68 }
69
70 let args = Args::parse_from(&argv);
71
72 #[cfg(feature = "app")]
73 if args.app {
74 return draw_app::run_app(None);
75 }
76
77 #[cfg(feature = "webapp")]
78 if args.webapp {
79 return draw_webapp::run_webapp(None);
80 }
81
82 match args.command {
83 Some(Command::New { name }) => {
84 let doc = Document::new(name);
85 let path = draw_core::storage::save_to_storage(&doc)?;
86 println!("Created: {} ({})", doc.name, path.display());
87
88 #[cfg(feature = "webapp")]
89 return draw_webapp::run_webapp(Some(doc.id));
90
91 #[cfg(not(feature = "webapp"))]
92 {
93 println!("Run with --webapp to open in browser");
94 Ok(())
95 }
96 }
97 Some(Command::Open { file }) => {
98 let doc = draw_core::storage::load(&file)?;
99 println!("Loaded: {} ({} elements)", doc.name, doc.elements.len());
100
101 #[cfg(feature = "webapp")]
102 return draw_webapp::run_webapp(Some(doc.id));
103
104 #[cfg(not(feature = "webapp"))]
105 {
106 println!("Run with --webapp to open in browser");
107 Ok(())
108 }
109 }
110 Some(Command::List) => {
111 let drawings = draw_core::storage::list_drawings()?;
112 if drawings.is_empty() {
113 println!("No saved drawings.");
114 } else {
115 for (name, path) in drawings {
116 println!(" {} ({})", name, path.display());
117 }
118 }
119 Ok(())
120 }
121 Some(Command::ExportSvg { file, output }) => {
122 let doc = draw_core::storage::load(&file)?;
123 let svg = draw_core::export_svg(&doc);
124 let out_path = output.unwrap_or_else(|| file.with_extension("svg"));
125 std::fs::write(&out_path, &svg)?;
126 println!("Exported SVG to {}", out_path.display());
127 Ok(())
128 }
129 Some(Command::ExportPng {
130 file,
131 output,
132 scale,
133 }) => {
134 let doc = draw_core::storage::load(&file)?;
135 let png = draw_core::export_png_with_scale(&doc, scale)?;
136 let out_path = output.unwrap_or_else(|| file.with_extension("png"));
137 std::fs::write(&out_path, &png)?;
138 println!("Exported PNG to {}", out_path.display());
139 Ok(())
140 }
141 None => {
142 Args::parse_from(["draw", "--help"]);
144 unreachable!()
145 }
146 }
147}