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
38fn database_for(family: &str) -> Result<Database, UnknownFamily> {
42 match family {
43 "STM32F446RE" | "" => Ok(Database::f446re()),
44 other => Err(UnknownFamily(other.to_string())),
45 }
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
50pub struct UnknownFamily(pub String);
51
52impl std::fmt::Display for UnknownFamily {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 write!(
55 f,
56 "unsupported device family {:?}: Nucleus currently supports only STM32F446RE",
57 self.0
58 )
59 }
60}
61
62impl std::error::Error for UnknownFamily {}
63
64pub fn check(text: &str) -> Result<CheckReport, ParseError> {
70 let config = config::parse(text)?;
71 let db = database_for(&config.device.family).unwrap_or_else(|_| Database::f446re());
74 let conflicts = solver::solve(&config, &db);
75 Ok(CheckReport { config, conflicts })
76}
77
78pub fn check_family(text: &str) -> Result<(CheckReport, Option<UnknownFamily>), ParseError> {
80 let config = config::parse(text)?;
81 let family_warning = database_for(&config.device.family).err();
82 let db = Database::f446re();
83 let conflicts = solver::solve(&config, &db);
84 Ok((CheckReport { config, conflicts }, family_warning))
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[test]
92 fn check_reports_ok_for_clean_config() {
93 let report = check(
94 r#"
95[device]
96family = "STM32F446RE"
97
98[peripherals.usart2]
99tx = "PA2"
100rx = "PA3"
101"#,
102 )
103 .unwrap();
104 assert!(report.is_ok());
105 }
106
107 #[test]
108 fn check_surfaces_conflicts() {
109 let report = check(
110 r#"
111[peripherals.spi1]
112mosi = "PA7"
113miso = "PA6"
114sck = "PA5"
115
116[peripherals.tim2]
117channel1 = "PA5"
118"#,
119 )
120 .unwrap();
121 assert!(!report.is_ok());
122 }
123
124 #[test]
125 fn unknown_family_is_flagged() {
126 let (_report, warning) = check_family(
127 r#"
128[device]
129family = "STM32H750"
130"#,
131 )
132 .unwrap();
133 assert_eq!(warning, Some(UnknownFamily("STM32H750".to_string())));
134 }
135
136 #[test]
137 fn malformed_toml_is_a_parse_error() {
138 assert!(check("this is not toml = = =").is_err());
139 }
140}