1pub mod codegen;
11pub mod config;
12pub mod model;
13pub mod solver;
14
15use nucleus_db::Database;
16
17pub use codegen::{generate, Generated};
18pub use config::{Config, ParseError};
19pub use solver::Conflict;
20
21#[derive(Debug, Clone)]
23pub struct CheckReport {
24 pub config: Config,
26 pub conflicts: Vec<Conflict>,
29}
30
31impl CheckReport {
32 pub fn is_ok(&self) -> bool {
34 self.conflicts.is_empty()
35 }
36}
37
38pub fn database_for(family: &str) -> Result<Database, UnknownFamily> {
42 match family {
43 "STM32F446RE" | "" => Ok(Database::f446re()),
44 "STM32F411RE" => Ok(Database::f411re()),
45 other => Err(UnknownFamily(other.to_string())),
46 }
47}
48
49#[derive(Debug, Clone, PartialEq, Eq)]
51pub struct UnknownFamily(pub String);
52
53impl std::fmt::Display for UnknownFamily {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 write!(
56 f,
57 "unsupported device family {:?}: Nucleus supports STM32F446RE and STM32F411RE",
58 self.0
59 )
60 }
61}
62
63impl std::error::Error for UnknownFamily {}
64
65pub fn check(text: &str) -> Result<CheckReport, ParseError> {
71 let config = config::parse(text)?;
72 let db = database_for(&config.device.family).unwrap_or_else(|_| Database::f446re());
75 let conflicts = solver::solve(&config, &db);
76 Ok(CheckReport { config, conflicts })
77}
78
79pub fn check_family(text: &str) -> Result<(CheckReport, Option<UnknownFamily>), ParseError> {
81 let config = config::parse(text)?;
82 let family_warning = database_for(&config.device.family).err();
83 let db = database_for(&config.device.family).unwrap_or_else(|_| Database::f446re());
84 let conflicts = solver::solve(&config, &db);
85 Ok((CheckReport { config, conflicts }, family_warning))
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn check_reports_ok_for_clean_config() {
94 let report = check(
95 r#"
96[device]
97family = "STM32F446RE"
98
99[peripherals.usart2]
100tx = "PA2"
101rx = "PA3"
102"#,
103 )
104 .unwrap();
105 assert!(report.is_ok());
106 }
107
108 #[test]
109 fn check_surfaces_conflicts() {
110 let report = check(
111 r#"
112[peripherals.spi1]
113mosi = "PA7"
114miso = "PA6"
115sck = "PA5"
116
117[peripherals.tim2]
118channel1 = "PA5"
119"#,
120 )
121 .unwrap();
122 assert!(!report.is_ok());
123 }
124
125 #[test]
126 fn unknown_family_is_flagged() {
127 let (_report, warning) = check_family(
128 r#"
129[device]
130family = "STM32H750"
131"#,
132 )
133 .unwrap();
134 assert_eq!(warning, Some(UnknownFamily("STM32H750".to_string())));
135 }
136
137 #[test]
138 fn malformed_toml_is_a_parse_error() {
139 assert!(check("this is not toml = = =").is_err());
140 }
141
142 #[test]
143 fn database_for_resolves_known_families() {
144 assert!(database_for("STM32F446RE").is_ok());
145 assert!(database_for("STM32F411RE").is_ok());
146 assert!(database_for("").is_ok()); assert!(database_for("STM32H750").is_err());
148 }
149
150 #[test]
151 fn check_family_resolves_db_for_f411re() {
152 let (report, warning) = check_family(
156 "[device]\nfamily = \"STM32F411RE\"\n\n[peripherals.uart4]\ntx = \"PA0\"\nrx = \"PA1\"\n",
157 )
158 .unwrap();
159 assert_eq!(warning, None);
160 assert!(
161 report.conflicts.iter().any(|c| matches!(
162 c,
163 Conflict::PeripheralUnavailable { peripheral, family }
164 if peripheral == "UART4" && family == "STM32F411RE"
165 )),
166 "got {:?}",
167 report.conflicts
168 );
169 }
170}