dce_cli/
protocol.rs

1use std::any::Any;
2use std::collections::HashMap;
3use std::env::args;
4use std::fmt::Debug;
5use std::ops::{Deref, DerefMut};
6use std::sync::Arc;
7use dce_router::protocol::{HEAD_PATH_NAME, Meta, RoutableProtocol};
8use dce_router::request::{Request, Context, Response};
9use dce_router::router::Router;
10use dce_router::serializer::Serialized;
11use dce_util::mixed::DceResult;
12#[cfg(feature = "async")]
13use async_trait::async_trait;
14
15pub type CliRaw<'a> = Request<'a, CliProtocol, (), ()>;
16pub type CliGet<'a, Resp> = Request<'a, CliProtocol, (), Resp>;
17
18const PASS_SEPARATOR: &str = "--";
19
20enum ArgType {
21    AssignExpr(String, String), // arg is an assign expr, 0th is name, 1st is value
22    PrefixName, // - prefix name
23    DownFlowSeparator, // a separator to separate remain args to down flow cli
24    Path, // path type arg
25}
26
27#[derive(Debug)]
28pub struct CliProtocol {
29    meta: Meta<Vec<String>, String>,
30    pass: Vec<String>,
31    args: HashMap<String, String>,
32}
33
34impl CliProtocol {
35    pub fn pass(&self) -> &Vec<String> {
36        &self.pass
37    }
38
39    pub fn args(&self) -> &HashMap<String, String> {
40        &self.args
41    }
42
43    pub fn args_mut(&mut self) -> &mut HashMap<String, String> {
44        &mut self.args
45    }
46
47    #[cfg(feature = "async")]
48    pub async fn route(self, router: Arc<Router<Self>>, context_data: HashMap<String, Box<dyn Any + Send>>) {
49        if let Some(resp) = Self::handle(self, router, context_data).await {
50            println!("{resp}");
51        }
52    }
53
54    #[cfg(not(feature = "async"))]
55    pub fn route(self, router: Arc<Router<Self>>, context_data: HashMap<String, Box<dyn Any + Send>>) {
56        if let Some(resp) = Self::handle(self, router, context_data) {
57            println!("{resp}");
58        }
59    }
60
61    pub fn new(base: usize) -> Self {
62        let raw = args().collect::<Vec<_>>();
63        let mut cli = Self::from(raw.iter().skip(base).map(|a| a.clone()).collect::<Vec<_>>());
64        *cli.req_mut() = Some(raw);
65        cli
66    }
67
68    fn parse_type(arg: &str) -> ArgType {
69        return if let Some((left, right)) = arg.split_once("=") {
70            ArgType::AssignExpr(left.to_string(), right.to_string())
71        } else if arg.starts_with("-") {
72            if arg == PASS_SEPARATOR { return ArgType::DownFlowSeparator }
73            ArgType::PrefixName
74        } else {
75            ArgType::Path
76        }
77    }
78}
79
80impl From<Vec<String>> for CliProtocol {
81    fn from(mut value: Vec<String>) -> Self {
82        let mut pass = vec![];
83        let mut paths = vec![];
84        let mut args = HashMap::<String, String>::new();
85
86        while ! value.is_empty() {
87            let arg = value.remove(0);
88            match Self::parse_type(&arg) {
89                ArgType::AssignExpr(name, value) => { args.insert(name, value); },
90                ArgType::PrefixName => {
91                    args.insert(arg, match value.get(0) {
92                        Some(next) if matches!(Self::parse_type(next), ArgType::Path) => value.remove(0),
93                        _ => String::new(),
94                    });
95                },
96                ArgType::DownFlowSeparator => while ! value.is_empty() {
97                    pass.push(value.remove(0));
98                },
99                _ => paths.push(arg),
100            }
101        }
102
103        CliProtocol { meta: Meta::new(vec![], HashMap::from([(HEAD_PATH_NAME.to_string(), paths.join("/"))])), pass, args }
104    }
105}
106
107impl Into<String> for CliProtocol {
108    fn into(mut self) -> String {
109        #[allow(unused_mut)]
110        let mut resp = match self.resp_mut().take() {
111            Some(Response::Serialized(sd)) => self.pack_resp(sd),
112            Some(Response::Raw(resp)) => resp,
113            _ => "".to_string(),
114        };
115        #[cfg(feature = "session")]
116        if let Some(resp_sid) = self.get_resp_sid() {
117            resp.push_str(format!("\n\nNew sid: {}", resp_sid).as_str());
118        }
119        resp
120    }
121}
122
123impl Deref for CliProtocol {
124    type Target = Meta<Vec<String>, String>;
125
126    fn deref(&self) -> &Self::Target {
127        &self.meta
128    }
129}
130
131impl DerefMut for CliProtocol {
132    fn deref_mut(&mut self) -> &mut Self::Target {
133        &mut self.meta
134    }
135}
136
137#[cfg_attr(feature = "async", async_trait)]
138impl RoutableProtocol for CliProtocol {
139    type Req = Vec<String>;
140    type Resp = String;
141
142    #[cfg(feature = "async")]
143    async fn body(&mut self) -> DceResult<Serialized> {
144        unreachable!("not support cli body yet")
145    }
146
147    #[cfg(not(feature = "async"))]
148    fn body(&mut self) -> DceResult<Serialized> {
149        unreachable!("not support cli body yet")
150    }
151
152    fn pack_resp(&self, serialized: Serialized) -> Self::Resp {
153        match serialized {
154            Serialized::String(str) => str,
155            Serialized::Bytes(bytes) => String::from_utf8_lossy(bytes.as_ref()).to_string(),
156        }
157    }
158
159    fn handle_result(self, result: DceResult<()>, context: &mut Context<Self>) -> Option<Self::Resp> {
160        Self::try_print_err(&result);
161        if ! result.is_err() && ! context.api().map_or(false, |a| a.unresponsive()) {
162            return Some(self.into());
163        }
164        None
165    }
166
167    #[cfg(feature = "session")]
168    fn sid(&self) -> Option<&str> {
169        self.args.get("--sid").map(|a| a.as_str())
170    }
171}