oca_bundle_semantics/state/oca/overlay/
attribute_framing.rs

1use crate::state::{attribute::Attribute, oca::Overlay};
2use isolang::Language;
3use oca_ast_semantics::ast::OverlayType;
4use said::derivation::HashFunctionCode;
5use said::{sad::SerializationFormats, sad::SAD};
6use serde::{ser::SerializeMap, Deserialize, Serialize, Serializer};
7use std::any::Any;
8use std::collections::HashMap;
9
10pub type Framing = HashMap<String, FramingScope>;
11
12#[derive(Serialize, Deserialize, Debug, Clone)]
13pub struct FramingScope {
14    pub predicate_id: String,
15    pub framing_justification: String,
16    #[serde(skip)]
17    pub frame_meta: HashMap<String, String>,
18}
19
20pub trait Framings {
21    fn set_framing(&mut self, id: String, framing: Framing);
22}
23
24impl Framings for Attribute {
25    fn set_framing(&mut self, id: String, framing: Framing) {
26        match self.framings {
27            Some(ref mut framings) => {
28                if let Some(f) = framings.get_mut(&id) {
29                    f.extend(framing);
30                } else {
31                    framings.insert(id, framing);
32                }
33            }
34            None => {
35                let mut framings = HashMap::new();
36                framings.insert(id, framing);
37                self.framings = Some(framings);
38            }
39        }
40    }
41}
42
43pub fn serialize_metadata<S>(metadata: &HashMap<String, String>, s: S) -> Result<S::Ok, S::Error>
44where
45    S: Serializer,
46{
47    use std::collections::BTreeMap;
48
49    let mut ser = s.serialize_map(Some(metadata.len()))?;
50    let sorted_metadata: BTreeMap<_, _> = metadata.iter().collect();
51    for (k, v) in sorted_metadata {
52        ser.serialize_entry(k, v)?;
53    }
54    ser.end()
55}
56
57pub fn serialize_framing<S>(attributes: &HashMap<String, Framing>, s: S) -> Result<S::Ok, S::Error>
58where
59    S: Serializer,
60{
61    use std::collections::BTreeMap;
62
63    let mut ser = s.serialize_map(Some(attributes.len()))?;
64    let sorted_attributes: BTreeMap<_, _> = attributes.iter().collect();
65    for (k, v) in sorted_attributes {
66        let sorted_framings: BTreeMap<_, _> = v.iter().collect();
67        ser.serialize_entry(k, &sorted_framings)?;
68    }
69    ser.end()
70}
71
72#[derive(SAD, Serialize, Deserialize, Debug, Clone)]
73pub struct AttributeFramingOverlay {
74    #[said]
75    #[serde(rename = "d")]
76    said: Option<said::SelfAddressingIdentifier>,
77    capture_base: Option<said::SelfAddressingIdentifier>,
78    #[serde(rename = "type")]
79    overlay_type: OverlayType,
80    #[serde(rename = "framing_metadata", serialize_with = "serialize_metadata")]
81    pub metadata: HashMap<String, String>,
82    #[serde(serialize_with = "serialize_framing")]
83    pub attribute_framing: HashMap<String, Framing>,
84}
85
86impl Overlay for AttributeFramingOverlay {
87    fn as_any(&self) -> &dyn Any {
88        self
89    }
90    fn capture_base(&self) -> &Option<said::SelfAddressingIdentifier> {
91        &self.capture_base
92    }
93    fn set_capture_base(&mut self, said: &said::SelfAddressingIdentifier) {
94        self.capture_base = Some(said.clone());
95    }
96    fn overlay_type(&self) -> &OverlayType {
97        &self.overlay_type
98    }
99    fn said(&self) -> &Option<said::SelfAddressingIdentifier> {
100        &self.said
101    }
102    fn language(&self) -> Option<&Language> {
103        None
104    }
105    fn attributes(&self) -> Vec<&String> {
106        self.attribute_framing.keys().collect::<Vec<&String>>()
107    }
108    /// Add an attribute to the Label Overlay
109    /// TODO add assignment of attribute to category
110    fn add(&mut self, attribute: &Attribute) {
111        if let Some(id) = self.metadata.get("frame_id") {
112            if let Some(framing) = &attribute.framings {
113                if let Some(value) = framing.get(id) {
114                    self.attribute_framing
115                        .insert(attribute.name.clone(), value.clone());
116
117                    for framing_scope in value.values() {
118                        for (k, v) in framing_scope.frame_meta.iter() {
119                            self.metadata.insert(k.clone(), v.clone());
120                        }
121                    }
122                }
123            }
124        }
125    }
126}
127
128impl AttributeFramingOverlay {
129    pub fn new(id: String) -> Self {
130        let mut metadata = HashMap::new();
131        metadata.insert("frame_id".to_string(), id);
132        let overlay_version = "1.1".to_string();
133        Self {
134            capture_base: None,
135            said: None,
136            overlay_type: OverlayType::AttributeFraming(overlay_version),
137            metadata,
138            attribute_framing: HashMap::new(),
139        }
140    }
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146
147    #[test]
148    fn create_attribute_framing_overlay() {
149        let mut overlay = AttributeFramingOverlay::new("frame_id".to_string());
150        let mut loc1 = HashMap::new();
151        loc1.insert(
152            "http://loc.1".to_string(),
153            FramingScope {
154                predicate_id: "skos:exactMatch".to_string(),
155                framing_justification: "semapv:ManualMappingCuration".to_string(),
156                frame_meta: HashMap::new(),
157            },
158        );
159        let mut loc2 = HashMap::new();
160        loc2.insert(
161            "http://loc.2".to_string(),
162            FramingScope {
163                predicate_id: "skos:exactMatch".to_string(),
164                framing_justification: "semapv:ManualMappingCuration".to_string(),
165                frame_meta: HashMap::new(),
166            },
167        );
168        let attr = cascade! {
169            Attribute::new("attr1".to_string());
170            ..set_framing("frame_id".to_string(), loc1);
171            ..set_framing("frame_id".to_string(), loc2);
172        };
173        // even that attribute has 2 lagnuage only one attribute should be added to the overlay according to it's language
174        overlay.add(&attr);
175
176        let overlay_version = "1.1".to_string();
177        assert_eq!(overlay.overlay_type, OverlayType::AttributeFraming(overlay_version));
178        assert_eq!(overlay.attribute_framing.len(), 1);
179    }
180}