prefix_file_tree/
builder.rs

1use 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    /// Internal unvalidated conversion.
63    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}