Skip to main content

firewood_storage/path/
buf.rs

1// Copyright (C) 2025, Ava Labs, Inc. All rights reserved.
2// See the file LICENSE.md for licensing terms.
3
4use crate::{ComponentIter, IntoSplitPath, PathComponent, TriePath};
5
6/// An owned buffer of path components.
7pub type PathBuf = smallvec::SmallVec<[PathComponent; 32]>;
8
9/// A trie path represented as a slice of path components, either borrowed or owned.
10pub enum PartialPath<'a> {
11    /// A borrowed slice of path components.
12    Borrowed(&'a [PathComponent]),
13
14    /// An owned buffer of path components.
15    Owned(PathBuf),
16}
17
18impl std::fmt::Debug for PartialPath<'_> {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        self.display().fmt(f)
21    }
22}
23
24impl std::fmt::Display for PartialPath<'_> {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        self.display().fmt(f)
27    }
28}
29
30impl PartialPath<'_> {
31    /// Returns the path as a slice of path components.
32    #[inline]
33    #[must_use]
34    pub fn as_slice(&self) -> &[PathComponent] {
35        match self {
36            PartialPath::Borrowed(slice) => slice,
37            PartialPath::Owned(buf) => buf.as_slice(),
38        }
39    }
40
41    /// Converts this partial path into an owned path buffer.
42    ///
43    /// If the path is already owned, this is a no-op.
44    /// If the path is borrowed, this allocates a new buffer and copies the
45    /// components into it.
46    #[inline]
47    #[must_use]
48    pub fn into_owned(self) -> PathBuf {
49        match self {
50            PartialPath::Borrowed(slice) => slice.into(),
51            PartialPath::Owned(buf) => buf,
52        }
53    }
54
55    /// Returns true if this partial path is a borrowed slice.
56    #[inline]
57    #[must_use]
58    pub const fn is_borrowed(&self) -> bool {
59        matches!(self, PartialPath::Borrowed(_))
60    }
61
62    /// Returns true if this partial path is an owned buffer.
63    #[inline]
64    #[must_use]
65    pub const fn is_owned(&self) -> bool {
66        matches!(self, PartialPath::Owned(_))
67    }
68
69    /// Acquires a mutable reference to the owned path buffer, converting
70    /// the path to an owned buffer if it is currently a borrowed slice.
71    pub fn to_mut(&mut self) -> &mut PathBuf {
72        if let Self::Borrowed(buf) = self {
73            *self = Self::Owned((*buf).into());
74        }
75
76        if let Self::Owned(buf) = self {
77            buf
78        } else {
79            unreachable!()
80        }
81    }
82}
83
84impl std::ops::Deref for PartialPath<'_> {
85    type Target = [PathComponent];
86
87    fn deref(&self) -> &Self::Target {
88        self.as_slice()
89    }
90}
91
92impl std::borrow::Borrow<[PathComponent]> for PartialPath<'_> {
93    fn borrow(&self) -> &[PathComponent] {
94        self.as_slice()
95    }
96}
97
98impl AsRef<[PathComponent]> for PartialPath<'_> {
99    fn as_ref(&self) -> &[PathComponent] {
100        self.as_slice()
101    }
102}
103
104impl TriePath for PartialPath<'_> {
105    type Components<'a>
106        = ComponentIter<'a>
107    where
108        Self: 'a;
109
110    fn len(&self) -> usize {
111        self.as_slice().len()
112    }
113
114    fn components(&self) -> Self::Components<'_> {
115        self.as_slice().iter().copied()
116    }
117
118    fn as_component_slice(&self) -> PartialPath<'_> {
119        PartialPath::Borrowed(self.as_slice())
120    }
121}
122
123impl<'a> IntoSplitPath for &'a PartialPath<'_> {
124    type Path = &'a [PathComponent];
125
126    #[inline]
127    fn into_split_path(self) -> Self::Path {
128        self
129    }
130}
131
132/// A RAII guard that resets a path buffer to its original length when dropped.
133///
134/// The guard exposes append-only methods to add components to the path buffer
135/// but not remove them without dropping the guard. This ensures that the guard
136/// will always restore the path buffer to its original state when dropped.
137#[must_use]
138pub struct PathGuard<'a> {
139    buf: &'a mut PathBuf,
140    len: usize,
141}
142
143impl std::fmt::Debug for PathGuard<'_> {
144    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145        self.buf.display().fmt(f)
146    }
147}
148
149impl Drop for PathGuard<'_> {
150    fn drop(&mut self) {
151        self.buf.truncate(self.len);
152    }
153}
154
155impl<'a> PathGuard<'a> {
156    /// Creates a new guard that will reset the provided buffer to its current
157    /// length when dropped.
158    pub fn new(buf: &'a mut PathBuf) -> Self {
159        Self {
160            len: buf.len(),
161            buf,
162        }
163    }
164
165    /// Creates a new guard that will reset this guard's buffer to its current
166    /// length when dropped.
167    ///
168    /// This allows for nested guards that can be used in recursive algorithms.
169    pub fn fork(&mut self) -> PathGuard<'_> {
170        PathGuard::new(self.buf)
171    }
172
173    /// Fork this guard and append the given segment to the path buffer.
174    ///
175    /// This is a convenience method that combines `fork` then `extend` in a
176    /// single operation for ergonomic one-liners.
177    ///
178    /// The returned guard will reset the path buffer to its original length,
179    /// before appending the given segment, when dropped.
180    pub fn fork_append(&mut self, path: impl TriePath) -> PathGuard<'_> {
181        let mut fork = self.fork();
182        fork.extend(path.components());
183        fork
184    }
185
186    /// Appends the given component to the path buffer.
187    ///
188    /// This component will be removed when the guard is dropped.
189    pub fn push(&mut self, component: PathComponent) {
190        self.buf.push(component);
191    }
192}
193
194impl std::ops::Deref for PathGuard<'_> {
195    type Target = PathBuf;
196
197    fn deref(&self) -> &Self::Target {
198        self.buf
199    }
200}
201
202impl Extend<PathComponent> for PathGuard<'_> {
203    fn extend<T: IntoIterator<Item = PathComponent>>(&mut self, iter: T) {
204        self.buf.extend(iter);
205    }
206}