pub enum FormatCode {
    PromptLevel,
    Config,
    ScriptName,
    ScriptType,
    Regex,
    RangeQuery,
    ScriptQuery,
    Tag,
    NonEmptyArray,
    EnvPair,
}

Variants§

§

PromptLevel

§

Config

§

ScriptName

§

ScriptType

§

Regex

§

RangeQuery

§

ScriptQuery

§

Tag

§

NonEmptyArray

§

EnvPair

Implementations§

Examples found in repository?
src/query/range_query.rs (line 17)
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
fn parse_int(s: &str) -> Result<NonZeroU64> {
    let num: NonZeroU64 = s.parse().map_err(|e| {
        RangeQueryCode
            .to_err(s.to_owned())
            .context(format!("解析整數錯誤 {}", e))
    })?;
    Ok(num)
}

impl RangeQuery {
    pub fn get_max(&self) -> Option<NonZeroU64> {
        self.max
    }
    pub fn get_min(&self) -> NonZeroU64 {
        self.min
    }
}

impl FromStr for RangeQuery {
    type Err = DisplayError;
    fn from_str(s: &str) -> DisplayResult<Self> {
        if let Some((first, second)) = s.split_once(SEP) {
            if first.is_empty() && second.is_empty() {
                return Err(RangeQueryCode
                    .to_err(s.to_owned())
                    .context("不可前後皆為空")
                    .into());
            }
            let min = if first.is_empty() {
                NonZeroU64::new(1).unwrap()
            } else {
                parse_int(first)?
            };
            let max = if second.is_empty() {
                None
            } else {
                let max = parse_int(second)?;
                if max <= min {
                    return Err(RangeQueryCode
                        .to_err(s.to_owned())
                        .context("max 不可小於等於 min")
                        .into());
                }
                Some(max)
            };
            Ok(RangeQuery { min, max })
        } else {
            let num = parse_int(s)?;
            Ok(RangeQuery {
                min: num,
                max: Some(NonZeroU64::new(num.get() + 1).unwrap()),
            })
        }
    }
More examples
Hide additional examples
src/config.rs (line 29)
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
fn de_nonempty_vec<'de, D, T>(deserializer: D) -> std::result::Result<Vec<T>, D::Error>
where
    D: serde::de::Deserializer<'de>,
    T: Deserialize<'de>,
{
    let v: Vec<T> = Deserialize::deserialize(deserializer)?;
    if v.is_empty() {
        return Err(serde::de::Error::custom(
            FormatCode::NonEmptyArray.to_err(String::new()),
        ));
    }
    Ok(v)
}

fn config_file(home: &Path) -> PathBuf {
    home.join(CONFIG_FILE)
}

fn is_false(b: &bool) -> bool {
    !*b
}

#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub struct NamedTagSelector {
    pub content: TagSelector,
    pub name: String,
    #[serde(default, skip_serializing_if = "is_false")]
    pub inactivated: bool,
}

#[derive(Deserialize, Serialize, PartialEq, Eq, Debug, Clone)]
pub struct Alias {
    pub after: Vec<String>,
}
impl From<Vec<String>> for Alias {
    fn from(after: Vec<String>) -> Self {
        Alias { after }
    }
}

#[derive(Display, PartialEq, Eq, Debug, Clone, Copy)]
pub enum PromptLevel {
    #[display(fmt = "always")]
    Always,
    #[display(fmt = "never")]
    Never,
    #[display(fmt = "smart")]
    Smart,
    #[display(fmt = "on_multi_fuzz")]
    OnMultiFuzz,
}
impl FromStr for PromptLevel {
    type Err = DisplayError;
    fn from_str(s: &str) -> DisplayResult<Self> {
        let l = match s {
            "always" => PromptLevel::Always,
            "never" => PromptLevel::Never,
            "smart" => PromptLevel::Smart,
            "on-multi-fuzz" => PromptLevel::OnMultiFuzz,
            _ => return FormatCode::PromptLevel.to_display_res(s.to_owned()),
        };
        Ok(l)
    }
}
impl_ser_by_to_string!(PromptLevel);
impl_de_by_from_str!(PromptLevel);

