rvlib/tools/
attributes.rs

1use tracing::info;
2
3use super::Manipulate;
4use crate::{
5    annotations_accessor_mut,
6    events::Events,
7    file_util::PathPair,
8    history::History,
9    make_tool_transform,
10    result::trace_ok_err,
11    tools_data::{
12        attributes_data::{set_attrmap_val, AttrMap, AttrVal},
13        AttributesToolData,
14    },
15    tools_data_accessors,
16    world::World,
17    world_annotations_accessor,
18};
19use std::mem;
20const MISSING_DATA_MSG: &str = "Missing data for Attributes";
21pub const ACTOR_NAME: &str = "Attributes";
22annotations_accessor_mut!(ACTOR_NAME, attributes_mut, "Attribute didn't work", AttrMap);
23world_annotations_accessor!(ACTOR_NAME, attributes, "Attribute didn't work", AttrMap);
24tools_data_accessors!(
25    ACTOR_NAME,
26    MISSING_DATA_MSG,
27    attributes_data,
28    AttributesToolData,
29    attributes,
30    attributes_mut
31);
32
33fn propagate_annos(
34    mut annos: AttrMap,
35    attr_names: &[String],
36    to_propagate: &[(usize, AttrVal)],
37) -> AttrMap {
38    for (attr_idx, val) in to_propagate {
39        if let Some(attr_val) = annos.get_mut(&attr_names[*attr_idx]) {
40            *attr_val = val.clone();
41        }
42    }
43    annos
44}
45
46fn get_buffers(world: &World) -> Vec<String> {
47    let annos = get_annos(world);
48    let data = get_specific(world);
49    if let (Some(data), Some(annos)) = (data, annos) {
50        data.attr_names()
51            .iter()
52            .map(|attr_name| annos.get(attr_name).unwrap().to_string())
53            .collect()
54    } else {
55        vec![]
56    }
57}
58fn propagate_buffer(
59    mut attribute_buffer: Vec<String>,
60    to_propagate: &[(usize, AttrVal)],
61) -> Vec<String> {
62    for (attr_idx, val) in to_propagate {
63        attribute_buffer[*attr_idx] = val.to_string();
64    }
65    attribute_buffer
66}
67fn file_change(mut world: World) -> World {
68    let attr_buffers = get_buffers(&world);
69    let annos = get_annos_mut(&mut world).map(mem::take);
70    let data = get_specific_mut(&mut world);
71
72    if let (Some(data), Some(mut annos)) = (data, annos) {
73        for (attr_name, attr_val) in data.attr_names().iter().zip(data.attr_vals().iter()) {
74            if !annos.contains(attr_name) {
75                set_attrmap_val(&mut annos, attr_name, attr_val);
76            }
77        }
78
79        // put string representations of the attribute values into the buffer
80        let attr_buffers = propagate_buffer(attr_buffers, &data.to_propagate_attr_val);
81        for (i, buffer) in attr_buffers.into_iter().enumerate() {
82            *data.attr_value_buffer_mut(i) = buffer;
83        }
84
85        annos = propagate_annos(annos, data.attr_names(), &data.to_propagate_attr_val);
86
87        if let Some(annos_) = get_annos_mut(&mut world) {
88            *annos_ = annos;
89        }
90    }
91    let current = get_annos(&world).cloned();
92    if let Some(data) = get_specific_mut(&mut world) {
93        data.current_attr_map = current;
94    }
95    world
96}
97fn add_attribute(mut world: World, suppress_exists_err: bool) -> World {
98    let attr_map_tmp = get_annos_mut(&mut world).map(mem::take);
99    let data = get_specific_mut(&mut world);
100
101    if let (Some(mut attr_map_tmp), Some(data)) = (attr_map_tmp, data) {
102        let new_attr_name = data.new_attr_name.clone();
103        if data.attr_names().contains(&new_attr_name) && !suppress_exists_err {
104            tracing::error!("New attribute {new_attr_name} could not be created, already exists");
105        } else {
106            let new_attr_val = data.new_attr_val.clone();
107            for (_, (val_map, _)) in data.anno_iter_mut() {
108                set_attrmap_val(val_map, &new_attr_name, &new_attr_val);
109            }
110            set_attrmap_val(&mut attr_map_tmp, &new_attr_name, &new_attr_val);
111            if let Some(a) = get_annos_mut(&mut world) {
112                a.clone_from(&attr_map_tmp);
113            }
114            if let Some(data) = get_specific_mut(&mut world) {
115                data.current_attr_map = Some(attr_map_tmp);
116                data.push(new_attr_name, new_attr_val);
117            }
118        }
119    }
120    if let Some(data) = get_specific_mut(&mut world) {
121        data.options.is_addition_triggered = false;
122        data.new_attr_name = String::new();
123        data.new_attr_val = AttrVal::default();
124    }
125    world
126}
127#[derive(Clone, Copy, Debug)]
128pub struct Attributes;
129
130impl Manipulate for Attributes {
131    fn new() -> Self
132    where
133        Self: Sized,
134    {
135        Self
136    }
137
138    fn on_activate(&mut self, mut world: World) -> World {
139        let data = get_data_mut(&mut world);
140        if let Some(data) = trace_ok_err(data) {
141            data.menu_active = true;
142        }
143        file_change(world)
144    }
145    fn on_deactivate(&mut self, mut world: World) -> World {
146        let data = get_data_mut(&mut world);
147        if let Some(data) = trace_ok_err(data) {
148            data.menu_active = false;
149        }
150        world
151    }
152    fn on_filechange(&mut self, world: World, history: History) -> (World, History) {
153        (file_change(world), history)
154    }
155    fn events_tf(
156        &mut self,
157        mut world: World,
158        history: History,
159        _event: &Events,
160    ) -> (World, History) {
161        let is_addition_triggered = get_specific(&world).map(|d| d.options.is_addition_triggered);
162        if is_addition_triggered == Some(true) {
163            // handle addition triggered in the GUI
164            world = add_attribute(world, false);
165        }
166        let attr_data = get_specific_mut(&mut world);
167        if let Some(attr_data) = attr_data {
168            if let Some(rename_src_idx) = attr_data.options.rename_src_idx {
169                let from_name = &attr_data.attr_names()[rename_src_idx].clone();
170                let to_name = &attr_data.new_attr_name.clone();
171                tracing::info!("Rename attribute {from_name} to {to_name}");
172                attr_data.rename(from_name, to_name);
173                attr_data.options.rename_src_idx = None;
174            }
175        }
176        let is_update_triggered = get_specific(&world).map(|d| d.options.is_update_triggered);
177        if is_update_triggered == Some(true) {
178            info!("update attr");
179            let current_from_menu_clone =
180                get_specific(&world).and_then(|d| d.current_attr_map.clone());
181            if let (Some(mut cfm), Some(anno)) =
182                (current_from_menu_clone, get_annos_mut(&mut world))
183            {
184                *anno = mem::take(&mut cfm);
185            }
186            if let Some(update_current_attr_map) =
187                get_specific_mut(&mut world).map(|d| &mut d.options.is_update_triggered)
188            {
189                *update_current_attr_map = false;
190            }
191        }
192        if let Some(removal_idx) = get_specific(&world).map(|d| d.options.removal_idx) {
193            let data = get_specific_mut(&mut world);
194            if let (Some(data), Some(removal_idx)) = (data, removal_idx) {
195                data.remove_attr(removal_idx);
196            }
197            if let Some(removal_idx) =
198                get_specific_mut(&mut world).map(|d| &mut d.options.removal_idx)
199            {
200                *removal_idx = None;
201            }
202        }
203        let is_export_triggered =
204            get_specific(&world).map(|d| d.options.import_export_trigger.export_triggered());
205        if is_export_triggered == Some(true) {
206            let ssh_cfg = world.data.meta_data.ssh_cfg.clone();
207            let attr_data = get_specific(&world);
208            let export_only_opened_folder =
209                attr_data.map(|d| d.options.export_only_opened_folder) == Some(true);
210            let key_filter = if export_only_opened_folder {
211                world
212                    .data
213                    .meta_data
214                    .opened_folder
215                    .as_ref()
216                    .map(PathPair::path_relative)
217            } else {
218                None
219            };
220            let annos_str = get_specific(&world)
221                .and_then(|d| trace_ok_err(d.serialize_annotations(key_filter)));
222            if let (Some(annos_str), Some(data)) = (annos_str, get_specific(&world)) {
223                if trace_ok_err(data.export_path.conn.write(
224                    &annos_str,
225                    &data.export_path.path,
226                    ssh_cfg.as_ref(),
227                ))
228                .is_some()
229                {
230                    info!("exported annotations to {:?}", data.export_path.path);
231                }
232            }
233            if let Some(export_triggered) =
234                get_specific_mut(&mut world).map(|d| &mut d.options.import_export_trigger)
235            {
236                export_triggered.untrigger_export();
237            }
238        }
239        let is_import_triggered =
240            get_specific(&world).map(|d| d.options.import_export_trigger.import_triggered());
241        if is_import_triggered == Some(true) {
242            tracing::info!("import attr tiggered");
243            let ssh_cfg = world.data.meta_data.ssh_cfg.clone();
244            let cur_prj = world.data.meta_data.prj_path().map(|p| p.to_path_buf());
245            let attr_data = get_specific_mut(&mut world);
246            let imported_map = attr_data.and_then(|data| {
247                let in_path = &data.export_path.path;
248                tracing::info!("importing attributes from {in_path:?}");
249                let json_str = trace_ok_err(data.export_path.conn.read(in_path, ssh_cfg.as_ref()));
250                if let Some(s) = json_str {
251                    trace_ok_err(AttributesToolData::deserialize_annotations(
252                        &s,
253                        cur_prj.as_deref(),
254                    ))
255                } else {
256                    None
257                }
258            });
259            if let Some(imported_map) = &imported_map {
260                // add attributes in case they don't exist
261                for (_, (attr_map, _)) in imported_map.iter() {
262                    for (attr_name, attr_val) in attr_map.iter() {
263                        let data = get_specific_mut(&mut world);
264                        if let Some(d) = data {
265                            d.new_attr_name = attr_name.clone();
266                            d.new_attr_val = attr_val.clone().reset();
267                        }
268                        tracing::debug!("inserting attr {attr_name} with value {attr_val}");
269                        world = add_attribute(world, true);
270                    }
271                }
272            }
273            if let Some(imported_map) = imported_map {
274                let data = get_specific_mut(&mut world);
275                if let Some(d) = data {
276                    d.merge_map(imported_map);
277                }
278            }
279            let annos = get_annos(&world).cloned();
280            let attr_buffer = get_buffers(&world);
281            if let (Some(data), Some(annos)) = (get_specific_mut(&mut world), annos) {
282                data.current_attr_map = Some(annos);
283                data.set_new_attr_value_buffer(attr_buffer);
284            }
285        }
286        if let Some(import_trigger) =
287            get_specific_mut(&mut world).map(|d| &mut d.options.import_export_trigger)
288        {
289            import_trigger.untrigger_import();
290        }
291        make_tool_transform!(self, world, history, event, [])
292    }
293}
294#[cfg(test)]
295use {
296    crate::tracing_setup::init_tracing_for_tests, crate::types::ViewImage, image::DynamicImage,
297    std::collections::HashMap, std::fs, std::path::Path,
298};
299#[cfg(test)]
300pub(super) fn test_data() -> (World, History) {
301    use std::path::Path;
302
303    use crate::ToolsDataMap;
304
305    let im_test = DynamicImage::ImageRgb8(ViewImage::new(64, 64));
306    let mut world = World::from_real_im(
307        im_test,
308        ToolsDataMap::new(),
309        None,
310        Some("superimage.png".to_string()),
311        Path::new("superimage.png"),
312        Some(0),
313    );
314    world.data.meta_data.flags.is_loading_screen_active = Some(false);
315
316    let history = History::default();
317    (world, history)
318}
319#[test]
320fn test_import_export() {
321    init_tracing_for_tests();
322    fn test(testpath: &Path) {
323        let (mut world, history) = test_data();
324        let data = get_specific_mut(&mut world).unwrap();
325        let json_str = fs::read_to_string(testpath).unwrap();
326        let reference_data = AttributesToolData::deserialize_annotations(&json_str, None).unwrap();
327        tracing::debug!("reference_data: {:?}", reference_data);
328        data.export_path.path = testpath.to_path_buf();
329        data.options.import_export_trigger.trigger_import();
330        let events = Events::default();
331        let (world, _) = Attributes {}.events_tf(world, history, &events);
332        let annos = world.data.tools_data_map[ACTOR_NAME]
333            .specifics
334            .attributes()
335            .unwrap()
336            .anno_iter()
337            .collect::<HashMap<_, _>>();
338        tracing::debug!("annos: {:?}", annos);
339        for k in reference_data.keys() {
340            tracing::debug!("k: {:?}", k);
341            let (annos, _) = annos.get(k).unwrap();
342            let (ref_annos, _) = &reference_data[k];
343            assert_eq!(annos, ref_annos);
344        }
345        let current = get_annos(&world).unwrap();
346        for v in current.values() {
347            assert!(v.is_default());
348        }
349    }
350    let testpath = Path::new("resources/test_data/attr_import.json");
351    test(testpath);
352    let testpath = Path::new("resources/test_data/attr_import_untagged.json");
353    test(testpath);
354}