switchbot_cli/
cli.rs

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}