Skip to main content

device_driver_common/
identifier.rs

1use std::{
2    fmt::{Debug, Display},
3    num::NonZeroU32,
4    sync::Arc,
5};
6
7use convert_case::{Boundary, Case, Pattern};
8
9#[repr(C)]
10#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
11pub enum RuntimeType {
12    /// Used for things that participate in all types.
13    /// These are manifests, blocks, fields, enum variants, ...
14    All,
15    /// Used for things that define operations or things you can do with a driver.
16    /// These are registers, commands, buffers, ...
17    Operation,
18    /// Used for things that define a type.
19    /// These are devices, fieldsets, enums, ...
20    Type,
21}
22
23impl RuntimeType {
24    pub fn shares_namespace_with(&self, other: RuntimeType) -> bool {
25        matches!(
26            (self, other),
27            (RuntimeType::All, _)
28                | (_, RuntimeType::All)
29                | (RuntimeType::Operation, RuntimeType::Operation)
30                | (RuntimeType::Type, RuntimeType::Type)
31        )
32    }
33}
34
35#[repr(transparent)]
36#[derive(Debug, Copy, Clone)]
37pub struct Type(RuntimeType);
38impl Default for Type {
39    fn default() -> Self {
40        Self(RuntimeType::Type)
41    }
42}
43#[repr(transparent)]
44#[derive(Debug, Copy, Clone)]
45pub struct Operation(RuntimeType);
46impl Default for Operation {
47    fn default() -> Self {
48        Self(RuntimeType::Operation)
49    }
50}
51#[repr(transparent)]
52#[derive(Debug, Copy, Clone)]
53pub struct All(RuntimeType);
54impl Default for All {
55    fn default() -> Self {
56        Self(RuntimeType::All)
57    }
58}
59
60/// # Safety
61/// Must only be implemented on type that are transparently [RuntimeType]
62pub unsafe trait IdentifierType: Debug {
63    fn runtime_value(&self) -> RuntimeType;
64}
65
66unsafe impl IdentifierType for Type {
67    fn runtime_value(&self) -> RuntimeType {
68        RuntimeType::Type
69    }
70}
71unsafe impl IdentifierType for Operation {
72    fn runtime_value(&self) -> RuntimeType {
73        RuntimeType::Operation
74    }
75}
76unsafe impl IdentifierType for All {
77    fn runtime_value(&self) -> RuntimeType {
78        RuntimeType::All
79    }
80}
81unsafe impl IdentifierType for RuntimeType {
82    fn runtime_value(&self) -> RuntimeType {
83        *self
84    }
85}
86
87impl From<All> for Type {
88    fn from(_: All) -> Self {
89        Type::default()
90    }
91}
92impl From<All> for Operation {
93    fn from(_: All) -> Self {
94        Operation::default()
95    }
96}
97
98/// A structure that holds the name data of objects
99#[derive(Debug, Clone)]
100#[repr(C)]
101pub struct Identifier<T: IdentifierType> {
102    boundaries_applied: bool,
103    /// The original string that was parsed without concats
104    original: Arc<String>,
105    words: Arc<[String]>,
106    duplicate_id: Option<NonZeroU32>,
107    /// Must never change!
108    id_type: T,
109}
110
111impl<T: IdentifierType> Identifier<T> {
112    /// Try parse a string as an identifier.
113    /// It will not have boundaries applied yet.
114    pub fn try_parse(value: &str) -> Result<Self, Error>
115    where
116        T: Default,
117    {
118        Self::try_parse_with_type(value, T::default())
119    }
120
121    /// Try parse a string as an identifier.
122    /// It will not have boundaries applied yet.
123    pub fn try_parse_with_type(value: &str, id_type: T) -> Result<Self, Error> {
124        if value.is_empty() {
125            return Err(Error::Empty);
126        }
127
128        Ok(Self {
129            boundaries_applied: false,
130            original: Arc::new(value.into()),
131            words: [value.into()].into(),
132            duplicate_id: None,
133            id_type,
134        })
135    }
136
137    /// Apply the boundaries. This can only be called once and must be called before [`Self::to_case`]
138    pub fn apply_boundaries(&mut self, boundaries: &[Boundary]) -> &mut Self {
139        assert!(!self.boundaries_applied);
140
141        let mut words = Vec::new();
142
143        for word in self.words.iter() {
144            let mut local_words = convert_case::split(word, boundaries);
145            local_words.retain(|word| !word.is_empty());
146            words.append(&mut local_words);
147        }
148
149        let words = Pattern::Lowercase.mutate(&words);
150
151        self.boundaries_applied = true;
152        self.words = words.into_iter().collect();
153        self
154    }
155
156    pub fn check_validity(&self) -> Result<(), Error> {
157        assert!(self.boundaries_applied);
158
159        for (word_index, word) in self.words.iter().enumerate() {
160            for (char_offset, char) in word.char_indices() {
161                let tfn = match (word_index, char_offset) {
162                    (0, 0) => |c| unicode_ident::is_xid_start(c),
163                    _ => |c| unicode_ident::is_xid_continue(c),
164                };
165
166                if !tfn(char) {
167                    let offset = self
168                        .original()
169                        .to_lowercase()
170                        .find(word)
171                        .map(|word_offset| word_offset + char_offset)
172                        .expect("Word should be present in identifier words");
173                    return Err(Error::InvalidCharacter {
174                        byte_offset: offset,
175                        invalid_char: char,
176                    });
177                }
178            }
179        }
180
181        if self.words.iter().all(String::is_empty) {
182            return Err(Error::EmptyAfterSplits);
183        }
184
185        let converted = self.to_case(Case::Pascal);
186        if converted.contains(['-', '_', ' ']) {
187            return Err(Error::CannotConvert {
188                case_name: "Pascal",
189                example: converted,
190            });
191        }
192
193        Ok(())
194    }
195
196    /// Convert the identifier to a string in the given case
197    pub fn to_case(&self, case: Case) -> String {
198        assert!(
199            self.boundaries_applied,
200            "Boundaries not applied for `{}`",
201            self.original()
202        );
203
204        let mut words = self.words.to_vec();
205
206        if let Some(dup_id) = self.duplicate_id {
207            words.push("dup".to_string());
208            words.push(format!("{dup_id:X}"));
209        }
210
211        let words = case.mutate(&words.iter().map(String::as_str).collect::<Vec<_>>());
212        case.join(&words)
213    }
214
215    /// Get the original text. Don't use this unless it's important to get the *exact* original value.
216    /// Better to use [`Self::to_case`] in most circumstances.
217    pub fn original(&self) -> &str {
218        &self.original
219    }
220
221    /// Get a display string that separates the words that make up the identifier visually
222    pub fn words_display(&self) -> String {
223        self.words.join("ยท")
224    }
225
226    /// Same as [Self::words_display], but prepends another word
227    pub fn words_display_prepended(&self, word: String) -> String {
228        let mut words = self.words.to_vec();
229        words.insert(0, word);
230        words.join("ยท")
231    }
232
233    pub fn is_empty(&self) -> bool {
234        self.words.iter().all(String::is_empty)
235    }
236
237    /// Get a type ref if this is a type identifier
238    pub fn take_ref(&self) -> IdentifierRef<T>
239    where
240        T: Clone,
241    {
242        IdentifierRef {
243            original: self.original.clone(),
244            id_type: self.id_type.clone(),
245        }
246    }
247
248    pub fn set_duplicate_id(&mut self, val: NonZeroU32) {
249        self.duplicate_id = Some(val);
250    }
251
252    pub fn duplicate_id(&self) -> Option<NonZeroU32> {
253        self.duplicate_id
254    }
255
256    pub fn to_runtime_type(self) -> Identifier<RuntimeType> {
257        Identifier {
258            boundaries_applied: self.boundaries_applied,
259            original: self.original,
260            words: self.words,
261            duplicate_id: self.duplicate_id,
262            id_type: self.id_type.runtime_value(),
263        }
264    }
265
266    pub fn as_runtime_type_mut(&mut self) -> &mut Identifier<RuntimeType> {
267        assert_eq!(size_of::<T>(), size_of::<RuntimeType>());
268        // Safety: We're only casting the T to a RuntimeType which is explicitly allowed by all implementors of IdentifierType
269        // The Identifier itself is repr C and so won't be weird when the generic type changes
270        unsafe { std::mem::transmute::<&mut Self, &mut Identifier<RuntimeType>>(self) }
271    }
272
273    pub fn as_runtime_type(&self) -> &Identifier<RuntimeType> {
274        assert_eq!(size_of::<T>(), size_of::<RuntimeType>());
275        // Safety: We're only casting the T to a RuntimeType which is explicitly allowed by all implementors of IdentifierType
276        // The Identifier itself is repr C and so won't be weird when the generic type changes
277        unsafe { std::mem::transmute::<&Self, &Identifier<RuntimeType>>(self) }
278    }
279
280    /// Get the identifier type
281    pub fn id_type(&self) -> &T {
282        &self.id_type
283    }
284
285    /// Change the type of the identifier to a more specific type
286    pub fn cast<U>(self) -> Identifier<U>
287    where
288        U: IdentifierType + Default,
289        U: From<T>,
290    {
291        // Fine to do since we have the where bound
292        self.cast_unchecked()
293    }
294
295    /// Change the type of the identifier.
296    /// This is generally a bad idea because of the subtleties!
297    /// So make sure this is actually what you want.
298    pub fn cast_unchecked<U: IdentifierType + Default>(self) -> Identifier<U> {
299        Identifier {
300            boundaries_applied: self.boundaries_applied,
301            original: self.original,
302            words: self.words,
303            duplicate_id: self.duplicate_id,
304            id_type: U::default(),
305        }
306    }
307
308    /// Change the type of the identifier, but only if the runtime type is already that type.
309    /// This function will panic if they're different.
310    #[track_caller]
311    pub fn cast_assert<U: IdentifierType + Default>(self) -> Identifier<U> {
312        assert_eq!(self.id_type.runtime_value(), U::default().runtime_value());
313
314        Identifier {
315            boundaries_applied: self.boundaries_applied,
316            original: self.original,
317            words: self.words,
318            duplicate_id: self.duplicate_id,
319            id_type: U::default(),
320        }
321    }
322}
323
324impl<T: IdentifierType + Default> Default for Identifier<T> {
325    fn default() -> Self {
326        Self {
327            boundaries_applied: Default::default(),
328            original: Default::default(),
329            words: Default::default(),
330            duplicate_id: Default::default(),
331            id_type: T::default(),
332        }
333    }
334}
335
336impl<T: IdentifierType> std::hash::Hash for Identifier<T> {
337    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
338        self.original.hash(state);
339        self.duplicate_id.hash(state);
340        self.id_type.runtime_value().hash(state);
341    }
342}
343
344impl<T: IdentifierType> PartialEq for Identifier<T> {
345    fn eq(&self, other: &Self) -> bool {
346        (self.original == other.original || self.words == other.words)
347            && self.duplicate_id == other.duplicate_id
348            && self
349                .id_type
350                .runtime_value()
351                .shares_namespace_with(other.id_type.runtime_value())
352    }
353}
354impl<T: IdentifierType> Eq for Identifier<T> {}
355
356#[derive(Debug, Clone, Default)]
357pub struct IdentifierRef<T: IdentifierType> {
358    original: Arc<String>,
359    id_type: T,
360}
361
362impl<T: IdentifierType> IdentifierRef<T> {
363    pub fn new(identifier_original: String) -> Self
364    where
365        T: Default,
366    {
367        Self {
368            original: Arc::new(identifier_original),
369            id_type: T::default(),
370        }
371    }
372
373    pub fn original(&self) -> &str {
374        &self.original
375    }
376
377    pub fn is_ref_to<U: IdentifierType>(&self, identifier: &Identifier<U>) -> bool {
378        identifier.id_type.runtime_value() == self.id_type.runtime_value()
379            && self.original() == identifier.original()
380    }
381}
382
383impl<T: IdentifierType> std::hash::Hash for IdentifierRef<T> {
384    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
385        self.original.hash(state);
386        self.id_type.runtime_value().hash(state);
387    }
388}
389
390impl<T: IdentifierType> PartialEq for IdentifierRef<T> {
391    fn eq(&self, other: &Self) -> bool {
392        self.original == other.original
393            && self
394                .id_type
395                .runtime_value()
396                .shares_namespace_with(other.id_type.runtime_value())
397    }
398}
399impl<T: IdentifierType> Eq for IdentifierRef<T> {}
400
401#[derive(Debug, Clone, PartialEq, Eq)]
402pub enum Error {
403    Empty,
404    EmptyAfterSplits,
405    InvalidCharacter {
406        byte_offset: usize,
407        invalid_char: char,
408    },
409    CannotConvert {
410        case_name: &'static str,
411        example: String,
412    },
413}
414
415impl std::error::Error for Error {}
416impl Display for Error {
417    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
418        match self {
419            Error::Empty => write!(f, "identifier is empty"),
420            Error::EmptyAfterSplits => write!(f, "identifier is empty after word split"),
421            Error::InvalidCharacter {
422                byte_offset,
423                invalid_char,
424            } => {
425                write!(
426                    f,
427                    "identifier contains an invalid character at byte offset {byte_offset}: '{invalid_char:?}'"
428                )
429            }
430            Error::CannotConvert { case_name, example } => {
431                write!(
432                    f,
433                    "cannot change the casing of the identifier. Identifier is `{example}` when converted to {case_name} case, but that's not correct casing"
434                )
435            }
436        }
437    }
438}
439
440#[cfg(test)]
441mod tests {
442    use super::*;
443
444    #[test]
445    fn simple_cases() {
446        assert_eq!(Identifier::<All>::try_parse(""), Err(Error::Empty));
447        assert_eq!(
448            Identifier::<All>::try_parse("1")
449                .unwrap()
450                .apply_boundaries(&[Boundary::Underscore])
451                .check_validity(),
452            Err(Error::InvalidCharacter {
453                byte_offset: 0,
454                invalid_char: '1'
455            })
456        );
457        assert_eq!(
458            Identifier::<All>::try_parse("_1")
459                .unwrap()
460                .apply_boundaries(&[Boundary::Underscore])
461                .to_case(Case::Kebab),
462            "1"
463        );
464        assert_eq!(
465            Identifier::<All>::try_parse("a1")
466                .unwrap()
467                .apply_boundaries(&[Boundary::Underscore])
468                .to_case(Case::Kebab),
469            "a1"
470        );
471        assert_eq!(
472            Identifier::<All>::try_parse("a_1")
473                .unwrap()
474                .apply_boundaries(&[Boundary::Underscore])
475                .to_case(Case::Kebab),
476            "a-1"
477        );
478        assert_eq!(
479            Identifier::<All>::try_parse("๐Ÿ˜ˆ")
480                .unwrap()
481                .apply_boundaries(&[Boundary::Underscore])
482                .check_validity(),
483            Err(Error::InvalidCharacter {
484                byte_offset: 0,
485                invalid_char: '๐Ÿ˜ˆ'
486            })
487        );
488        assert_eq!(
489            Identifier::<All>::try_parse("abc๐Ÿ˜ˆ")
490                .unwrap()
491                .apply_boundaries(&[Boundary::Underscore])
492                .check_validity(),
493            Err(Error::InvalidCharacter {
494                byte_offset: 3,
495                invalid_char: '๐Ÿ˜ˆ'
496            })
497        );
498        assert_eq!(
499            Identifier::<All>::try_parse("_")
500                .unwrap()
501                .apply_boundaries(&[Boundary::Space])
502                .to_case(Case::Kebab),
503            "_"
504        );
505        assert_eq!(
506            Identifier::<All>::try_parse("_")
507                .unwrap()
508                .apply_boundaries(&[Boundary::Underscore])
509                .check_validity(),
510            Err(Error::EmptyAfterSplits)
511        );
512        assert_eq!(
513            Identifier::<All>::try_parse("abc def")
514                .unwrap()
515                .apply_boundaries(&[Boundary::Underscore])
516                .check_validity(),
517            Err(Error::InvalidCharacter {
518                byte_offset: 3,
519                invalid_char: ' '
520            })
521        );
522        Identifier::<All>::try_parse("abc def")
523            .unwrap()
524            .apply_boundaries(&[Boundary::Space])
525            .check_validity()
526            .unwrap();
527        assert_eq!(
528            Identifier::<All>::try_parse("abc_def")
529                .unwrap()
530                .apply_boundaries(&[Boundary::Underscore])
531                .to_case(Case::Kebab),
532            "abc-def"
533        );
534        assert_eq!(
535            Identifier::<All>::try_parse("_abc_def")
536                .unwrap()
537                .apply_boundaries(&[Boundary::Underscore])
538                .to_case(Case::Kebab),
539            "abc-def"
540        );
541        assert_eq!(
542            Identifier::<All>::try_parse("Bar๐Ÿšฉbar")
543                .unwrap()
544                .apply_boundaries(&[Boundary::Underscore])
545                .check_validity(),
546            Err(Error::InvalidCharacter {
547                byte_offset: 3,
548                invalid_char: '๐Ÿšฉ'
549            })
550        );
551    }
552
553    #[test]
554    fn default_is_empty() {
555        assert!(Identifier::<All>::default().is_empty());
556    }
557
558    #[test]
559    fn static_vs_runtime_equals() {
560        assert_eq!(
561            Identifier::<Type>::try_parse("a")
562                .unwrap()
563                .to_runtime_type(),
564            Identifier::try_parse_with_type("a", RuntimeType::Type).unwrap()
565        );
566
567        assert_ne!(
568            Identifier::<Type>::try_parse("a")
569                .unwrap()
570                .to_runtime_type(),
571            Identifier::try_parse_with_type("a", RuntimeType::Operation).unwrap()
572        );
573    }
574
575    #[test]
576    fn all_vs_specific_equals() {
577        assert_eq!(
578            Identifier::<All>::try_parse("a").unwrap().to_runtime_type(),
579            Identifier::<Type>::try_parse("a")
580                .unwrap()
581                .to_runtime_type(),
582        );
583        assert_eq!(
584            Identifier::<All>::try_parse("a").unwrap().to_runtime_type(),
585            Identifier::<Operation>::try_parse("a")
586                .unwrap()
587                .to_runtime_type(),
588        );
589    }
590
591    #[test]
592    fn issue_274() {
593        // https://github.com/diondokter/device-driver/issues/274
594        Identifier::<All>::try_parse("io_pad_i2c_b1")
595            .unwrap()
596            .apply_boundaries(&Boundary::defaults())
597            .check_validity()
598            .unwrap();
599
600        Identifier::<All>::try_parse("io_pad_i2c-b1")
601            .unwrap()
602            .apply_boundaries(&[Boundary::Underscore])
603            .check_validity()
604            .unwrap_err();
605    }
606}