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