2026-10-01 18:42:31 +02:00
|
|
|
use uuid::Uuid;
|
|
|
|
|
|
2026-10-03 10:03:22 +02:00
|
|
|
use crate::{Trackpoint, Waypoint};
|
2026-09-23 19:53:39 +02:00
|
|
|
|
2026-10-07 18:22:30 +02:00
|
|
|
/// A run of items, shared between the successive versions of a [`crate::Chunked`] list: a chunk
|
|
|
|
|
/// that is not touched by an edit is not copied.
|
|
|
|
|
///
|
|
|
|
|
/// Chunks are equal when they have the same identity, which is cheap to compare and changes
|
|
|
|
|
/// whenever the content does (a modified chunk is a new one).
|
|
|
|
|
pub trait Chunk {
|
|
|
|
|
type Item: Clone;
|
|
|
|
|
|
|
|
|
|
/// Maximum number of items of a chunk.
|
|
|
|
|
const MAX_SIZE: usize;
|
|
|
|
|
|
|
|
|
|
/// A chunk with a new identity, holding `items`.
|
|
|
|
|
fn new(items: Vec<Self::Item>) -> Self;
|
|
|
|
|
|
|
|
|
|
fn items(&self) -> &Vec<Self::Item>;
|
|
|
|
|
|
|
|
|
|
fn items_mut(&mut self) -> &mut Vec<Self::Item>;
|
|
|
|
|
|
|
|
|
|
fn is_full(&self) -> bool {
|
|
|
|
|
self.items().len() >= Self::MAX_SIZE
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-03 20:54:05 +02:00
|
|
|
const MAX_TRKPT_CHUNK_SIZE: usize = 4096;
|
2026-09-23 19:53:39 +02:00
|
|
|
|
2026-10-02 18:06:45 +02:00
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
|
|
|
pub struct TrackpointChunkId(Uuid);
|
|
|
|
|
|
|
|
|
|
impl Default for TrackpointChunkId {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self(Uuid::new_v4())
|
|
|
|
|
}
|
2026-09-23 19:53:39 +02:00
|
|
|
}
|
|
|
|
|
|
2026-10-02 18:06:45 +02:00
|
|
|
#[derive(Debug, Default)]
|
|
|
|
|
pub struct TrackpointChunk {
|
|
|
|
|
pub id: TrackpointChunkId,
|
|
|
|
|
pub trkpt: Vec<Trackpoint>,
|
2026-10-01 18:42:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl PartialEq for TrackpointChunk {
|
|
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
|
self.id == other.id
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-07 18:22:30 +02:00
|
|
|
impl Chunk for TrackpointChunk {
|
|
|
|
|
type Item = Trackpoint;
|
|
|
|
|
const MAX_SIZE: usize = MAX_TRKPT_CHUNK_SIZE;
|
|
|
|
|
|
|
|
|
|
fn new(trkpt: Vec<Trackpoint>) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
trkpt,
|
|
|
|
|
..Default::default()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn items(&self) -> &Vec<Trackpoint> {
|
|
|
|
|
&self.trkpt
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn items_mut(&mut self) -> &mut Vec<Trackpoint> {
|
|
|
|
|
&mut self.trkpt
|
2026-09-23 19:53:39 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-03 19:17:58 +02:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
2026-10-02 18:06:45 +02:00
|
|
|
pub struct WaypointChunkId(Uuid);
|
2026-09-23 19:53:39 +02:00
|
|
|
|
2026-10-02 18:06:45 +02:00
|
|
|
impl Default for WaypointChunkId {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self(Uuid::new_v4())
|
|
|
|
|
}
|
2026-09-23 19:53:39 +02:00
|
|
|
}
|
|
|
|
|
|
2026-10-03 20:54:05 +02:00
|
|
|
const MAX_WPT_CHUNK_SIZE: usize = 128;
|
2026-10-02 18:06:45 +02:00
|
|
|
|
|
|
|
|
#[derive(Debug, Default)]
|
|
|
|
|
pub struct WaypointChunk {
|
|
|
|
|
pub id: WaypointChunkId,
|
|
|
|
|
pub wpt: Vec<Waypoint>,
|
2026-10-01 18:42:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl PartialEq for WaypointChunk {
|
|
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
|
self.id == other.id
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-07 18:22:30 +02:00
|
|
|
impl Chunk for WaypointChunk {
|
|
|
|
|
type Item = Waypoint;
|
|
|
|
|
const MAX_SIZE: usize = MAX_WPT_CHUNK_SIZE;
|
|
|
|
|
|
|
|
|
|
fn new(wpt: Vec<Waypoint>) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
wpt,
|
|
|
|
|
..Default::default()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn items(&self) -> &Vec<Waypoint> {
|
|
|
|
|
&self.wpt
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn items_mut(&mut self) -> &mut Vec<Waypoint> {
|
|
|
|
|
&mut self.wpt
|
2026-09-23 19:53:39 +02:00
|
|
|
}
|
|
|
|
|
}
|
2026-10-03 16:16:47 +02:00
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_trackpoint_chunk_full() {
|
|
|
|
|
let mut chunk = TrackpointChunk::default();
|
|
|
|
|
assert!(!chunk.is_full());
|
|
|
|
|
for _ in 0..MAX_TRKPT_CHUNK_SIZE - 1 {
|
|
|
|
|
chunk.trkpt.push(Trackpoint::default());
|
|
|
|
|
}
|
|
|
|
|
assert!(!chunk.is_full());
|
|
|
|
|
chunk.trkpt.push(Trackpoint::default());
|
|
|
|
|
assert!(chunk.is_full());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_waypoint_chunk_full() {
|
|
|
|
|
let mut chunk = WaypointChunk::default();
|
|
|
|
|
assert!(!chunk.is_full());
|
|
|
|
|
for _ in 0..MAX_WPT_CHUNK_SIZE {
|
|
|
|
|
chunk.wpt.push(Waypoint::default());
|
|
|
|
|
}
|
|
|
|
|
assert!(chunk.is_full());
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-07 18:22:30 +02:00
|
|
|
#[test]
|
|
|
|
|
fn test_new_chunks_have_their_own_identity() {
|
|
|
|
|
let a = TrackpointChunk::new(vec![Trackpoint::default()]);
|
|
|
|
|
let b = TrackpointChunk::new(vec![Trackpoint::default()]);
|
|
|
|
|
assert_ne!(a, b);
|
|
|
|
|
assert_eq!(a.items().len(), 1);
|
|
|
|
|
assert_ne!(WaypointChunk::new(vec![]), WaypointChunk::new(vec![]));
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-03 16:16:47 +02:00
|
|
|
#[test]
|
|
|
|
|
fn test_chunk_equality_is_by_id() {
|
|
|
|
|
let a = TrackpointChunk::default();
|
|
|
|
|
let b = TrackpointChunk::default();
|
|
|
|
|
assert_ne!(a, b);
|
2026-10-03 20:54:05 +02:00
|
|
|
let c = TrackpointChunk {
|
|
|
|
|
id: TrackpointChunkId(a.id.0),
|
|
|
|
|
trkpt: vec![Trackpoint::default()],
|
|
|
|
|
};
|
2026-10-03 16:16:47 +02:00
|
|
|
assert_eq!(a, c);
|
|
|
|
|
}
|
|
|
|
|
}
|