1use 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}