#![cfg_attr(
not(test),
deny(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::unreachable,
clippy::todo,
clippy::unimplemented,
clippy::indexing_slicing,
clippy::string_slice,
clippy::arithmetic_side_effects,
)
)]
use std::fmt::{self, Display, Write as _};
#[derive(Debug, Clone, Copy)]
enum Segment<'p> {
Root(&'p str),
Key(&'static str),
Name(&'p str),
Index(usize),
Component(&'p str),
}
#[derive(Debug, Clone, Copy)]
pub struct Path<'p> {
parent: Option<&'p Self>,
segment: Segment<'p>,
}
impl<'p> Path<'p> {
pub const fn root(root: &'p str) -> Self {
Self {
parent: None,
segment: Segment::Root(root),
}
}
const fn push(&'p self, segment: Segment<'p>) -> Self {
Self {
parent: Some(self),
segment,
}
}
#[must_use]
pub const fn key(&'p self, key: &'static str) -> Self {
self.push(Segment::Key(key))
}
#[must_use]
pub const fn name(&'p self, name: &'p str) -> Self {
self.push(Segment::Name(name))
}
#[must_use]
pub const fn index(&'p self, index: usize) -> Self {
self.push(Segment::Index(index))
}
#[must_use]
pub const fn component(&'p self, name: &'p str) -> Self {
self.push(Segment::Component(name))
}
}
struct Clipped {
text: String,
max: usize,
}
impl fmt::Write for Clipped {
fn write_str(&mut self, piece: &str) -> fmt::Result {
let room = self.max.saturating_sub(self.text.len());
let mut end = piece.len().min(room);
while !piece.is_char_boundary(end) {
end = end.saturating_sub(1);
}
self.text.push_str(piece.get(..end).unwrap_or_default());
if end < piece.len() {
Err(fmt::Error)
} else {
Ok(())
}
}
}
impl Path<'_> {
#[must_use]
pub fn render_clipped(self, max: usize) -> String {
let mut sink = Clipped {
text: String::new(),
max,
};
let _ = write!(sink, "{self}");
sink.text
}
}
impl Display for Path<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut chain = Vec::new();
let mut current = Some(self);
while let Some(path) = current {
chain.push(path.segment);
current = path.parent;
}
for segment in chain.into_iter().rev() {
match segment {
Segment::Root(text) => f.write_str(text)?,
Segment::Key(text) | Segment::Name(text) => {
f.write_char('.')?;
f.write_str(text)?;
}
Segment::Index(index) => write!(f, "[{index}]")?,
Segment::Component(text) => {
f.write_char('<')?;
f.write_str(text)?;
f.write_char('>')?;
}
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_path_renders_like_the_old_format_strings() {
let root = Path::root("view");
let text = root
.key("children")
.index(2)
.key("props")
.name("href")
.to_string();
assert_eq!(text, "view.children[2].props.href");
assert_eq!(
root.component("Card").key("body").to_string(),
"view<Card>.body"
);
let meta = Path::root("route.meta");
assert_eq!(meta.name("og:title").to_string(), "route.meta.og:title");
}
#[test]
fn a_clipped_path_keeps_at_most_max_bytes() {
let name = "é".repeat(1000);
let root = Path::root("view");
let text = root.name(&name).render_clipped(11);
assert!(text.len() <= 11);
assert!(text.starts_with("view.é"));
assert_eq!(Path::root("view").render_clipped(100), "view");
}
#[test]
fn a_child_path_leaves_its_parent_unchanged() {
let root = Path::root("view");
let _left = root.key("left");
assert_eq!(root.to_string(), "view");
}
#[test]
fn a_path_holds_a_name_by_reference() {
let name = "a".repeat(1 << 20);
let root = Path::root("components");
let path = root.name(&name);
assert!(std::mem::size_of_val(&path) <= 48);
assert_eq!(path.to_string().len(), "components.".len() + name.len());
}
}