mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2026-10-04 12:58:23 +00:00
progress
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
mod file;
|
||||||
|
|
||||||
|
pub use 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>,
|
||||||
|
|||||||
@@ -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>,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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>,
|
||||||
|
|||||||
@@ -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(())
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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::*;
|
||||||
|
|||||||
@@ -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,
|
||||||
|
}
|
||||||
|
|||||||
@@ -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 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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());
|
||||||
|
|||||||
Reference in New Issue
Block a user