stm32builder/types/
device_id.rs

1//! Device Identifiant
2
3use crate::{
4    api::Error,
5    types::{FlashSize, Package, TemperatureRange},
6};
7use serde::ser::{Serialize, Serializer};
8
9/// The device identifiant number.
10///
11/// ```
12/// use stm32builder::types::{DeviceId, Package};
13/// let id = DeviceId::from_str("stm32f051R8T6").unwrap();
14///
15/// assert_eq!(id.id, "stm32f051R8T6");
16/// assert_eq!(id.name(), "stm32f051");
17/// assert_eq!(id.part(), "R8");
18/// assert_eq!(id.package, Package::LQFP64);
19/// ```
20#[derive(Debug, Clone, PartialEq)]
21pub struct DeviceId {
22    pub id: String,
23    pub package: Package,
24    pub flash_size: FlashSize,
25    pub temperature: TemperatureRange,
26}
27
28impl Serialize for DeviceId {
29    /// Serialize DeviceId as a string.
30    fn serialize<S>(&self, serialize: S) -> Result<S::Ok, S::Error>
31    where
32        S: Serializer,
33    {
34        serialize.serialize_str(&self.id)
35    }
36}
37
38// NOTE: We need the `$function(id)` for our own use and `$method(&self)` for use by the user.
39// We use `get()` on function to prevent panicking not present characters.
40// We `unwrap()` on methods as has already validate the id, so we know it can't fail.
41macro_rules! decode {
42    ( $method:ident (on $pos:tt position) as $function:ident ) =>  {
43        decode!($method (from $pos to $pos characters) as $function);
44    };
45    ( $method:ident (from $start:tt to $end:tt characters) as  $function:ident ) => {
46        pub fn $method(&self) -> &str {
47            Self::$function(&self.id).unwrap()
48        }
49        fn $function(id: &str) -> Option<&str> {
50            id.get($start..=$end)
51        }
52    };
53}
54
55impl DeviceId {
56    const BRAND: &'static str = "stm32";
57
58    // NOTE: Remenber indice starts at zero.
59    decode!( header (from 0 to 4 characters) as header_from_str );
60    decode!( family (on 5 position) as family_from_str );
61    decode!( sub_family (from 6 to 8 characters) as sub_family_from_str );
62    decode!( name (from 0 to 8 characters) as name_from_str );
63    decode!( part (from 9 to 10 characters) as part_from_str );
64    decode!( pin_count (on 9 position) as pin_count_from_str );
65    decode!( flash_size (on 10 position) as flash_size_from_str );
66    decode!( package_type (on 11 position) as package_type_from_str );
67    decode!( temperature_range (on 12 position) as temperature_range_from_str );
68
69    /// Decode the device idendification number.
70    ///
71    /// ## Returns
72    ///
73    /// One of `NoBrand`, `BadBrand`, `NoFamily`, `BadFamily`, `NoSubFamily`, `BadSubFamily`,
74    /// `NoPinCount`, `BadPinCount`, `UnknownPinCount`, `NoFlashSize`, `BadFlashSize`,
75    /// `UnknownFlashSize`, `NoPackageType`, `BadPackageType`, `UnknownPackageType`,
76    /// `NoTemperatureRange`, `BadTemperatureRange`, `UnknownTemperatureRange` if case of error.
77    ///
78    pub fn from_str(id: &str) -> Result<Self, Error> {
79        let is_ascii_digit = |c: u8| c.is_ascii_digit();
80        let is_ascii_alphabetic = |c: u8| c.is_ascii_alphabetic();
81
82        if Self::header_from_str(id).ok_or(Error::NoBrand)? != Self::BRAND {
83            return Err(Error::BadBrand);
84        }
85        if !Self::family_from_str(id)
86            .ok_or(Error::NoFamily)?
87            .bytes()
88            .all(is_ascii_alphabetic)
89        {
90            return Err(Error::BadFamily);
91        }
92        if !Self::sub_family_from_str(id)
93            .ok_or(Error::NoSubFamily)?
94            .bytes()
95            .all(is_ascii_digit)
96        {
97            return Err(Error::BadSubFamily);
98        }
99        let pin_count = Self::pin_count_from_str(id).ok_or(Error::NoPinCount)?;
100        if !pin_count.bytes().all(is_ascii_alphabetic) {
101            return Err(Error::BadPinCount);
102        }
103        let pin_count = pin_count.to_ascii_uppercase();
104        let flash_size = Self::flash_size_from_str(id).ok_or(Error::NoFlashSize)?;
105        if !flash_size.bytes().all(is_ascii_digit) {
106            return Err(Error::BadFlashSize);
107        }
108        let package_type = Self::package_type_from_str(id).ok_or(Error::NoPackageType)?;
109        if !package_type.bytes().all(is_ascii_alphabetic) {
110            return Err(Error::BadPackageType);
111        }
112        let package_type = package_type.to_ascii_uppercase();
113        let temperature_range =
114            Self::temperature_range_from_str(id).ok_or(Error::NoTemperatureRange)?;
115        if !temperature_range.bytes().all(is_ascii_digit) {
116            return Err(Error::BadTemperatureRange);
117        }
118
119        let mut new_id = String::new();
120        new_id.push_str(Self::header_from_str(id).unwrap());
121        new_id.push_str(Self::family_from_str(id).unwrap());
122        new_id.push_str(Self::sub_family_from_str(id).unwrap());
123        new_id.push_str(&pin_count);
124        new_id.push_str(&flash_size);
125        new_id.push_str(&package_type);
126        new_id.push_str(&temperature_range);
127        Ok(DeviceId {
128            id: new_id,
129            package: Package::from_pin_and_package_str(&pin_count, &package_type)?,
130            flash_size: FlashSize::from_flash_size_str(flash_size)?,
131            temperature: TemperatureRange::from_temperature_range_str(temperature_range)?,
132        })
133    }
134
135    pub fn datasheet_url(&self) -> String {
136        format!(
137            "https://www.st.com/resource/en/datasheet/{}{}.pdf",
138            self.name(),
139            self.part()
140        )
141    }
142}
143
144#[cfg(test)]
145mod tests {
146    use super::*;
147
148    #[test]
149    #[allow(non_snake_case)]
150    fn decoding_stm32f051R8T6() {
151        let id = DeviceId::from_str("stm32f051R8T6").unwrap();
152
153        assert_eq!(id.header(), "stm32");
154        assert_eq!(id.name(), "stm32f051");
155        assert_eq!(id.family(), "f");
156        assert_eq!(id.sub_family(), "051");
157        assert_eq!(id.part(), "R8");
158        assert_eq!(id.pin_count(), "R");
159        assert_eq!(id.flash_size(), "8");
160        assert_eq!(id.package_type(), "T");
161        assert_eq!(id.temperature_range(), "6");
162        assert_eq!(id.package, Package::LQFP64);
163        assert_eq!(id.flash_size, FlashSize(64));
164        assert_eq!(id.temperature.min, -40);
165        assert_eq!(id.temperature.max, 85);
166    }
167
168    // NOTE: The match against error is a hack around the fact that Error must implement PartialEq
169    // for assert to work on but as we want to embed serde_yaml errors on it and this one doesn't
170    // implement PartialEq, we can't write `assert_eq!(..., Error::$error);` assertion.
171    macro_rules! decoding_fail {
172        ( $str:tt $error:ident $name:ident) => {
173            #[test]
174            pub fn $name() {
175                match DeviceId::from_str($str).err().unwrap() {
176                    Error::$error => {}
177                    _ => assert!(false, "expected {} error", stringify!($error)),
178                }
179            }
180        };
181        ( $str:tt $error:ident($with:tt) $name:ident ) => {
182            #[test]
183            pub fn $name() {
184                match DeviceId::from_str($str).err().unwrap() {
185                    Error::$error(value) => assert_eq!(value, $with.to_string()),
186                    _ => assert!(false, "expected {} error", stringify!($error)),
187                }
188            }
189        };
190    }
191    decoding_fail!( ""              NoBrand                        error_on_empty_str );
192    decoding_fail!( "stm"           NoBrand                        error_on_incomplet_brand ); // Should be BadBrand
193    decoding_fail!( "st_32f051R8T6" BadBrand                       error_on_invalid_brand );
194    decoding_fail!( "stm32"         NoFamily                       error_without_family );
195    decoding_fail!( "stm32_051R8T6" BadFamily                      error_on_digit_family );
196    decoding_fail!( "stm32f"        NoSubFamily                    error_without_sub_family );
197    decoding_fail!( "stm32f_51R8T6" BadSubFamily                   error_on_alphabetical_sub_family );
198    decoding_fail!( "stm32f051"     NoPinCount                     error_without_pin_count );
199    decoding_fail!( "stm32f05108T6" BadPinCount                    error_on_digit_pin_count );
200    decoding_fail!( "stm32f051O8T6" UnknownPinCount("O")           error_on_unknown_pin_count );
201    decoding_fail!( "stm32f051R"    NoFlashSize                    error_without_flash_size );
202    decoding_fail!( "stm32f051RAT6" BadFlashSize                   error_on_alphabetical_flash_size );
203    decoding_fail!( "stm32f051R0T6" UnknownFlashSize("0")          error_on_unknown_flash_size );
204    decoding_fail!( "stm32f051R8"   NoPackageType                  error_without_package_type );
205    decoding_fail!( "stm32f051R886" BadPackageType                 error_on_digit_package_type );
206    decoding_fail!( "stm32f051R8O6" UnknownPackageType("O")        error_on_unknown_package_type );
207    decoding_fail!( "stm32f051R8T"  NoTemperatureRange             error_without_temperature_range );
208    decoding_fail!( "stm32f051R8TT" BadTemperatureRange            error_on_alphabetic_temperature_range );
209    decoding_fail!( "stm32f051R8T0" UnknownTemperatureRange("0")   error_on_unknown_temperature_range );
210
211    #[test]
212    fn can_be_compared_against() {
213        assert_eq!(
214            DeviceId::from_str("stm32f051R8T6").unwrap(),
215            DeviceId::from_str("stm32f051R8T6").unwrap()
216        );
217    }
218}