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 href: 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 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<String>,
@@ -31,7 +31,7 @@ pub struct FileInfo {
pub time: Option<i64>,
}
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Default, Clone, PartialEq)]
pub struct Author {
pub name: Option<String>,
pub email: Option<String>,
+2 -2
View File
@@ -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,
+3 -3
View File
@@ -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<TrackSegment>,
}
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Default, Clone, PartialEq)]
pub struct TrackInfo {
pub name: Option<String>,
pub cmt: Option<String>,
+2
View File
@@ -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::*;
@@ -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 pattern;
pub use applier::*;
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
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<F>(&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);
}
-2
View File
@@ -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::*;
+10
View File
@@ -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,
}
+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>,
},
}
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>
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());