hglib/commands/
config.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this file,
3// You can obtain one at http://mozilla.org/MPL/2.0/.
4
5use crate::client::{Client, HglibError, Runner};
6use crate::{runcommand, MkArg};
7
8pub struct Arg<'a> {
9    pub names: &'a [&'a str],
10    pub untrusted: bool,
11    pub showsource: bool,
12}
13
14impl<'a> Default for Arg<'a> {
15    fn default() -> Self {
16        Self {
17            names: &[],
18            untrusted: false,
19            showsource: false,
20        }
21    }
22}
23
24impl<'a> Arg<'a> {
25    fn run(&self, client: &mut Client) -> Result<(Vec<u8>, i32), HglibError> {
26        runcommand!(
27            client,
28            "showconfig",
29            self.names,
30            "-u",
31            self.untrusted,
32            "--debug",
33            self.showsource
34        )
35    }
36}
37
38#[derive(Debug, PartialEq)]
39pub struct Config {
40    pub source: Option<String>,
41    pub section: String,
42    pub key: String,
43    pub value: String,
44}
45
46impl Config {
47    fn no_source(line: &[u8]) -> Result<Self, HglibError> {
48        let (section, key, value) = splitline(line)?;
49        Ok(Self {
50            source: None,
51            section: section.to_string(),
52            key: key.to_string(),
53            value: value.to_string(),
54        })
55    }
56
57    fn with_source(source: &str, line: &[u8]) -> Result<Self, HglibError> {
58        let (section, key, value) = splitline(line)?;
59        Ok(Self {
60            source: Some(source.to_string()),
61            section: section.to_string(),
62            key: key.to_string(),
63            value: value.to_string(),
64        })
65    }
66}
67
68fn splitline(data: &[u8]) -> Result<(&str, &str, &str), HglibError> {
69    let data = std::str::from_utf8(data)?;
70    let err = || HglibError::from(format!("Invalid line in config: {}", data));
71    let data = data.trim_end();
72    let mut iter = data.rsplitn(2, '=');
73    let value = iter.next().ok_or_else(err)?;
74    let section_key = iter.next().ok_or_else(err)?;
75
76    let mut iter = section_key.splitn(2, '.');
77    let section = iter.next().ok_or_else(err)?;
78    let key = iter.next().ok_or_else(err)?;
79
80    Ok((section, key, value))
81}
82
83fn get_skv(line: &[u8]) -> Option<(&[u8], &[u8])> {
84    for (n, c) in line.windows(2).enumerate() {
85        if c == b": " {
86            return Some((&line[..n], &line[n + 2..]));
87        }
88    }
89    None
90}
91
92impl Client {
93    pub fn config(&mut self, x: Arg) -> Result<Vec<Config>, HglibError> {
94        let (data, _) = x.run(self)?;
95        let mut conf = Vec::new();
96        if x.showsource {
97            let mut iter = data.split(|x| *x == b'\n').filter(|x| !x.is_empty());
98            let mut line = iter.next();
99            while let Some(l) = line {
100                if !l.starts_with(b"read config from: ") && !l.starts_with(b"set config by: ") {
101                    break;
102                }
103                line = iter.next();
104            }
105            while let Some(l) = line {
106                if let Some((source, kv)) = get_skv(l) {
107                    let source = std::str::from_utf8(source)?;
108                    conf.push(Config::with_source(source, kv)?);
109                } else {
110                    return Err(HglibError::from(format!(
111                        "invalid line in config: {}",
112                        std::str::from_utf8(l)?
113                    )));
114                }
115                line = iter.next();
116            }
117        } else {
118            for line in data.split(|x| *x == b'\n').filter(|x| !x.is_empty()) {
119                conf.push(Config::no_source(line)?);
120            }
121        }
122        Ok(conf)
123    }
124}