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
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use *;
use crc::CRC64WE as CRC;

/// A normalized `File`
///
/// The underlying file can't be exposed in a mutable way. Any change to the normalized format would "denormalize" it.
/// The `NormalizedFile` can be used to calculate the DSDL signature
///
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NormalizedFile(File);

impl NormalizedFile {
    /// Return a reference to the underlying `File`
    pub fn as_file<'a>(&'a self) -> &'a File {
        &self.0
    }

    /// Turn the `NormalizedFile` into the underlying `File`
    pub fn into_file(self) -> File {
        self.0
    }

    /// Calculate the DSDL signature
    pub fn dsdl_signature(&self) -> u64 {
        let mut crc = CRC::new();
        crc.add(format!("{}", self).as_bytes());
        crc.value()
    }
        
}

impl File {
    /// Normalizes a file according to the uavcan specification.
    pub fn normalize(self) -> NormalizedFile {
        let definition = self.definition.normalize(&self.name);
        NormalizedFile(File{name: self.name, definition: definition})
    }
}

impl TypeDefinition {
    fn normalize(self, file_name: &FileName) -> Self {
        match self {
            TypeDefinition::Message(x) => TypeDefinition::Message(x.normalize(file_name)),
            TypeDefinition::Service(x) => TypeDefinition::Service(x.normalize(file_name)),
        }
    }
}

impl ServiceDefinition {
    fn normalize(self, file_name: &FileName) -> Self {
        ServiceDefinition{request: self.request.normalize(file_name), response: self.response.normalize(file_name)}
    }
}

impl MessageDefinition {
    fn normalize(self, file_name: &FileName) -> Self {
        let mut normalized_lines = Vec::new();
        for line in self.0 {
            match line.normalize(file_name) {
                Some(x) => normalized_lines.push(x),
                None => (),
            }
        }
        MessageDefinition(normalized_lines)        
    }
}

impl Line {
    fn normalize(self, file_name: &FileName) -> Option<Self> {
        // 1. Remove comments.
        match self {
            Line::Empty => None,
            Line::Comment(_) => None,
            Line::Definition(def, _) => match def.normalize(file_name) {
                Some(norm_def) => Some(Line::Definition(norm_def, None)),
                None => None,},
            Line::Directive(dir, _) => Some(Line::Directive(dir, None)),
        }
    }
}

impl AttributeDefinition {
    fn normalize(self, file_name: &FileName) -> Option<Self> {
        match self {
            AttributeDefinition::Field(def) => Some(AttributeDefinition::Field(def.normalize(file_name))),
            // 2. Remove all constant definitions
            AttributeDefinition::Const(_) => None,
        }
    }
}

impl FieldDefinition {
    fn normalize(self, file_name: &FileName) -> Self {
        match self {
            // 3. Ensure that all cast specifiers are explicitly defined; if not, add default cast specifiers.
            FieldDefinition{cast_mode: None, field_type: Ty::Primitive(primitive_type), array, name} =>
                FieldDefinition{
                    cast_mode: Some(CastMode::Saturated),
                    field_type: Ty::Primitive(primitive_type),
                    array: array.normalize(),
                    name: name,
                },
            // 5. For nested data structures, replace all short names with full names.
            FieldDefinition{field_type: Ty::Composite(CompositeType{namespace: None, name: type_name}), cast_mode, array, name: field_name} =>
                FieldDefinition{
                    cast_mode: cast_mode,
                    field_type: Ty::Composite(CompositeType{namespace: Some(Ident::from(file_name.clone().namespace)), name: type_name}),
                    array: array.normalize(),
                    name: field_name,
                },
            x => x,
        }
    }
}

impl ArrayInfo {
    fn normalize(self) -> Self {
        // 4. For dynamic arrays, replace the max length specifier in the form [<X] to the form [<=Y]
        match self {
            ArrayInfo::DynamicLess(Size(num)) => ArrayInfo::DynamicLeq(Size(num-1)),
            x => x,
        }
    }
}