|
| 1 | +use std::{ |
| 2 | + collections::{BTreeMap, BTreeSet, HashMap}, |
| 3 | + path::PathBuf, |
| 4 | +}; |
| 5 | + |
| 6 | +use serde; |
| 7 | + |
| 8 | +pub type PinCollection = BTreeSet<String>; |
| 9 | + |
| 10 | +#[derive(serde::Deserialize, Debug)] |
| 11 | +#[serde(deny_unknown_fields)] |
| 12 | +#[serde(rename_all = "kebab-case")] |
| 13 | +struct Devices { |
| 14 | + pin_groups: HashMap<String, PinCollection>, |
| 15 | + families: HashMap<String, Family>, |
| 16 | +} |
| 17 | + |
| 18 | +#[derive(serde::Deserialize, Debug)] |
| 19 | +#[serde(deny_unknown_fields)] |
| 20 | +struct Family { |
| 21 | + pins: HashMap<String, PinCollection>, |
| 22 | + peripherals: Vec<Peripheral>, |
| 23 | +} |
| 24 | + |
| 25 | +#[derive(Debug)] |
| 26 | +struct Peripheral { |
| 27 | + name: String, |
| 28 | + variant: Option<String>, |
| 29 | + count: Option<u32>, |
| 30 | + only: Option<Vec<String>>, |
| 31 | +} |
| 32 | + |
| 33 | +impl<'de> serde::Deserialize<'de> for Peripheral { |
| 34 | + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |
| 35 | + where |
| 36 | + D: serde::Deserializer<'de>, |
| 37 | + { |
| 38 | + #[derive(serde::Deserialize, Debug)] |
| 39 | + #[serde(deny_unknown_fields)] |
| 40 | + struct PeripheralBody { |
| 41 | + #[serde(default)] |
| 42 | + variant: Option<String>, |
| 43 | + #[serde(default)] |
| 44 | + only: Option<Vec<String>>, |
| 45 | + #[serde(default)] |
| 46 | + count: Option<u32>, |
| 47 | + } |
| 48 | + |
| 49 | + struct Visitor; |
| 50 | + |
| 51 | + impl<'de> serde::de::Visitor<'de> for Visitor { |
| 52 | + type Value = Peripheral; |
| 53 | + |
| 54 | + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { |
| 55 | + write!(formatter, "a string or a peripheral mapping") |
| 56 | + } |
| 57 | + |
| 58 | + fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> |
| 59 | + where |
| 60 | + E: serde::de::Error, |
| 61 | + { |
| 62 | + Ok(Peripheral { |
| 63 | + name: v.to_string(), |
| 64 | + variant: None, |
| 65 | + count: None, |
| 66 | + only: None, |
| 67 | + }) |
| 68 | + } |
| 69 | + |
| 70 | + fn visit_string<E>(self, v: String) -> Result<Self::Value, E> |
| 71 | + where |
| 72 | + E: serde::de::Error, |
| 73 | + { |
| 74 | + Ok(Peripheral { |
| 75 | + name: v, |
| 76 | + variant: None, |
| 77 | + count: None, |
| 78 | + only: None, |
| 79 | + }) |
| 80 | + } |
| 81 | + |
| 82 | + fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> |
| 83 | + where |
| 84 | + A: serde::de::MapAccess<'de>, |
| 85 | + { |
| 86 | + if let Some((name, body)) = map.next_entry::<String, PeripheralBody>()? { |
| 87 | + if map |
| 88 | + .next_entry::<serde_yaml::Value, serde_yaml::Value>()? |
| 89 | + .is_none() |
| 90 | + { |
| 91 | + Ok(Peripheral { |
| 92 | + name, |
| 93 | + variant: body.variant, |
| 94 | + only: body.only, |
| 95 | + count: body.count, |
| 96 | + }) |
| 97 | + } else { |
| 98 | + Err(serde::de::Error::invalid_length( |
| 99 | + 2, |
| 100 | + &"expected peripheral map to have a single element", |
| 101 | + )) |
| 102 | + } |
| 103 | + } else { |
| 104 | + Err(serde::de::Error::invalid_length( |
| 105 | + 0, |
| 106 | + &"expected peripheral map to have a single element", |
| 107 | + )) |
| 108 | + } |
| 109 | + } |
| 110 | + } |
| 111 | + |
| 112 | + deserializer.deserialize_any(Visitor) |
| 113 | + } |
| 114 | +} |
| 115 | + |
| 116 | +fn normalize_pins(pin_groups: &HashMap<String, PinCollection>, pins: &mut PinCollection) -> bool { |
| 117 | + let mut new = Vec::new(); |
| 118 | + let mut changed = false; |
| 119 | + loop { |
| 120 | + pins.retain(|pin| { |
| 121 | + if let Some(pins) = pin_groups.get(pin) { |
| 122 | + new.extend(pins.iter().cloned()); |
| 123 | + false |
| 124 | + } else { |
| 125 | + true |
| 126 | + } |
| 127 | + }); |
| 128 | + if new.is_empty() { |
| 129 | + break; |
| 130 | + } |
| 131 | + pins.extend(new.drain(..)); |
| 132 | + changed = true; |
| 133 | + } |
| 134 | + changed |
| 135 | +} |
| 136 | + |
| 137 | +fn main() -> std::io::Result<()> { |
| 138 | + println!("cargo:rerun-if-changed=devices.yaml"); |
| 139 | + println!("cargo:rerun-if-changed=build.rs"); |
| 140 | + let out_dir: PathBuf = std::env::var("OUT_DIR").unwrap().into(); |
| 141 | + let manifest_dir: PathBuf = std::env::var("CARGO_MANIFEST_DIR").unwrap().into(); |
| 142 | + |
| 143 | + let peripheral_mapping = load_peripheral_mapping(manifest_dir)?; |
| 144 | + let out = generate_output_file(peripheral_mapping); |
| 145 | + |
| 146 | + std::fs::write(out_dir.join("generated.rs"), out)?; |
| 147 | + |
| 148 | + Ok(()) |
| 149 | +} |
| 150 | + |
| 151 | +fn generate_output_file(peripheral_mapping: BTreeMap<String, BTreeSet<String>>) -> String { |
| 152 | + let mut out = |
| 153 | + "fn match_peripheral(s: &str) -> Option<&'static [&'static str]> { match s {\n".to_string(); |
| 154 | + |
| 155 | + for (peripheral, devices) in peripheral_mapping { |
| 156 | + use std::fmt::Write; |
| 157 | + write!(out, "{peripheral:?} => Some(&[").unwrap(); |
| 158 | + for device in devices { |
| 159 | + write!(out, "{device:?},").unwrap(); |
| 160 | + } |
| 161 | + out += "]),\n"; |
| 162 | + } |
| 163 | + out += "_ => None }}"; |
| 164 | + out |
| 165 | +} |
| 166 | + |
| 167 | +fn load_peripheral_mapping( |
| 168 | + manifest_dir: PathBuf, |
| 169 | +) -> Result<BTreeMap<String, BTreeSet<String>>, std::io::Error> { |
| 170 | + let mut devices: Devices = |
| 171 | + serde_yaml::from_reader(std::fs::File::open(manifest_dir.join("devices.yaml"))?).unwrap(); |
| 172 | + loop { |
| 173 | + let mut changed = false; |
| 174 | + for key in devices |
| 175 | + .pin_groups |
| 176 | + .keys() |
| 177 | + .cloned() |
| 178 | + .collect::<Vec<String>>() |
| 179 | + .into_iter() |
| 180 | + { |
| 181 | + let mut cur = std::mem::take(devices.pin_groups.get_mut(&key).unwrap()); |
| 182 | + changed |= normalize_pins(&devices.pin_groups, &mut cur); |
| 183 | + devices.pin_groups.insert(key, cur); |
| 184 | + } |
| 185 | + if !changed { |
| 186 | + break; |
| 187 | + } |
| 188 | + } |
| 189 | + let mut peripheral_mapping: BTreeMap<String, BTreeSet<String>> = BTreeMap::new(); |
| 190 | + let mut peripheral_sub_mapping: BTreeMap<String, BTreeSet<String>> = BTreeMap::new(); |
| 191 | + for (family_name, mut family) in devices.families { |
| 192 | + for (device_name, pins) in family.pins.iter_mut() { |
| 193 | + normalize_pins(&devices.pin_groups, pins); |
| 194 | + for pin in pins.iter() { |
| 195 | + peripheral_mapping |
| 196 | + .entry(pin.clone()) |
| 197 | + .or_default() |
| 198 | + .insert(device_name.clone()); |
| 199 | + } |
| 200 | + } |
| 201 | + for peripheral in family.peripherals { |
| 202 | + let variant = peripheral.variant.unwrap_or_else(|| family_name.clone()); |
| 203 | + for i in 0..peripheral.count.unwrap_or(1) { |
| 204 | + let name = if peripheral.count.is_some() { |
| 205 | + format!("{}{i}", peripheral.name) |
| 206 | + } else { |
| 207 | + peripheral.name.clone() |
| 208 | + }; |
| 209 | + let combined_name = format!("{}-{}", name, variant); |
| 210 | + let devices = family |
| 211 | + .pins |
| 212 | + .keys() |
| 213 | + .filter(|key| { |
| 214 | + peripheral |
| 215 | + .only |
| 216 | + .as_ref() |
| 217 | + .map_or(true, |only| only.contains(key)) |
| 218 | + }) |
| 219 | + .cloned(); |
| 220 | + peripheral_mapping |
| 221 | + .entry(name) |
| 222 | + .or_default() |
| 223 | + .extend(devices.clone()); |
| 224 | + peripheral_sub_mapping |
| 225 | + .entry(combined_name) |
| 226 | + .or_default() |
| 227 | + .extend(devices); |
| 228 | + } |
| 229 | + } |
| 230 | + } |
| 231 | + |
| 232 | + for (sub_peripheral_key, sub_peripheral) in peripheral_sub_mapping { |
| 233 | + assert!(peripheral_mapping |
| 234 | + .insert(sub_peripheral_key, sub_peripheral) |
| 235 | + .is_none()); |
| 236 | + } |
| 237 | + |
| 238 | + Ok(peripheral_mapping) |
| 239 | +} |
0 commit comments