1use crate::seal::ConfigFormat;
8use serde::de::DeserializeOwned;
9use zeroize::Zeroizing;
10
11use crate::error::{Error, code};
12
13pub struct SecretValue {
15 pub(crate) name: String,
16 pub(crate) version: u64,
17 pub(crate) value: Zeroizing<Vec<u8>>,
18}
19
20impl SecretValue {
21 pub fn name(&self) -> &str {
23 &self.name
24 }
25
26 pub fn version(&self) -> u64 {
28 self.version
29 }
30
31 pub fn len(&self) -> usize {
33 self.value.len()
34 }
35
36 pub fn is_empty(&self) -> bool {
38 self.value.is_empty()
39 }
40
41 pub fn expose(&self) -> &[u8] {
43 &self.value
44 }
45}
46
47impl std::fmt::Debug for SecretValue {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 f.debug_struct("SecretValue")
50 .field("name", &self.name)
51 .field("version", &self.version)
52 .field("size", &self.value.len())
53 .finish()
54 }
55}
56
57#[derive(Debug)]
59pub struct SecretSet {
60 pub(crate) values: Vec<SecretValue>,
61}
62
63impl SecretSet {
64 pub fn get(&self, name: &str) -> Option<&SecretValue> {
66 self.values.iter().find(|v| v.name == name)
67 }
68
69 pub fn iter(&self) -> impl Iterator<Item = &SecretValue> {
71 self.values.iter()
72 }
73
74 pub fn len(&self) -> usize {
76 self.values.len()
77 }
78
79 pub fn is_empty(&self) -> bool {
81 self.values.is_empty()
82 }
83}
84
85impl IntoIterator for SecretSet {
86 type Item = SecretValue;
87 type IntoIter = std::vec::IntoIter<SecretValue>;
88
89 fn into_iter(self) -> Self::IntoIter {
90 self.values.into_iter()
91 }
92}
93
94pub struct ConfigDocument {
96 pub(crate) name: String,
97 pub(crate) version: u64,
98 pub(crate) format: ConfigFormat,
99 pub(crate) body: Zeroizing<Vec<u8>>,
100}
101
102impl ConfigDocument {
103 pub fn name(&self) -> &str {
105 &self.name
106 }
107
108 pub fn version(&self) -> u64 {
110 self.version
111 }
112
113 pub fn format(&self) -> ConfigFormat {
115 self.format
116 }
117
118 pub fn len(&self) -> usize {
120 self.body.len()
121 }
122
123 pub fn is_empty(&self) -> bool {
125 self.body.is_empty()
126 }
127
128 pub fn expose(&self) -> &[u8] {
131 &self.body
132 }
133
134 pub fn text(&self) -> Result<&str, Error> {
137 std::str::from_utf8(&self.body)
138 .map_err(|_| Error::local(code::NOT_TEXT, format!("config {} is not UTF-8", self.name)))
139 }
140
141 pub fn deserialize<T: DeserializeOwned>(&self) -> Result<T, Error> {
144 match self.format {
145 ConfigFormat::Toml => toml::from_str::<T>(self.text()?).map_err(|e| {
146 let line = e.span().map_or(1, |s| {
147 self.body[..s.start.min(self.body.len())]
148 .iter()
149 .filter(|b| **b == b'\n')
150 .count()
151 + 1
152 });
153 Error::local(
154 code::INVALID_CONFIG,
155 format!(
156 "config {}, line {line}: the toml does not fit the type",
157 self.name
158 ),
159 )
160 }),
161 ConfigFormat::Json => serde_json::from_slice::<T>(&self.body).map_err(|e| {
162 Error::local(
163 code::INVALID_CONFIG,
164 format!(
165 "config {}, line {}: the json does not fit the type",
166 self.name,
167 e.line()
168 ),
169 )
170 }),
171 other => Err(Error::local(
172 code::UNSUPPORTED_FORMAT,
173 format!(
174 "config {} is {other}; only toml and json deserialize, so parse it from expose()",
175 self.name
176 ),
177 )),
178 }
179 }
180}
181
182impl std::fmt::Debug for ConfigDocument {
183 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184 f.debug_struct("ConfigDocument")
185 .field("name", &self.name)
186 .field("version", &self.version)
187 .field("format", &self.format)
188 .field("size", &self.body.len())
189 .finish()
190 }
191}
192
193#[derive(Debug, Clone, Copy, PartialEq, Eq)]
196#[non_exhaustive]
197pub enum Expect {
198 Version(u64),
200 Absent,
202}
203
204pub struct NewConfig {
206 pub(crate) format: ConfigFormat,
207 pub(crate) body: Zeroizing<Vec<u8>>,
208 pub(crate) allow_literals: bool,
209 pub(crate) expect: Option<Expect>,
210}
211
212impl NewConfig {
213 pub fn new(format: ConfigFormat, body: impl Into<Vec<u8>>) -> NewConfig {
215 NewConfig {
216 format,
217 body: Zeroizing::new(body.into()),
218 allow_literals: false,
219 expect: None,
220 }
221 }
222
223 pub fn toml(body: impl Into<Vec<u8>>) -> NewConfig {
225 NewConfig::new(ConfigFormat::Toml, body)
226 }
227
228 pub fn json(body: impl Into<Vec<u8>>) -> NewConfig {
230 NewConfig::new(ConfigFormat::Json, body)
231 }
232
233 pub fn yaml(body: impl Into<Vec<u8>>) -> NewConfig {
235 NewConfig::new(ConfigFormat::Yaml, body)
236 }
237
238 pub fn text(body: impl Into<Vec<u8>>) -> NewConfig {
240 NewConfig::new(ConfigFormat::Text, body)
241 }
242
243 pub fn allow_literals(mut self) -> NewConfig {
245 self.allow_literals = true;
246 self
247 }
248
249 pub fn expect_version(mut self, version: u64) -> NewConfig {
253 self.expect = Some(Expect::Version(version));
254 self
255 }
256
257 pub fn expect_absent(mut self) -> NewConfig {
259 self.expect = Some(Expect::Absent);
260 self
261 }
262}
263
264impl std::fmt::Debug for NewConfig {
265 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
266 f.debug_struct("NewConfig")
267 .field("format", &self.format)
268 .field("size", &self.body.len())
269 .field("allow_literals", &self.allow_literals)
270 .field("expect", &self.expect)
271 .finish()
272 }
273}
274
275#[derive(Debug, Clone, PartialEq, Eq)]
277#[non_exhaustive]
278pub struct Entry {
279 pub name: String,
281 pub version: u64,
283 pub updated_at: i64,
286 pub size: u32,
288 pub deleted: bool,
291}
292
293#[derive(Debug, Clone, Copy, PartialEq, Eq)]
295#[non_exhaustive]
296pub struct Expiry {
297 pub token_expires_at: Option<i64>,
299 pub vault_expires_at: Option<i64>,
301}