1use clap::Parser;
2use switchbot_api::{CommandRequest, Device, SwitchBot};
3
4use crate::{Args, UserInput};
5
6#[derive(Debug, Default)]
7pub struct Cli {
8 args: Args,
9 switch_bot: SwitchBot,
10 current_device_index: Option<usize>,
11}
12
13impl Cli {
14 pub fn new_from_args() -> Self {
15 let mut args = Args::parse();
16 if !args.clear {
17 if let Err(error) = args.load() {
18 log::debug!("Load config error: {error}");
19 }
20 }
21 Self {
22 args,
23 ..Default::default()
24 }
25 }
26
27 fn has_current_device(&self) -> bool {
28 self.current_device_index.is_some()
29 }
30
31 fn current_device(&self) -> Option<&Device> {
32 if let Some(index) = self.current_device_index {
33 return self.switch_bot.devices().get(index);
34 }
35 None
36 }
37
38 pub async fn run(&mut self) -> anyhow::Result<()> {
39 self.switch_bot = self.args.create_switch_bot()?;
40 self.switch_bot.load_devices().await?;
41
42 if !self.args.commands.is_empty() {
43 for command in self.args.commands.clone() {
44 self.execute_command(&command).await?;
45 }
46 return Ok(());
47 }
48
49 let mut input = UserInput::new();
50 loop {
51 if let Some(device) = self.current_device() {
52 println!("{device:#}");
53 input.set_prompt("Command> ");
54 } else {
55 self.print_devices();
56 input.set_prompt("Device> ");
57 }
58
59 let input_text = input.read_line()?;
60 match input_text {
61 "q" => break,
62 "" => {
63 if self.has_current_device() {
64 self.current_device_index = None;
65 continue;
66 }
67 break;
68 }
69 _ => self.execute_command(input_text).await?,
70 }
71 }
72 self.args.save()?;
73 Ok(())
74 }
75
76 fn print_devices(&self) {
77 for (i, device) in self.switch_bot.devices().iter().enumerate() {
78 println!("{}: {device}", i + 1);
79 }
80 }
81
82 async fn execute_command(&mut self, text: &str) -> anyhow::Result<()> {
83 if self.set_current_device(text).is_ok() {
84 return Ok(());
85 }
86 if let Some(device) = self.current_device() {
87 let command = self.parse_command(text);
88 device.command(&command).await?;
89 return Ok(());
90 }
91 self.set_current_device(text)?;
92 Ok(())
93 }
94
95 fn set_current_device(&mut self, value: &str) -> anyhow::Result<()> {
96 self.current_device_index = Some(self.parse_device_index(value)?);
97 log::debug!("current_device_index={:?}", self.current_device_index);
98 Ok(())
99 }
100
101 fn parse_device_index(&self, value: &str) -> anyhow::Result<usize> {
102 if let Ok(number) = value.parse::<usize>() {
103 return Ok(number - 1);
104 }
105 self.switch_bot
106 .devices()
107 .index_by_device_id(value)
108 .ok_or_else(|| anyhow::anyhow!("Not a valid device: {value}"))
109 }
110
111 fn parse_command(&self, mut text: &str) -> CommandRequest {
112 let mut command = CommandRequest::default();
113 if let Some((name, parameter)) = text.split_once(':') {
114 command.parameter = parameter.into();
115 text = name;
116 }
117 if let Some((command_type, name)) = text.split_once('/') {
118 command.command_type = command_type.into();
119 text = name;
120 }
121 command.command = text.into();
122 command
123 }
124}