satex_core/component/
configurable.rs

1use crate::component::Args;
2use crate::Error;
3use serde::de::DeserializeOwned;
4use serde_yaml::Value;
5use std::cmp::min;
6
7const COMMA: char = ',';
8
9const COLON: char = ':';
10
11const LF: char = '\n';
12
13const LINE: char = '-';
14
15const SPACE: char = ' ';
16
17pub trait Configurable: DeserializeOwned {
18    ///
19    /// 配置字段
20    ///
21    const FIELDS: &'static [&'static str];
22
23    ///
24    /// 配置类关联的Make名称
25    ///
26    const COMPANION: &'static str;
27
28    ///
29    /// 配置快捷模式
30    ///
31    const SHORTCUT_MODE: ShortcutMode;
32
33    ///
34    /// 使用Args反序列化自身
35    ///
36    fn with_args(args: Args) -> Result<Self, Error> {
37        match args {
38            Args::Full(value) => serde_yaml::from_value((*value).clone()).map_err(Error::new),
39            Args::Shortcut(None) => serde_yaml::from_value(Value::default()).map_err(Error::new),
40            Args::Shortcut(Some(value)) => {
41                let mut builder = String::default();
42                let fields = Self::FIELDS;
43                let shortcut_mode = Self::SHORTCUT_MODE;
44                let companion = Self::COMPANION;
45                match shortcut_mode {
46                    ShortcutMode::Object => {
47                        let values = value.split(COMMA).map(|value| value.trim());
48                        let size = min(fields.len(), values.clone().count());
49                        fields
50                            .iter()
51                            .take(size)
52                            .zip(values.take(size))
53                            .for_each(|(k, v)| {
54                                append_k_v(&mut builder, k, v);
55                            });
56                    }
57                    ShortcutMode::Sequence => {
58                        if fields.len() != 1 {
59                            return Err(Error::new(format!(
60                                "[{}] configure with shortcut mode `Sequence` must have exactly 1 field",
61                                companion
62                            )));
63                        }
64                        let field = fields[0];
65                        append_k_seq(&mut builder, field, value);
66                    }
67                    ShortcutMode::TailingSequence => {
68                        if fields.len() != 2 {
69                            return Err(Error::new(format!(
70                                "[{}] configure with shortcut mode [TailingSequence] must have exactly 2 fields",
71                                companion
72                            )));
73                        }
74                        let k1 = fields[0];
75                        let k2 = fields[1];
76                        match value.rsplit_once(COMMA) {
77                            Some((v1, v2)) => {
78                                append_k_v(&mut builder, k1, v1);
79                                append_k_seq(&mut builder, k2, v2);
80                            }
81                            None => {
82                                return Err(Error::new(format!(
83                                    "[{}] configure with shortcut mode [TailingSequence] value must contains `,`",
84                                    companion
85                                )));
86                            }
87                        }
88                    }
89                    ShortcutMode::Unsupported => {
90                        return Err(Error::new(format!(
91                            "[{}] configure unsupported shortcut",
92                            companion
93                        )));
94                    }
95                };
96                serde_yaml::from_str(&builder).map_err(Error::new)
97            }
98        }
99    }
100}
101
102///
103/// 快捷模式 - 参数模式
104///
105#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
106pub enum ShortcutMode {
107    Object,
108    Sequence,
109    TailingSequence,
110    Unsupported,
111}
112
113fn append_k_v(builder: &mut String, key: &str, value: &str) {
114    builder.push_str(key);
115    builder.push(COLON);
116    builder.push(SPACE);
117    builder.push_str(value);
118    builder.push(LF);
119}
120
121fn append_k_seq(builder: &mut String, key: &str, value: &str) {
122    builder.push_str(key);
123    builder.push(COLON);
124    builder.push(LF);
125    value.split(COMMA).for_each(|item| {
126        builder.push(LINE);
127        builder.push(SPACE);
128        builder.push_str(item.trim());
129        builder.push(LF);
130    });
131}