This commit is contained in:
vcoppe
2026-10-03 15:33:44 +02:00
parent 888f41e979
commit 2abf46c747
16 changed files with 151 additions and 94 deletions
+1
View File
@@ -0,0 +1 @@
+3
View File
@@ -0,0 +1,3 @@
mod file;
pub use file::*;
+1 -1
View File
@@ -1,4 +1,4 @@
#[derive(Debug, Default, PartialEq, Eq)] #[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct Link { pub struct Link {
pub href: String, pub href: String,
pub text: Option<String>, pub text: Option<String>,
+3 -3
View File
@@ -13,7 +13,7 @@ impl Default for FileId {
} }
} }
#[derive(Debug, Default, PartialEq)] #[derive(Debug, Default, Clone, PartialEq)]
pub struct File { pub struct File {
pub id: FileId, pub id: FileId,
pub info: FileInfo, pub info: FileInfo,
@@ -22,7 +22,7 @@ pub struct File {
// TODO routes // TODO routes
} }
#[derive(Debug, Default, PartialEq)] #[derive(Debug, Default, Clone, PartialEq)]
pub struct FileInfo { pub struct FileInfo {
pub name: String, pub name: String,
pub desc: Option<String>, pub desc: Option<String>,
@@ -31,7 +31,7 @@ pub struct FileInfo {
pub time: Option<i64>, pub time: Option<i64>,
} }
#[derive(Debug, Default, PartialEq)] #[derive(Debug, Default, Clone, PartialEq)]
pub struct Author { pub struct Author {
pub name: Option<String>, pub name: Option<String>,
pub email: Option<String>, pub email: Option<String>,
+2 -2
View File
@@ -4,7 +4,7 @@ use uuid::Uuid;
use crate::{Trackpoint, TrackpointChunk}; use crate::{Trackpoint, TrackpointChunk};
#[derive(Debug, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TrackSegmentId(Uuid); pub struct TrackSegmentId(Uuid);
impl Default for TrackSegmentId { 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 struct TrackSegment {
pub id: TrackSegmentId, pub id: TrackSegmentId,
pub rev_id: TrackSegmentRevisionId, pub rev_id: TrackSegmentRevisionId,
+3 -3
View File
@@ -2,7 +2,7 @@ use uuid::Uuid;
use crate::{Link, TrackSegment}; use crate::{Link, TrackSegment};
#[derive(Debug, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TrackId(Uuid); pub struct TrackId(Uuid);
impl Default for TrackId { 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 struct Track {
pub id: TrackId, pub id: TrackId,
pub info: TrackInfo, pub info: TrackInfo,
pub trkseg: Vec<TrackSegment>, pub trkseg: Vec<TrackSegment>,
} }
#[derive(Debug, Default, PartialEq)] #[derive(Debug, Default, Clone, PartialEq)]
pub struct TrackInfo { pub struct TrackInfo {
pub name: Option<String>, pub name: Option<String>,
pub cmt: Option<String>, pub cmt: Option<String>,
+2
View File
@@ -1,9 +1,11 @@
mod action;
mod algorithm; mod algorithm;
pub(crate) mod gpx; pub(crate) mod gpx;
mod io; mod io;
pub(crate) mod statistics; pub(crate) mod statistics;
mod utils; mod utils;
pub use action::*;
pub use algorithm::*; pub use algorithm::*;
pub use gpx::*; pub use gpx::*;
pub use io::*; pub use io::*;
@@ -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(())
}
+4
View File
@@ -1,3 +1,7 @@
mod applier;
mod command; mod command;
mod pattern;
pub use applier::*;
pub use command::*; pub use command::*;
pub use pattern::*;
@@ -0,0 +1,38 @@
use std::{
collections::{HashSet, hash_map::Entry},
rc::Rc,
};
use crate::{File, FileId, Selection, State};
pub fn produce<F>(state: &mut State, f: F)
where
F: FnOnce(&State) -> Vec<File>,
{
*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<FileId> = Default::default();
file_ids.insert(file_id);
*state.selection = Selection::File { file_ids };
}
}
}
pub fn update_each_selected_file<F>(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);
}
}
}
}
+51 -72
View File
@@ -1,17 +1,15 @@
// TODO license in every file // TODO license in every file
use std::rc::Rc;
use uuid::Uuid;
use crate::{ 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)] #[derive(Debug, Default)]
pub struct Engine { pub struct Engine {
stack: Stack, stack: Stack,
selection: Selection, selection: Selection,
order: FileOrder,
statistics_cache: StatisticsCache, statistics_cache: StatisticsCache,
statistics_buffer: StatisticsBuffer, statistics_buffer: StatisticsBuffer,
} }
@@ -21,75 +19,54 @@ impl Engine {
&self.statistics_buffer &self.statistics_buffer
} }
pub fn create_file(&mut self, name: &str) -> bool { // pub fn delete_file(&mut self, id: &[u8]) -> bool {
self.event_loop(|entry| { // self.event_loop(|entry| {
let mut file = File::default(); // if let Ok(id) = Uuid::from_slice(id) {
file.info.name = name.to_string(); // let id = FileId(id);
entry.insert(file.id, Rc::new(file)); // entry.remove(&id).is_some()
true // } else {
}) // false
} // }
// })
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
}
})
}
/// Single entry point for every user action coming from the frontend. /// Single entry point for every user action coming from the frontend.
pub fn execute(&mut self, command: Command) -> bool { pub fn execute(&mut self, command: Command) -> bool {
match command { if let Some(diff) = self.stack.create_and_push_next(|files| {
Command::New { name } => self.create_file(name), let mut state = State {
Command::Load { data } => self.load_file(data), files,
Command::Delete selection: &mut self.selection,
| Command::DeleteAll order: &mut self.order,
| Command::Duplicate };
| Command::Metadata { .. } match command {
| Command::Style { .. } Command::New { name } => create_file(&mut state, name),
| Command::NewTrack Command::Load { data } => load_file(&mut state, data),
| Command::NewTrackSegment Command::Metadata { name, desc } => update_metadata(&mut state, name, desc),
| Command::Reverse Command::Delete
| Command::Append { .. } | Command::DeleteAll
| Command::Replace { .. } | Command::Duplicate
| Command::NewWaypoint { .. } | Command::Style { .. }
| Command::MoveWaypoint { .. } | Command::NewTrack
| Command::Crop { .. } | Command::NewTrackSegment
| Command::Split { .. } | Command::Reverse
| Command::Time | Command::Append { .. }
| Command::Merge { .. } | Command::Replace { .. }
| Command::Extract | Command::NewWaypoint { .. }
| Command::Elevation { .. } | Command::MoveWaypoint { .. }
| Command::Clean { .. } | Command::Crop { .. }
| Command::Undo | Command::Split { .. }
| Command::Redo | Command::Time
| Command::Select { .. } | Command::Merge { .. }
| Command::AddSelect { .. } | Command::Extract
| Command::SelectAll => todo!("command not implemented yet"), | Command::Elevation { .. }
} | Command::Clean { .. }
} | Command::Undo
| Command::Redo
fn event_loop<F>(&mut self, f: F) -> bool | Command::Select { .. }
where | Command::AddSelect { .. }
F: FnOnce(&mut StackEntry) -> bool, | Command::SelectAll => todo!("command not implemented yet"),
{ }
if let Some(diff) = self.stack.create_and_push_next(f) { }) {
self.selection.select(diff.added[0]);
self.statistics_cache.update(self.stack.current()); self.statistics_cache.update(self.stack.current());
self.statistics_buffer.update( self.statistics_buffer.update(
&self &self
@@ -117,7 +94,9 @@ mod tests {
let mut data = String::new(); let mut data = String::new();
let _ = f.read_to_string(&mut data); 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); assert_eq!(engine.statistics_buffer.total_distance.len(), 80);
} }
-2
View File
@@ -1,11 +1,9 @@
mod command; mod command;
mod derived; mod derived;
mod engine; mod engine;
mod selection;
mod state; mod state;
pub use command::*; pub use command::*;
pub use derived::*; pub use derived::*;
pub use engine::*; pub use engine::*;
pub use selection::*;
pub use state::*; pub use state::*;
+10
View File
@@ -1,3 +1,13 @@
mod order;
mod selection;
mod stack; mod stack;
pub use order::*;
pub use selection::*;
pub use stack::*; pub use stack::*;
pub struct State<'a> {
pub files: &'a mut StackEntry,
pub selection: &'a mut Selection,
pub order: &'a mut FileOrder,
}
+4
View File
@@ -0,0 +1,4 @@
use crate::FileId;
#[derive(Debug, Default)]
pub struct FileOrder(pub Vec<FileId>);
@@ -26,11 +26,3 @@ pub enum Selection {
wpt_ids: HashSet<WaypointId>, wpt_ids: HashSet<WaypointId>,
}, },
} }
impl Selection {
pub fn select(&mut self, id: FileId) {
let mut file_ids = HashSet::default();
file_ids.insert(id);
*self = Selection::File { file_ids };
}
}
+3 -3
View File
@@ -18,11 +18,11 @@ impl Stack {
pub fn create_and_push_next<F>(&mut self, f: F) -> Option<Diff> pub fn create_and_push_next<F>(&mut self, f: F) -> Option<Diff>
where where
F: FnOnce(&mut StackEntry) -> bool, F: FnOnce(&mut StackEntry) -> Result<(), String>,
{ {
self.record_diff(|stack| { self.record_diff(|stack| {
let mut next = stack.current().map_or_default(|c| c.clone()); let mut next = stack.current().map_or_default(|c| c.clone());
if f(&mut next) { if f(&mut next).is_ok() {
stack.push(next); stack.push(next);
} }
}) })
@@ -139,7 +139,7 @@ mod tests {
let diff = stack.create_and_push_next(|e| { let diff = stack.create_and_push_next(|e| {
let file = Rc::new(File::default()); let file = Rc::new(File::default());
e.insert(file.id, file); e.insert(file.id, file);
true Ok(())
}); });
assert!(stack.can_undo()); assert!(stack.can_undo());