#[derive(Deserialize, Serialize, PartialEq, Eq, Debug, Clone)]
pub struct Config {
    pub recent: Option<u32>,
    pub main_tag_selector: TagSelector,
    prompt_level: PromptLevel,
    #[serde(deserialize_with = "de_nonempty_vec")]
    pub editor: Vec<String>,
    pub tag_selectors: Vec<NamedTagSelector>,
    pub alias: HashMap<String, Alias>,
    pub types: HashMap<ScriptType, ScriptTypeConfig>,
    pub env: HashMap<String, String>,
    #[serde(skip)]
    last_modified: Option<SystemTime>,
}
impl Default for Config {
    fn default() -> Self {
        fn gen_alias(from: &str, after: &[&str]) -> (String, Alias) {
            (
                from.to_owned(),
                Alias {
                    after: after.iter().map(|s| s.to_string()).collect(),
                },
            )
        }
        Config {
            last_modified: None,
            recent: Some(999999), // NOTE: 顯示兩千多年份的資料!
            editor: vec!["vim".to_string()],
            prompt_level: PromptLevel::Smart,
            tag_selectors: vec![
                NamedTagSelector {
                    content: "+pin,util".parse().unwrap(),
                    name: "pin".to_owned(),
                    inactivated: false,
                },
                NamedTagSelector {
                    content: "+all,^hide!".parse().unwrap(),
                    name: "no-hidden".to_owned(),
                    inactivated: false,
                },
                NamedTagSelector {
                    content: "+all,^remove!".parse().unwrap(),
                    name: "no-removed".to_owned(),
                    inactivated: false,
                },
            ],
            main_tag_selector: "+all".parse().unwrap(),
            types: ScriptTypeConfig::default_script_types(),
            alias: [
                gen_alias("la", &["ls", "-a"]),
                gen_alias("ll", &["ls", "-l"]),
                gen_alias("l", &["ls", "--grouping", "none", "--limit", "5"]),
                gen_alias("e", &["edit"]),
                gen_alias("gc", &["rm", "--timeless", "--purge", "-s", "remove", "*"]),
                gen_alias("t", &["tags"]),
                gen_alias("p", &["run", "--previous"]),
                gen_alias("pc", &["=util/historian!", "--sequence", "c", "--show-env"]),
                gen_alias("pr", &["=util/historian!", "--sequence", "r", "--show-env"]),
                gen_alias("purge", &["rm", "--purge"]),
                gen_alias("h", &["=util/historian!", "--show-env"]),
            ]
            .into_iter()
            .collect(),
            env: [
                ("NAME", "{{name}}"),
                ("HS_HOME", "{{home}}"),
                ("HS_CMD", "{{cmd}}"),
                ("HS_RUN_ID", "{{run_id}}"),
                (
                    "HS_TAGS",
                    "{{#each tags}}{{{this}}}{{#unless @last}} {{/unless}}{{/each}}",
                ),
                (
                    "HS_ENV_DESC",
                    "{{#each env_desc}}{{{this}}}{{#unless @last}}\n{{/unless}}{{/each}}",
                ),
                ("HS_EXE", "{{exe}}"),
                ("HS_SOURCE", "{{home}}/.hs_source"),
                ("TMP_DIR", "/tmp"),
            ]
            .into_iter()
            .map(|(k, v)| (k.to_owned(), v.to_owned()))
            .collect(),
        }
    }
}
impl Config {
    pub fn load(p: &Path) -> Result<Self> {
        let path = config_file(p);
        log::info!("載入設定檔:{:?}", path);
        match util::read_file(&path) {
            Ok(s) => {
                let meta = util::handle_fs_res(&[&path], std::fs::metadata(&path))?;
                let modified = util::handle_fs_res(&[&path], meta.modified())?;

                let mut conf: Config = toml::from_str(&s).map_err(|err| {
                    FormatCode::Config.to_err(format!("{}: {}", path.to_string_lossy(), err))
                })?;
                conf.last_modified = Some(modified);
                Ok(conf)
            }
            Err(Error::PathNotFound(_)) => {
                log::debug!("找不到設定檔");
                Ok(Default::default())
            }
            Err(e) => Err(e),
        }
    }
Examples found in repository?
src/script.rs (line 42)
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
    fn valid(s: &str) -> Result {
        // FIXME: 好好想想什麼樣的腳本名可行,並補上單元測試
        for s in s.split('/') {
            if illegal_name(s) {
                return ScriptNameCode.to_res(s.to_owned());
            }
        }
        Ok(())
    }
    pub fn new(s: String) -> Result<Self> {
        Self::valid(&s)?;
        Ok(ConcreteScriptName { inner: s })
    }
    pub fn new_id(id: u32) -> Self {
        ConcreteScriptName {
            inner: id.to_string(),
        }
    }
    fn new_unchecked(s: String) -> Self {
        ConcreteScriptName { inner: s }
    }
    fn stem_inner(&self) -> &str {
        if let Some((_, stem)) = self.inner.rsplit_once('/') {
            stem
        } else {
            &self.inner
        }
    }
    pub fn stem(&self) -> ConcreteScriptName {
        Self::new_unchecked(self.stem_inner().to_owned())
    }
    pub fn join(&mut self, other: &ConcreteScriptName) {
        write!(&mut self.inner, "/{}", other.stem_inner()).unwrap();
    }
    pub fn join_id(&mut self, other: u32) {
        write!(&mut self.inner, "/{}", other).unwrap();
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ScriptName {
    Anonymous(u32),
    Named(ConcreteScriptName),
}
impl FromStr for ScriptName {
    type Err = DisplayError;
    fn from_str(s: &str) -> DisplayResult<Self> {
        let n = s.to_owned().into_script_name()?;
        Ok(n)
    }
}
impl ScriptName {
    #[inline]
    pub fn valid(
        mut s: &str,
        allow_endwith_slash: bool,
        allow_dot: bool,
        check: bool,
    ) -> Result<Option<u32>> {
        log::debug!("檢查腳本名:{}", s);

        if s.starts_with('.') {
            if s.len() == 1 && allow_dot {
                log::info!("特殊規則:允許單一個`.`");
                return Ok(None); // NOTE: 讓匿名腳本可以直接用 `.` 來搜
            }
            match s[1..].parse::<u32>() {
                Ok(id) => Ok(Some(id)),
                Err(e) => ScriptNameCode.to_res(s.to_owned()).context(e),
            }
        } else if check {
            if s.ends_with('/') && allow_endwith_slash {
                log::info!("特殊規則:允許以`/`結尾");
                s = &s[..s.len() - 1]; // NOTE: 有了補全,很容易補出帶著`/`結尾的指令,放寬標準吧
            }
            ConcreteScriptName::valid(&s)?;
            Ok(None)
        } else {
            Ok(None)
        }
    }
More examples
Hide additional examples
src/query/mod.rs (line 201)
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
fn parse_prev(s: &str) -> Result<NonZeroUsize> {
    // NOTE: 解析 `^^^^ = Prev(4)`
    let mut is_pure_prev = true;
    for ch in s.chars() {
        if ch != '^' {
            is_pure_prev = false;
            break;
        }
    }
    if is_pure_prev {
        return Ok(none0_usize(s.len()));
    }
    // NOTE: 解析 `^4 = Prev(4)`
    match s[1..s.len()].parse::<NonZeroUsize>() {
        Ok(prev) => Ok(prev),
        Err(e) => ScriptQueryCode
            .to_res(s.to_owned())
            .context(format!("解析整數錯誤:{}", e)),
    }
}
Examples found in repository?
src/tag.rs (line 98)
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
    fn from_str(s: &str) -> DisplayResult<Self> {
        if illegal_name(s) {
            log::error!("標籤格式不符:{}", s);
            return TagCode.to_display_res(s.to_owned());
        }
        Ok(Tag(s.to_owned()))
    }
}
impl FromStr for TagControl {
    type Err = DisplayError;
    fn from_str(mut s: &str) -> DisplayResult<Self> {
        let allow = if s.starts_with('^') {
            s = &s[1..s.len()];
            false
        } else {
            true
        };
        Ok(TagControl {
            tag: s.parse()?,
            allow,
        })
    }
}
const MANDATORY_SUFFIX: &str = "!";
const APPEND_PREFIX: &str = "+";
impl FromStr for TagSelector {
    type Err = DisplayError;
    fn from_str(mut s: &str) -> DisplayResult<Self> {
        let append = if s.starts_with(APPEND_PREFIX) {
            s = &s[APPEND_PREFIX.len()..];
            true
        } else {
            false
        };

        let mandatory = if s.ends_with(MANDATORY_SUFFIX) {
            s = &s[0..(s.len() - MANDATORY_SUFFIX.len())];
            true
        } else {
            false
        };

        let mut tags = vec![];
        for ctrl in s.split(',') {
            tags.push(ctrl.parse()?);
        }
        if tags.is_empty() {
            return TagCode.to_display_res(s.to_owned());
        }
        Ok(TagSelector {
            tags,
            append,
            mandatory,
        })
    }
More examples
Hide additional examples
src/script_type.rs (line 181)
178
179
180
181
182
183
184
    fn from_str(s: &str) -> DisplayResult<Self> {
        if illegal_name(s) {
            log::error!("類型格式不符:{}", s);
            return TypeCode.to_display_res(s.to_owned());
        }
        Ok(ScriptType(s.to_string()))
    }
src/env_pair.rs (line 40)
33
34
35
36
37
38
39
40
41
42
    fn from_str(s: &str) -> DisplayResult<Self> {
        if let Some((key, val)) = s.split_once('=') {
            Ok(EnvPair {
                key: key.to_owned(),
                val: val.to_owned(),
            })
        } else {
            EnvPairCode.to_display_res(s.to_owned())
        }
    }
src/config.rs (line 80)
74
75
76
77
78
79
80
81
82
83
    fn from_str(s: &str) -> DisplayResult<Self> {
        let l = match s {
            "always" => PromptLevel::Always,
            "never" => PromptLevel::Never,
            "smart" => PromptLevel::Smart,
            "on-multi-fuzz" => PromptLevel::OnMultiFuzz,
            _ => return FormatCode::PromptLevel.to_display_res(s.to_owned()),
        };
        Ok(l)
    }
src/query/mod.rs (line 128)
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
    fn from_str(s: &str) -> DisplayResult<Self> {
        if s.contains('*') {
            // TODO: 好好檢查
            let s = s.to_owned();
            let re = s.replace(".", r"\.");
            let re = re.replace("*", ".*");
            let (re, bang) = if re.ends_with('!') {
                (&re[0..re.len() - 1], true)
            } else {
                (&re[..], false)
            };
            match Regex::new(&format!("^{re}$",)) {
                Ok(re) => Ok(ListQuery::Pattern(re, s, bang)),
                Err(e) => {
                    log::error!("正規表達式錯誤:{}", e);
                    RegexCode.to_display_res(s)
                }
            }
        } else {
            Ok(ListQuery::Query(s.parse()?))
        }
    }

Trait Implementations§

Returns a copy of the value. Read more
Performs copy-assignment from source. Read more
Formats the value using the given formatter. Read more
This method tests for self and other values to be equal, and is used by ==.
This method tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.

Auto Trait Implementations§

Blanket Implementations§

Gets the TypeId of self. Read more
Immutably borrows from an owned value. Read more
Mutably borrows from an owned value. Read more
Compare self to key and return true if they are equal.

Returns the argument unchanged.

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Should always be Self
The resulting type after obtaining ownership.
Creates owned data from borrowed data, usually by cloning. Read more
Uses borrowed data to replace owned data, usually by cloning. Read more
The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.