rune/indexing/
items.rs

1use core::fmt;
2
3use crate::alloc;
4use crate::alloc::prelude::*;
5use crate::item::ComponentRef;
6use crate::{Item, ItemBuf};
7
8#[derive(Debug)]
9#[non_exhaustive]
10pub(crate) struct MissingLastId;
11
12impl fmt::Display for MissingLastId {
13    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
14        write!(f, "Missing last inserted id into the items stack")
15    }
16}
17
18impl core::error::Error for MissingLastId {}
19
20#[derive(Debug)]
21#[non_exhaustive]
22pub(crate) struct GuardMismatch {
23    actual: usize,
24    expected: usize,
25}
26
27impl fmt::Display for GuardMismatch {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        write!(
30            f,
31            "Guard mismatch when popping items, {} (actual) != {} (expected)",
32            self.actual, self.expected
33        )
34    }
35}
36
37impl core::error::Error for GuardMismatch {}
38
39/// Guard returned.
40#[derive(Debug)]
41#[must_use]
42pub(crate) struct Guard(usize);
43
44/// Manage item paths.
45#[derive(Debug)]
46pub(crate) struct Items {
47    item: ItemBuf,
48}
49
50impl Items {
51    /// Construct a new items manager.
52    pub(crate) fn new(item: &Item) -> alloc::Result<Self> {
53        Ok(Self {
54            item: item.try_to_owned()?,
55        })
56    }
57
58    /// Get the item for the current state of the path.
59    pub(crate) fn item(&self) -> &Item {
60        &self.item
61    }
62
63    /// Push a component and return a guard to it.
64    pub(super) fn push_id(&mut self, id: usize) -> alloc::Result<Guard> {
65        self.item.push(ComponentRef::Id(id))?;
66        Ok(Guard(self.item.as_bytes().len()))
67    }
68
69    /// Push a component and return a guard to it.
70    pub(super) fn push_name(&mut self, name: &str) -> alloc::Result<Guard> {
71        self.item.push(name)?;
72        Ok(Guard(self.item.as_bytes().len()))
73    }
74
75    /// Pop the scope associated with the given guard.
76    pub(super) fn pop(&mut self, Guard(expected): Guard) -> Result<(), GuardMismatch> {
77        if self.item.as_bytes().len() != expected {
78            return Err(GuardMismatch {
79                actual: self.item.as_bytes().len(),
80                expected,
81            });
82        }
83
84        self.item.pop();
85        Ok(())
86    }
87}