1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
use super::error::{Error, OdsError};
use tblgen::{error::WithLocation, record::Record};

#[derive(Debug, Clone)]
pub enum Trait {
    Interface {
        name: String,
    },
    Internal {
        name: String,
    },
    Native {
        name: String,
        #[allow(unused)]
        structural: bool,
    },
    Predicate,
}

impl Trait {
    pub fn new(definition: Record) -> Result<Self, Error> {
        Ok(if definition.subclass_of("PredTrait") {
            Self::Predicate
        } else if definition.subclass_of("InterfaceTrait") {
            Self::Interface {
                name: Self::build_name(definition)?,
            }
        } else if definition.subclass_of("NativeTrait") {
            Self::Native {
                name: Self::build_name(definition)?,
                structural: definition.subclass_of("StructuralOpTrait"),
            }
        } else if definition.subclass_of("GenInternalTrait") {
            Self::Internal {
                name: definition.string_value("trait")?,
            }
        } else {
            return Err(OdsError::InvalidTrait.with_location(definition).into());
        })
    }

    pub fn name(&self) -> Option<&str> {
        match self {
            Self::Native { name, .. } | Self::Internal { name } | Self::Interface { name } => {
                Some(name)
            }
            Self::Predicate => None,
        }
    }

    fn build_name(definition: Record) -> Result<String, Error> {
        let r#trait = definition.string_value("trait")?;
        let namespace = definition.string_value("cppNamespace")?;

        Ok(if namespace.is_empty() {
            r#trait
        } else {
            format!("{namespace}::{trait}")
        })
    }
}