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