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