dctap/
tap_headers.rs

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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
use crate::tap_error::Result;
use csv::StringRecord;
use tracing::debug;

#[derive(Debug, Default)]
pub struct TapHeaders {
    shape_id: Option<usize>,
    shape_label: Option<usize>,
    property_id: Option<usize>,
    property_label: Option<usize>,
    repeatable: Option<usize>,
    mandatory: Option<usize>,
    value_datatype: Option<usize>,
    value_nodetype: Option<usize>,
    value_shape: Option<usize>,
    value_constraint: Option<usize>,
    value_constraint_type: Option<usize>,
    note: Option<usize>,
    // The following headers are not part of DCTAP standard but they useful when there are cases of inheritance
    extends_id: Option<usize>,
    extends_label: Option<usize>,
}

impl TapHeaders {
    pub(crate) fn new() -> TapHeaders {
        TapHeaders::default()
    }

    pub(crate) fn from_record(record: &StringRecord) -> Result<TapHeaders> {
        let mut shape_id = None;
        let mut shape_label = None;
        let mut property_id = None;
        let mut property_label = None;
        let mut repeatable = None;
        let mut mandatory = None;
        let mut value_nodetype = None;
        let mut value_datatype = None;
        let mut value_constraint = None;
        let mut value_constraint_type = None;
        let mut value_shape = None;
        let mut note = None;
        let mut extends_id = None;
        let mut extends_label = None;

        for (idx, field) in record.iter().enumerate() {
            match clean(field).as_str() {
                "SHAPEID" => shape_id = Some(idx),
                "PROPERTYID" => property_id = Some(idx),
                "PROPERTYLABEL" => property_label = Some(idx),
                "SHAPELABEL" => shape_label = Some(idx),
                "REPEATABLE" => repeatable = Some(idx),
                "MANDATORY" => mandatory = Some(idx),
                "VALUEDATATYPE" => value_datatype = Some(idx),
                "VALUESHAPE" => value_shape = Some(idx),
                "VALUENODETYPE" => value_nodetype = Some(idx),
                "VALUECONSTRAINT" => value_constraint = Some(idx),
                "VALUECONSTRAINTTYPE" => value_constraint_type = Some(idx),
                "NOTE" => note = Some(idx),
                "EXTENDSID" => extends_id = Some(idx),
                "EXTENDSLABEL" => extends_label = Some(idx),
                _ => {
                    debug!("Unknown field reading headers: {field}")
                }
            }
        }
        Ok(TapHeaders {
            shape_id,
            shape_label,
            property_id,
            property_label,
            repeatable,
            mandatory,
            value_datatype,
            value_nodetype,
            value_shape,
            value_constraint,
            value_constraint_type,
            note,
            extends_id,
            extends_label,
        })
    }

    pub fn shape_id(&self, rcd: &StringRecord) -> Option<String> {
        self.shape_id.and_then(|idx| get_str_from_rcd(rcd, idx))
    }

    pub fn property_id(&self, rcd: &StringRecord) -> Option<String> {
        self.property_id.and_then(|idx| get_str_from_rcd(rcd, idx))
    }

    pub fn property_label(&self, rcd: &StringRecord) -> Option<String> {
        self.property_label
            .and_then(|idx| get_str_from_rcd(rcd, idx))
    }
    pub fn shape_label(&self, rcd: &StringRecord) -> Option<String> {
        self.shape_label.and_then(|idx| get_str_from_rcd(rcd, idx))
    }
    pub fn repeatable(&self, rcd: &StringRecord) -> Option<String> {
        self.repeatable.and_then(|idx| get_str_from_rcd(rcd, idx))
    }
    pub fn mandatory(&self, rcd: &StringRecord) -> Option<String> {
        self.mandatory.and_then(|idx| get_str_from_rcd(rcd, idx))
    }
    pub fn value_datatype(&self, rcd: &StringRecord) -> Option<String> {
        self.value_datatype
            .and_then(|idx| get_str_from_rcd(rcd, idx))
    }
    pub fn value_nodetype(&self, rcd: &StringRecord) -> Option<String> {
        self.value_nodetype
            .and_then(|idx| get_str_from_rcd(rcd, idx))
    }
    pub fn value_shape(&self, rcd: &StringRecord) -> Option<String> {
        self.value_shape.and_then(|idx| get_str_from_rcd(rcd, idx))
    }
    pub fn value_constraint(&self, rcd: &StringRecord) -> Option<String> {
        self.value_constraint
            .and_then(|idx| get_str_from_rcd(rcd, idx))
    }
    pub fn value_constraint_type(&self, rcd: &StringRecord) -> Option<String> {
        self.value_constraint_type
            .and_then(|idx| get_str_from_rcd(rcd, idx))
    }
    pub fn note(&self, rcd: &StringRecord) -> Option<String> {
        self.note.and_then(|idx| get_str_from_rcd(rcd, idx))
    }

    pub fn extends_id(&self, rcd: &StringRecord) -> Option<String> {
        self.extends_id.and_then(|idx| get_str_from_rcd(rcd, idx))
    }
    pub fn extends_label(&self, rcd: &StringRecord) -> Option<String> {
        self.extends_label
            .and_then(|idx| get_str_from_rcd(rcd, idx))
    }
}

fn clean(str: &str) -> String {
    str.trim().to_uppercase()
}

fn get_str_from_rcd(rcd: &StringRecord, idx: usize) -> Option<String> {
    rcd.get(idx).map(|str| str.to_string())
}