mirror of
https://github.com/storytold/wordcraft.git
synced 2026-11-09 04:01:39 +00:00
74 lines
2.3 KiB
Rust
74 lines
2.3 KiB
Rust
use super::{Deserialize, Serialize};
|
|
|
|
/// Which story a position is in.
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Default)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub enum StoryRef {
|
|
#[default]
|
|
Body,
|
|
Part(u32),
|
|
}
|
|
|
|
/// Path to a paragraph: `[block]` in a story, `[block, row, cell, block]` inside a table cell,
|
|
/// three more indexes per nesting level. Lexicographic order = document order.
|
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Default)]
|
|
pub struct Path(pub Vec<u32>);
|
|
|
|
impl Path {
|
|
pub fn top(i: usize) -> Path {
|
|
Path(vec![i as u32])
|
|
}
|
|
/// The block index within its container.
|
|
pub fn last(&self) -> usize {
|
|
self.0.last().copied().unwrap_or(0) as usize
|
|
}
|
|
pub fn with_last(&self, i: usize) -> Path {
|
|
let mut p = self.clone();
|
|
if let Some(l) = p.0.last_mut() {
|
|
*l = i as u32;
|
|
}
|
|
p
|
|
}
|
|
/// The container prefix (everything but the last index).
|
|
pub fn parent(&self) -> &[u32] {
|
|
self.0.split_last().map(|(_, r)| r).unwrap_or(&[])
|
|
}
|
|
/// Nesting depth (0 = top-level story block).
|
|
pub fn depth(&self) -> usize {
|
|
self.0.len().saturating_sub(1) / 3
|
|
}
|
|
/// The enclosing table cell (table block path, row, cell) if any.
|
|
pub fn cell(&self) -> Option<(Path, usize, usize)> {
|
|
if self.0.len() < 4 {
|
|
return None;
|
|
}
|
|
let n = self.0.len();
|
|
let t = Path(self.0.get(..n - 3)?.to_vec());
|
|
Some((t, *self.0.get(n - 3)? as usize, *self.0.get(n - 2)? as usize))
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Display for Path {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
let s: Vec<String> = self.0.iter().map(ToString::to_string).collect();
|
|
write!(f, "{}", s.join("/"))
|
|
}
|
|
}
|
|
|
|
/// A position in a document.
|
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Default)]
|
|
pub struct Pos {
|
|
pub story: StoryRef,
|
|
pub path: Path,
|
|
pub off: usize,
|
|
}
|
|
|
|
impl Pos {
|
|
pub fn new(story: StoryRef, path: Path, off: usize) -> Pos {
|
|
Pos { story, path, off }
|
|
}
|
|
pub fn body(block: usize, off: usize) -> Pos {
|
|
Pos { story: StoryRef::Body, path: Path::top(block), off }
|
|
}
|
|
}
|