examples_example_cli/
main.rs
1use clap::{Parser, Subcommand};
6
7use example_component::{ApiResult, ExampleComponent, TodoItem};
8
9#[derive(Debug, Parser)]
15#[command(about, long_about = None)]
16struct Cli {
17 #[arg(short, long, action)]
24 verbose: bool,
25
26 #[command(subcommand)]
29 command: Commands,
30}
31
32#[derive(Debug, Subcommand)]
33enum Commands {
34 Lists {
36 #[command(subcommand)]
38 lists_command: Option<ListsCommands>,
39 },
40 Items {
42 list: String,
44 #[command(subcommand)]
45 items_command: Option<ItemsCommands>,
46 },
47}
48
49#[derive(Debug, Subcommand)]
50enum ListsCommands {
51 List,
53 Create {
55 name: String,
57 },
58 Delete { name: String },
60}
61
62#[derive(Debug, Subcommand)]
63enum ItemsCommands {
64 List,
66 Add {
68 name: String,
70 #[arg(short, long)]
71 github_issue: Option<String>,
72 },
73 Update {
75 name: String,
77 #[arg(short, long)]
78 description: Option<String>,
79 #[arg(short, long)]
80 url: Option<String>,
81 #[arg(short, long, action)]
82 toggle: bool,
83 },
84 Delete {
86 name: String,
88 },
89}
90
91fn main() -> ApiResult<()> {
92 let cli = Cli::parse();
93 init_logging(&cli);
94 viaduct_reqwest::use_reqwest_backend();
97 let component = build_example_component()?;
98 println!();
99 match cli.command {
100 Commands::Lists {
101 lists_command: command,
102 } => {
103 let command = command.unwrap_or(ListsCommands::List);
104 handle_lists(component, command)
105 }
106 Commands::Items {
107 list,
108 items_command: command,
109 } => {
110 let command = command.unwrap_or(ItemsCommands::List);
111 handle_todos(component, list, command)
112 }
113 }
114}
115
116fn init_logging(cli: &Cli) {
117 let log_filter = if cli.verbose {
121 "example_component=trace"
122 } else {
123 "example_component=info"
124 };
125 env_logger::init_from_env(env_logger::Env::default().filter_or("RUST_LOG", log_filter));
126}
127
128fn build_example_component() -> ApiResult<ExampleComponent> {
129 let db_path = cli_support::cli_data_path("example-component.db");
132 ExampleComponent::new(&db_path)
133}
134
135fn handle_lists(component: ExampleComponent, subcommand: ListsCommands) -> ApiResult<()> {
136 match subcommand {
137 ListsCommands::List => {
138 let lists = component.get_lists()?;
139 if lists.is_empty() {
140 println!("No lists created");
141 } else {
142 for list in lists {
143 println!("{}", list);
144 }
145 }
146 }
147 ListsCommands::Create { name } => {
148 component.create_list(&name)?;
149 println!("Created list: {name}");
150 }
151 ListsCommands::Delete { name } => {
152 component.delete_list(&name)?;
153 println!("Deleted list: {name}");
154 }
155 }
156 Ok(())
157}
158
159fn handle_todos(
160 component: ExampleComponent,
161 list: String,
162 subcommand: ItemsCommands,
163) -> ApiResult<()> {
164 match subcommand {
165 ItemsCommands::List => {
166 let items = component.get_list_items(&list)?;
167 if items.is_empty() {
168 println!("No items created");
169 } else {
170 println!("{:-^79}", format!(" {list} "));
171 println!(
172 "{:<9} {:<29} {:<29} {:>9}",
173 "name", "description", "url", "completed"
174 );
175 for saved in items {
176 println!(
177 "{:<9} {:<29} {:<29} {:>9}",
178 clamp_string(&saved.item.name, 9),
179 clamp_string(&saved.item.description, 29),
180 clamp_string(&saved.item.url, 29),
181 if saved.item.completed { "X" } else { "" },
182 )
183 }
184 }
185 }
186 ItemsCommands::Add { name, github_issue } => {
187 match github_issue {
188 None => {
189 component.add_item(
190 &list,
191 TodoItem {
192 name: name.clone(),
193 ..TodoItem::default()
194 },
195 )?;
196 println!("Created item: {name}");
197 }
198 Some(github_issue) => {
199 component.add_item_from_gh_issue(&list, &name, &github_issue)?;
200 println!("Created item: {name} (from GH-{github_issue})");
201 }
202 };
203 }
204 ItemsCommands::Update {
205 name,
206 description,
207 url,
208 toggle,
209 } => {
210 let mut saved = component.get_list_item(&list, &name)?;
211 if let Some(description) = description {
212 saved.item.description = description;
213 }
214 if let Some(url) = url {
215 saved.item.url = url;
216 }
217 if toggle {
218 saved.item.completed = !saved.item.completed;
219 }
220 component.update_item(&saved)?;
221 println!("Updated item: {name}");
222 }
223 ItemsCommands::Delete { name } => {
224 let saved = component.get_list_item(&list, &name)?;
225 component.delete_item(saved)?;
226 println!("Deleted item: {name}");
227 }
228 }
229 Ok(())
230}
231
232fn clamp_string(val: &str, max_width: usize) -> String {
233 if val.len() > max_width {
234 format!("{}...", &val[0..max_width - 3])
235 } else {
236 val.to_string()
237 }
238}