prefix_file_tree/
builder.rs1use std::ops::Range;
2use std::path::PathBuf;
3
4use crate::{constraint, scheme};
5
6#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
7pub enum Error {
8 #[error("Inconsistent prefix part lengths")]
9 InconsistentPrefixPartLengths {
10 prefix_part_lengths_total: usize,
11 length_constraint: constraint::Length,
12 },
13}
14
15#[derive(Clone, Debug, Eq, PartialEq)]
16pub struct TreeBuilder<S> {
17 base: PathBuf,
18 length_constraint: Option<crate::constraint::Length>,
19 extension_constraint: Option<crate::constraint::Extension>,
20 prefix_part_lengths: Option<Vec<usize>>,
21 scheme: S,
22}
23
24impl TreeBuilder<crate::scheme::Identity> {
25 pub(crate) const fn new(base: PathBuf) -> Self {
26 Self {
27 base,
28 length_constraint: None,
29 extension_constraint: None,
30 prefix_part_lengths: None,
31 scheme: scheme::Identity,
32 }
33 }
34}
35
36impl<S> TreeBuilder<S> {
37 pub fn build(self) -> Result<crate::Tree<S>, Error> {
38 let tree = self.into_tree();
39
40 match tree.length_constraint {
41 Some(length_constraint) => {
42 let prefix_part_lengths_total =
43 tree.prefix_part_lengths.iter().copied().sum::<usize>();
44
45 match length_constraint {
46 constraint::Length::Fixed(length) | constraint::Length::Range(_, length) => {
47 if prefix_part_lengths_total <= length {
48 Ok(tree)
49 } else {
50 Err(Error::InconsistentPrefixPartLengths {
51 prefix_part_lengths_total,
52 length_constraint,
53 })
54 }
55 }
56 }
57 }
58 None => Ok(tree),
59 }
60 }
61
62 fn into_tree(self) -> crate::Tree<S> {
64 crate::Tree {
65 base: self.base,
66 length_constraint: self.length_constraint,
67 extension_constraint: self.extension_constraint,
68 prefix_part_lengths: self.prefix_part_lengths.unwrap_or_default(),
69 scheme: self.scheme,
70 }
71 }
72
73 #[must_use]
74 pub fn with_no_extension(self) -> Self {
75 Self {
76 base: self.base,
77 length_constraint: self.length_constraint,
78 extension_constraint: Some(crate::constraint::Extension::None),
79 prefix_part_lengths: self.prefix_part_lengths,
80 scheme: self.scheme,
81 }
82 }
83
84 #[must_use]
85 pub fn with_extension<E: Into<String>>(self, extension: E) -> Self {
86 Self {
87 base: self.base,
88 length_constraint: self.length_constraint,
89 extension_constraint: Some(crate::constraint::Extension::Fixed(extension.into())),
90 prefix_part_lengths: self.prefix_part_lengths,
91 scheme: self.scheme,
92 }
93 }
94
95 #[must_use]
96 pub fn with_any_extension(self) -> Self {
97 Self {
98 base: self.base,
99 length_constraint: self.length_constraint,
100 extension_constraint: Some(crate::constraint::Extension::Any),
101 prefix_part_lengths: self.prefix_part_lengths,
102 scheme: self.scheme,
103 }
104 }
105
106 #[must_use]
107 pub fn with_length(self, length: usize) -> Self {
108 Self {
109 base: self.base,
110 length_constraint: Some(length.into()),
111 extension_constraint: self.extension_constraint,
112 prefix_part_lengths: self.prefix_part_lengths,
113 scheme: self.scheme,
114 }
115 }
116
117 #[must_use]
118 pub fn with_length_range(self, range: Range<usize>) -> Self {
119 Self {
120 base: self.base,
121 length_constraint: Some(range.into()),
122 extension_constraint: self.extension_constraint,
123 prefix_part_lengths: self.prefix_part_lengths,
124 scheme: self.scheme,
125 }
126 }
127
128 #[must_use]
129 pub fn with_prefix_part_lengths<T: AsRef<[usize]>>(self, prefix_part_lengths: T) -> Self {
130 Self {
131 base: self.base,
132 length_constraint: self.length_constraint,
133 extension_constraint: self.extension_constraint,
134 prefix_part_lengths: Some(prefix_part_lengths.as_ref().to_vec()),
135 scheme: self.scheme,
136 }
137 }
138
139 #[must_use]
140 pub fn with_scheme<T: crate::scheme::Scheme>(self, scheme: T) -> TreeBuilder<T> {
141 let length_constraint = T::fixed_length().map_or(self.length_constraint, |fixed_length| {
142 Some(fixed_length.into())
143 });
144
145 TreeBuilder {
146 base: self.base,
147 length_constraint,
148 extension_constraint: self.extension_constraint,
149 prefix_part_lengths: self.prefix_part_lengths,
150 scheme,
151 }
152 }
153}