Files
designcraft/crates/doc/src/ids.rs
T
0f4f382b0e feat(text): improve editing, snapping, and threading
Add multi-click text selection, pending caret formatting and clipboard support, text-aware colour controls, guide/grid snapping, and frame threading with loaded-port interactions.

Include the layout, drop-cap and IDML updates carried from #83, with safeguards for generated and linked stories, external text references, local IDs, locked frames and atomic colour edits.

Fixes #23.

Co-authored-by: Sean Hammond <sean.hammond@und.edu>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 21:41:46 -06:00

45 lines
1.4 KiB
Rust

//! Stable identifiers. All ids are unique within a document (one counter).
use serde::{Deserialize, Serialize};
macro_rules! id_type {
($name:ident, $prefix:literal) => {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, Default)]
#[serde(transparent)]
pub struct $name(pub u64);
impl std::fmt::Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, concat!($prefix, "{}"), self.0)
}
}
};
}
id_type!(ItemId, "i");
id_type!(StoryId, "s");
id_type!(SpreadId, "sp");
id_type!(PageId, "p");
id_type!(LayerId, "l");
id_type!(AssetId, "a");
/// Fresh story-local id. Keep monotonically increasing ids normally; a loaded MAX id must
/// not make the next insertion overwrite it. In that case choose the first unused gap.
pub(crate) fn next_local_id(ids: impl Iterator<Item = u64> + Clone) -> u64 {
if let Some(next) = ids.clone().max().unwrap_or(0).checked_add(1) {
return next;
}
let mut used: Vec<_> = ids.collect();
used.sort_unstable();
let mut next = 1u64;
for id in used {
if id > next {
break;
}
if id == next {
// An allocated Vec cannot contain every u64 id, so this always finds a gap.
next = next.saturating_add(1);
}
}
next
}