Cactus/config/
read_properties.rs1use std::collections::HashMap;
2use std::error::Error;
3use std::io::BufRead;
4use std::{fmt, io};
5
6#[derive(Debug)]
7pub struct PropertyNotFoundError<'a>(&'a str);
8
9impl<'a> Error for PropertyNotFoundError<'a> {}
10
11impl<'a> fmt::Display for PropertyNotFoundError<'a> {
12 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
13 writeln!(f, "Property {:?} not found", self.0)
14 }
15}
16
17#[derive(Debug, Clone, PartialEq, Eq, Default)]
18pub struct Properties(HashMap<String, String>);
19
20impl From<HashMap<String, String>> for Properties {
21 fn from(value: HashMap<String, String>) -> Self {
22 Self(value)
23 }
24}
25
26impl Properties {
27 pub fn get_property<'a>(&self, key: &'a str) -> Result<&'_ str, PropertyNotFoundError<'a>> {
29 self.0
30 .get(key)
31 .map(String::as_ref)
32 .ok_or(PropertyNotFoundError(key))
33 }
34}
35
36#[derive(Debug)]
37pub struct PropertiesParseError {
38 pub line_number: usize,
39 pub kind: PropertiesParseErrorKind,
40}
41
42impl PropertiesParseError {
43 fn new_io(line_number: usize, error: io::Error) -> Self {
44 Self {
45 line_number,
46 kind: PropertiesParseErrorKind::Io(error),
47 }
48 }
49
50 fn new_invalid_kvp(line_number: usize, line: &str) -> Self {
51 Self {
52 line_number,
53 kind: PropertiesParseErrorKind::InvalidKeyValuePair(InvalidKeyValuePairError(
54 line.to_string(),
55 )),
56 }
57 }
58}
59
60#[derive(Debug)]
61pub struct InvalidKeyValuePairError(String);
62
63impl Error for InvalidKeyValuePairError {}
64
65impl fmt::Display for InvalidKeyValuePairError {
66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 write!(f, "cannot parse a key-value pair from {:?}", self.0)
68 }
69}
70
71#[derive(Debug)]
72pub enum PropertiesParseErrorKind {
73 Io(io::Error),
74 InvalidKeyValuePair(InvalidKeyValuePairError),
75}
76
77impl Error for PropertiesParseError {
78 fn source(&self) -> Option<&(dyn Error + 'static)> {
79 match &self.kind {
80 PropertiesParseErrorKind::Io(e) => Some(e),
81 PropertiesParseErrorKind::InvalidKeyValuePair(e) => Some(e),
82 }
83 }
84}
85
86impl fmt::Display for PropertiesParseError {
87 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88 write!(
89 f,
90 "Error while parsing properties file (line {})",
91 self.line_number
92 )
93 }
94}
95
96pub fn read_properties<R: BufRead>(reader: &mut R) -> Result<Properties, PropertiesParseError> {
105 let mut properties = Properties::default();
106
107 for (i, line) in reader.lines().enumerate() {
108 let line_number = i + 1;
109
110 let line = line.map_err(|e| PropertiesParseError::new_io(line_number, e))?;
111
112 let line = line.trim(); if line.is_empty() || line.starts_with("#") || line.starts_with("!") {
116 continue;
117 }
118
119 let (field, value) = line
120 .split_once('=')
121 .ok_or_else(|| PropertiesParseError::new_invalid_kvp(line_number, line))?;
122
123 let key = field.trim().to_string();
124 let value = value.trim().to_string();
125 properties.0.insert(key, value);
126 }
127
128 Ok(properties)
129}