1use anyhow::{Context, Result};
2use linux_embedded_hal::serial_core;
3use serde_derive::{Deserialize, Serialize};
4use std::{
5 fs::read_to_string,
6 path::{Path, PathBuf},
7};
8
9pub fn load(config_path: impl AsRef<Path>) -> Result<Config> {
15 let path = read_to_string(&config_path).with_context(|| {
16 format!(
17 "Failed to open config file {}",
18 config_path.as_ref().display()
19 )
20 })?;
21 toml::from_str(&path).map_err(|e| {
22 eprintln!(
23 "Failed to parse configuration file. Here's an example:\n{}",
24 toml::to_string(&Config::example()).unwrap()
25 );
26 anyhow::anyhow!(e)
27 })
28}
29#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
31pub struct Config {
32 pub(crate) serial_path: PathBuf,
33 pub(crate) baudrate: u32,
34 pub(crate) parity: Parity,
35 pub(crate) data_bits: u8,
36 pub(crate) stop_bits: u8,
37 pub(crate) gpiochip_path: PathBuf,
38 pub(crate) aux_pin: u32,
39 pub(crate) m0_pin: u32,
40 pub(crate) m1_pin: u32,
41}
42
43impl Config {
44 #[must_use]
46 pub fn example() -> Self {
47 Self {
48 serial_path: "dev/ttyAMA0".into(),
49 baudrate: 9600,
50 parity: Parity::None,
51 data_bits: 8,
52 stop_bits: 1,
53 gpiochip_path: "/dev/gpiochip0".into(),
54 aux_pin: 18,
55 m0_pin: 23,
56 m1_pin: 24,
57 }
58 }
59}
60
61#[derive(Debug, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
64pub(crate) enum Parity {
65 None,
67 Even,
69 Odd,
71}
72
73impl From<Parity> for serial_core::Parity {
74 fn from(value: Parity) -> Self {
75 match value {
76 Parity::None => Self::ParityNone,
77 Parity::Even => Self::ParityEven,
78 Parity::Odd => Self::ParityOdd,
79 }
80 }
81}
82
83#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
86pub(crate) enum StopBits {
87 Stop1,
89
90 Stop2,
92}
93
94impl TryFrom<u8> for StopBits {
95 type Error = anyhow::Error;
96
97 fn try_from(value: u8) -> Result<Self, Self::Error> {
98 match value {
99 1 => Ok(Self::Stop1),
100 2 => Ok(Self::Stop2),
101 n => anyhow::bail!(
102 "'{n}' is not a valid value for stop bits. Valid values are '1' and '2'."
103 ),
104 }
105 }
106}
107
108impl From<StopBits> for serial_core::StopBits {
109 fn from(value: StopBits) -> Self {
110 match value {
111 StopBits::Stop1 => Self::Stop1,
112 StopBits::Stop2 => Self::Stop2,
113 }
114 }
115}
116
117#[cfg(test)]
118mod test {
119 use super::*;
120
121 #[test]
122 fn parses_example_config() {
123 let config = std::fs::read_to_string("Config.toml").unwrap();
124 assert!(toml::from_str::<Config>(&config).is_ok());
125 }
126
127 #[test]
128 fn show_config() {
129 let config = Config {
130 serial_path: "/dev/ttyAMA0".into(),
131 parity: Parity::None,
132 baudrate: 115200,
133 data_bits: 8,
134 stop_bits: 1,
135 gpiochip_path: "/dev/gpiochip0".into(),
136 aux_pin: 0,
137 m0_pin: 1,
138 m1_pin: 2,
139 };
140 println!("{}", toml::to_string(&config).unwrap());
141 }
142}