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 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 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 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}