From 2abf46c74761cbc77bbc4543df8465bd35add869 Mon Sep 17 00:00:00 2001 From: vcoppe Date: Sat, 3 Oct 2026 15:33:44 +0200 Subject: [PATCH] progress --- gpx-rs/engine/src/core/action/file.rs | 1 + gpx-rs/engine/src/core/action/mod.rs | 3 + gpx-rs/engine/src/core/gpx/common.rs | 2 +- gpx-rs/engine/src/core/gpx/file.rs | 6 +- gpx-rs/engine/src/core/gpx/segment.rs | 4 +- gpx-rs/engine/src/core/gpx/track.rs | 6 +- gpx-rs/engine/src/core/mod.rs | 2 + gpx-rs/engine/src/engine/command/applier.rs | 26 ++++ gpx-rs/engine/src/engine/command/mod.rs | 4 + gpx-rs/engine/src/engine/command/pattern.rs | 38 ++++++ gpx-rs/engine/src/engine/engine.rs | 123 ++++++++---------- gpx-rs/engine/src/engine/mod.rs | 2 - gpx-rs/engine/src/engine/state/mod.rs | 10 ++ gpx-rs/engine/src/engine/state/order.rs | 4 + .../src/engine/{ => state}/selection.rs | 8 -- gpx-rs/engine/src/engine/state/stack.rs | 6 +- 16 files changed, 151 insertions(+), 94 deletions(-) create mode 100644 gpx-rs/engine/src/core/action/file.rs create mode 100644 gpx-rs/engine/src/core/action/mod.rs create mode 100644 gpx-rs/engine/src/engine/command/applier.rs create mode 100644 gpx-rs/engine/src/engine/command/pattern.rs create mode 100644 gpx-rs/engine/src/engine/state/order.rs rename gpx-rs/engine/src/engine/{ => state}/selection.rs (74%) diff --git a/gpx-rs/engine/src/core/action/file.rs b/gpx-rs/engine/src/core/action/file.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/gpx-rs/engine/src/core/action/file.rs @@ -0,0 +1 @@ + diff --git a/gpx-rs/engine/src/core/action/mod.rs b/gpx-rs/engine/src/core/action/mod.rs new file mode 100644 index 000000000..529c201fa --- /dev/null +++ b/gpx-rs/engine/src/core/action/mod.rs @@ -0,0 +1,3 @@ +mod file; + +pub use file::*; diff --git a/gpx-rs/engine/src/core/gpx/common.rs b/gpx-rs/engine/src/core/gpx/common.rs index 29897a73f..1fd1f3501 100644 --- a/gpx-rs/engine/src/core/gpx/common.rs +++ b/gpx-rs/engine/src/core/gpx/common.rs @@ -1,4 +1,4 @@ -#[derive(Debug, Default, PartialEq, Eq)] +#[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct Link { pub href: String, pub text: Option, diff --git a/gpx-rs/engine/src/core/gpx/file.rs b/gpx-rs/engine/src/core/gpx/file.rs index 1a035bb06..0e7d8b0a0 100644 --- a/gpx-rs/engine/src/core/gpx/file.rs +++ b/gpx-rs/engine/src/core/gpx/file.rs @@ -13,7 +13,7 @@ impl Default for FileId { } } -#[derive(Debug, Default, PartialEq)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct File { pub id: FileId, pub info: FileInfo, @@ -22,7 +22,7 @@ pub struct File { // TODO routes } -#[derive(Debug, Default, PartialEq)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct FileInfo { pub name: String, pub desc: Option, @@ -31,7 +31,7 @@ pub struct FileInfo { pub time: Option, } -#[derive(Debug, Default, PartialEq)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct Author { pub name: Option, pub email: Option, diff --git a/gpx-rs/engine/src/core/gpx/segment.rs b/gpx-rs/engine/src/core/gpx/segment.rs index de5d68989..f32468a94 100644 --- a/gpx-rs/engine/src/core/gpx/segment.rs +++ b/gpx-rs/engine/src/core/gpx/segment.rs @@ -4,7 +4,7 @@ use uuid::Uuid; use crate::{Trackpoint, TrackpointChunk}; -#[derive(Debug, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct TrackSegmentId(Uuid); impl Default for TrackSegmentId { @@ -22,7 +22,7 @@ impl Default for TrackSegmentRevisionId { } } -#[derive(Debug, Default, PartialEq)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct TrackSegment { pub id: TrackSegmentId, pub rev_id: TrackSegmentRevisionId, diff --git a/gpx-rs/engine/src/core/gpx/track.rs b/gpx-rs/engine/src/core/gpx/track.rs index 2b015ce4d..41cd4c381 100644 --- a/gpx-rs/engine/src/core/gpx/track.rs +++ b/gpx-rs/engine/src/core/gpx/track.rs @@ -2,7 +2,7 @@ use uuid::Uuid; use crate::{Link, TrackSegment}; -#[derive(Debug, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct TrackId(Uuid); impl Default for TrackId { @@ -11,14 +11,14 @@ impl Default for TrackId { } } -#[derive(Debug, Default, PartialEq)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct Track { pub id: TrackId, pub info: TrackInfo, pub trkseg: Vec, } -#[derive(Debug, Default, PartialEq)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct TrackInfo { pub name: Option, pub cmt: Option, diff --git a/gpx-rs/engine/src/core/mod.rs b/gpx-rs/engine/src/core/mod.rs index 1e058e91f..b03150b56 100644 --- a/gpx-rs/engine/src/core/mod.rs +++ b/gpx-rs/engine/src/core/mod.rs @@ -1,9 +1,11 @@ +mod action; mod algorithm; pub(crate) mod gpx; mod io; pub(crate) mod statistics; mod utils; +pub use action::*; pub use algorithm::*; pub use gpx::*; pub use io::*; diff --git a/gpx-rs/engine/src/engine/command/applier.rs b/gpx-rs/engine/src/engine/command/applier.rs new file mode 100644 index 000000000..bf3c4c214 --- /dev/null +++ b/gpx-rs/engine/src/engine/command/applier.rs @@ -0,0 +1,26 @@ +use crate::{File, State, parse, produce, update_each_selected_file}; + +pub fn create_file(state: &mut State, name: &str) -> Result<(), String> { + produce(state, |_| { + let mut file = File::default(); + file.info.name = name.to_owned(); + vec![file] + }); + Ok(()) +} + +pub fn load_file(state: &mut State, data: &[u8]) -> Result<(), String> { + let file = parse(data).map_err(|err| err.to_string())?; + produce(state, |_| vec![file]); + Ok(()) +} + +pub fn update_metadata(state: &mut State, name: &str, desc: &str) -> Result<(), String> { + update_each_selected_file(state, &mut |file| { + let mut next = (*file).clone(); + next.info.name = name.to_owned(); + next.info.desc = Some(desc.to_owned()); + next + }); + Ok(()) +} diff --git a/gpx-rs/engine/src/engine/command/mod.rs b/gpx-rs/engine/src/engine/command/mod.rs index a30f72292..525bd6863 100644 --- a/gpx-rs/engine/src/engine/command/mod.rs +++ b/gpx-rs/engine/src/engine/command/mod.rs @@ -1,3 +1,7 @@ +mod applier; mod command; +mod pattern; +pub use applier::*; pub use command::*; +pub use pattern::*; diff --git a/gpx-rs/engine/src/engine/command/pattern.rs b/gpx-rs/engine/src/engine/command/pattern.rs new file mode 100644 index 000000000..f01854fe9 --- /dev/null +++ b/gpx-rs/engine/src/engine/command/pattern.rs @@ -0,0 +1,38 @@ +use std::{ + collections::{HashSet, hash_map::Entry}, + rc::Rc, +}; + +use crate::{File, FileId, Selection, State}; + +pub fn produce(state: &mut State, f: F) +where + F: FnOnce(&State) -> Vec, +{ + *state.selection = Selection::Empty; + for file in f(state).drain(..) { + let file_id = file.id; + state.files.insert(file_id, Rc::new(file)); + state.order.0.push(file_id); + if matches!(*state.selection, Selection::Empty) { + let mut file_ids: HashSet = Default::default(); + file_ids.insert(file_id); + *state.selection = Selection::File { file_ids }; + } + } +} + +pub fn update_each_selected_file(state: &mut State, f: &mut F) +where + F: FnMut(&File) -> File, +{ + if let Selection::File { file_ids } = state.selection { + for file_id in file_ids.iter() { + if let Entry::Occupied(mut e) = state.files.entry(*file_id) { + let file = e.get(); + let updated = Rc::new(f(file.as_ref())); + e.insert(updated); + } + } + } +} diff --git a/gpx-rs/engine/src/engine/engine.rs b/gpx-rs/engine/src/engine/engine.rs index 1faac4f65..bafe405fc 100644 --- a/gpx-rs/engine/src/engine/engine.rs +++ b/gpx-rs/engine/src/engine/engine.rs @@ -1,17 +1,15 @@ // TODO license in every file -use std::rc::Rc; - -use uuid::Uuid; - use crate::{ - Command, File, FileId, Selection, Stack, StackEntry, StatisticsBuffer, StatisticsCache, parse, + Command, FileOrder, Selection, Stack, State, StatisticsBuffer, StatisticsCache, create_file, + load_file, update_metadata, }; #[derive(Debug, Default)] pub struct Engine { stack: Stack, selection: Selection, + order: FileOrder, statistics_cache: StatisticsCache, statistics_buffer: StatisticsBuffer, } @@ -21,75 +19,54 @@ impl Engine { &self.statistics_buffer } - pub fn create_file(&mut self, name: &str) -> bool { - self.event_loop(|entry| { - let mut file = File::default(); - file.info.name = name.to_string(); - entry.insert(file.id, Rc::new(file)); - true - }) - } - - pub fn load_file(&mut self, data: &[u8]) -> bool { - self.event_loop(|entry| { - if let Ok(file) = parse(data) { - entry.insert(file.id, Rc::new(file)); - true - } else { - false - } - }) - } - - pub fn delete_file(&mut self, id: &[u8]) -> bool { - self.event_loop(|entry| { - if let Ok(id) = Uuid::from_slice(id) { - let id = FileId(id); - entry.remove(&id).is_some() - } else { - false - } - }) - } + // pub fn delete_file(&mut self, id: &[u8]) -> bool { + // self.event_loop(|entry| { + // if let Ok(id) = Uuid::from_slice(id) { + // let id = FileId(id); + // entry.remove(&id).is_some() + // } else { + // false + // } + // }) + // } /// Single entry point for every user action coming from the frontend. pub fn execute(&mut self, command: Command) -> bool { - match command { - Command::New { name } => self.create_file(name), - Command::Load { data } => self.load_file(data), - Command::Delete - | Command::DeleteAll - | Command::Duplicate - | Command::Metadata { .. } - | Command::Style { .. } - | Command::NewTrack - | Command::NewTrackSegment - | Command::Reverse - | Command::Append { .. } - | Command::Replace { .. } - | Command::NewWaypoint { .. } - | Command::MoveWaypoint { .. } - | Command::Crop { .. } - | Command::Split { .. } - | Command::Time - | Command::Merge { .. } - | Command::Extract - | Command::Elevation { .. } - | Command::Clean { .. } - | Command::Undo - | Command::Redo - | Command::Select { .. } - | Command::AddSelect { .. } - | Command::SelectAll => todo!("command not implemented yet"), - } - } - - fn event_loop(&mut self, f: F) -> bool - where - F: FnOnce(&mut StackEntry) -> bool, - { - if let Some(diff) = self.stack.create_and_push_next(f) { - self.selection.select(diff.added[0]); + if let Some(diff) = self.stack.create_and_push_next(|files| { + let mut state = State { + files, + selection: &mut self.selection, + order: &mut self.order, + }; + match command { + Command::New { name } => create_file(&mut state, name), + Command::Load { data } => load_file(&mut state, data), + Command::Metadata { name, desc } => update_metadata(&mut state, name, desc), + Command::Delete + | Command::DeleteAll + | Command::Duplicate + | Command::Style { .. } + | Command::NewTrack + | Command::NewTrackSegment + | Command::Reverse + | Command::Append { .. } + | Command::Replace { .. } + | Command::NewWaypoint { .. } + | Command::MoveWaypoint { .. } + | Command::Crop { .. } + | Command::Split { .. } + | Command::Time + | Command::Merge { .. } + | Command::Extract + | Command::Elevation { .. } + | Command::Clean { .. } + | Command::Undo + | Command::Redo + | Command::Select { .. } + | Command::AddSelect { .. } + | Command::SelectAll => todo!("command not implemented yet"), + } + }) { self.statistics_cache.update(self.stack.current()); self.statistics_buffer.update( &self @@ -117,7 +94,9 @@ mod tests { let mut data = String::new(); let _ = f.read_to_string(&mut data); - engine.load_file(data.as_bytes()); + engine.execute(Command::Load { + data: data.as_bytes(), + }); assert_eq!(engine.statistics_buffer.total_distance.len(), 80); } diff --git a/gpx-rs/engine/src/engine/mod.rs b/gpx-rs/engine/src/engine/mod.rs index b58143013..9c5c7eedd 100644 --- a/gpx-rs/engine/src/engine/mod.rs +++ b/gpx-rs/engine/src/engine/mod.rs @@ -1,11 +1,9 @@ mod command; mod derived; mod engine; -mod selection; mod state; pub use command::*; pub use derived::*; pub use engine::*; -pub use selection::*; pub use state::*; diff --git a/gpx-rs/engine/src/engine/state/mod.rs b/gpx-rs/engine/src/engine/state/mod.rs index b21354ddf..2b91600fb 100644 --- a/gpx-rs/engine/src/engine/state/mod.rs +++ b/gpx-rs/engine/src/engine/state/mod.rs @@ -1,3 +1,13 @@ +mod order; +mod selection; mod stack; +pub use order::*; +pub use selection::*; pub use stack::*; + +pub struct State<'a> { + pub files: &'a mut StackEntry, + pub selection: &'a mut Selection, + pub order: &'a mut FileOrder, +} diff --git a/gpx-rs/engine/src/engine/state/order.rs b/gpx-rs/engine/src/engine/state/order.rs new file mode 100644 index 000000000..5c63c027e --- /dev/null +++ b/gpx-rs/engine/src/engine/state/order.rs @@ -0,0 +1,4 @@ +use crate::FileId; + +#[derive(Debug, Default)] +pub struct FileOrder(pub Vec); diff --git a/gpx-rs/engine/src/engine/selection.rs b/gpx-rs/engine/src/engine/state/selection.rs similarity index 74% rename from gpx-rs/engine/src/engine/selection.rs rename to gpx-rs/engine/src/engine/state/selection.rs index 2f4d6a42e..3cf7dee79 100644 --- a/gpx-rs/engine/src/engine/selection.rs +++ b/gpx-rs/engine/src/engine/state/selection.rs @@ -26,11 +26,3 @@ pub enum Selection { wpt_ids: HashSet, }, } - -impl Selection { - pub fn select(&mut self, id: FileId) { - let mut file_ids = HashSet::default(); - file_ids.insert(id); - *self = Selection::File { file_ids }; - } -} diff --git a/gpx-rs/engine/src/engine/state/stack.rs b/gpx-rs/engine/src/engine/state/stack.rs index b9db13534..0201d6443 100644 --- a/gpx-rs/engine/src/engine/state/stack.rs +++ b/gpx-rs/engine/src/engine/state/stack.rs @@ -18,11 +18,11 @@ impl Stack { pub fn create_and_push_next(&mut self, f: F) -> Option where - F: FnOnce(&mut StackEntry) -> bool, + F: FnOnce(&mut StackEntry) -> Result<(), String>, { self.record_diff(|stack| { let mut next = stack.current().map_or_default(|c| c.clone()); - if f(&mut next) { + if f(&mut next).is_ok() { stack.push(next); } }) @@ -139,7 +139,7 @@ mod tests { let diff = stack.create_and_push_next(|e| { let file = Rc::new(File::default()); e.insert(file.id, file); - true + Ok(()) }); assert!(stack.can_undo());