dropshot_api_manager_types/
validation.rs1use crate::{ManagedApiMetadata, Versions};
4use camino::Utf8PathBuf;
5use std::{fmt, ops::Deref};
6
7pub struct ValidationContext<'a> {
9 backend: &'a mut dyn ValidationBackend,
10}
11
12impl<'a> ValidationContext<'a> {
13 #[doc(hidden)]
15 pub fn new(backend: &'a mut dyn ValidationBackend) -> Self {
16 Self { backend }
17 }
18
19 pub fn ident(&self) -> &ApiIdent {
24 self.backend.ident()
25 }
26
27 pub fn file_name(&self) -> &ApiSpecFileName {
32 self.backend.file_name()
33 }
34
35 pub fn is_latest(&self) -> bool {
42 self.backend.is_latest()
43 }
44
45 pub fn is_blessed(&self) -> Option<bool> {
48 self.backend.is_blessed()
49 }
50
51 pub fn versions(&self) -> &Versions {
53 self.backend.versions()
54 }
55
56 pub fn title(&self) -> &str {
58 self.backend.title()
59 }
60
61 pub fn metadata(&self) -> &ManagedApiMetadata {
63 self.backend.metadata()
64 }
65
66 pub fn report_error(&mut self, error: anyhow::Error) {
68 self.backend.report_error(error);
69 }
70
71 pub fn record_file_contents(
79 &mut self,
80 path: impl Into<Utf8PathBuf>,
81 contents: Vec<u8>,
82 ) {
83 self.backend.record_file_contents(path.into(), contents);
84 }
85}
86
87#[doc(hidden)]
91pub trait ValidationBackend {
92 fn ident(&self) -> &ApiIdent;
93 fn file_name(&self) -> &ApiSpecFileName;
94 fn versions(&self) -> &Versions;
95 fn is_latest(&self) -> bool;
96 fn is_blessed(&self) -> Option<bool>;
97 fn title(&self) -> &str;
98 fn metadata(&self) -> &ManagedApiMetadata;
99 fn report_error(&mut self, error: anyhow::Error);
100 fn record_file_contents(&mut self, path: Utf8PathBuf, contents: Vec<u8>);
101}
102
103#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
108pub struct LockstepApiSpecFileName {
109 ident: ApiIdent,
110}
111
112impl LockstepApiSpecFileName {
113 pub fn new(ident: ApiIdent) -> Self {
115 Self { ident }
116 }
117
118 pub fn ident(&self) -> &ApiIdent {
120 &self.ident
121 }
122
123 pub fn path(&self) -> Utf8PathBuf {
126 Utf8PathBuf::from(self.basename())
127 }
128
129 pub fn basename(&self) -> String {
131 format!("{}.json", self.ident)
132 }
133}
134
135impl fmt::Display for LockstepApiSpecFileName {
136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137 f.write_str(self.path().as_str())
138 }
139}
140
141#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
147pub struct VersionedApiSpecFileName {
148 ident: ApiIdent,
149 version: semver::Version,
150 hash: String,
151 kind: VersionedApiSpecKind,
152}
153
154impl VersionedApiSpecFileName {
155 pub fn new(
157 ident: ApiIdent,
158 version: semver::Version,
159 hash: String,
160 ) -> Self {
161 Self { ident, version, hash, kind: VersionedApiSpecKind::Json }
162 }
163
164 pub fn new_git_stub(
166 ident: ApiIdent,
167 version: semver::Version,
168 hash: String,
169 ) -> Self {
170 Self { ident, version, hash, kind: VersionedApiSpecKind::GitStub }
171 }
172
173 pub fn ident(&self) -> &ApiIdent {
175 &self.ident
176 }
177
178 pub fn version(&self) -> &semver::Version {
180 &self.version
181 }
182
183 pub fn hash(&self) -> &str {
185 &self.hash
186 }
187
188 pub fn kind(&self) -> VersionedApiSpecKind {
190 self.kind
191 }
192
193 pub fn is_git_stub(&self) -> bool {
195 self.kind == VersionedApiSpecKind::GitStub
196 }
197
198 pub fn path(&self) -> Utf8PathBuf {
201 Utf8PathBuf::from_iter([self.ident.deref().clone(), self.basename()])
202 }
203
204 pub fn basename(&self) -> String {
206 match self.kind {
207 VersionedApiSpecKind::Json => {
208 format!("{}-{}-{}.json", self.ident, self.version, self.hash)
209 }
210 VersionedApiSpecKind::GitStub => {
211 format!(
212 "{}-{}-{}.json.gitstub",
213 self.ident, self.version, self.hash
214 )
215 }
216 }
217 }
218
219 pub fn to_json(&self) -> Self {
223 Self {
224 ident: self.ident.clone(),
225 version: self.version.clone(),
226 hash: self.hash.clone(),
227 kind: VersionedApiSpecKind::Json,
228 }
229 }
230
231 pub fn to_git_stub(&self) -> Self {
235 Self {
236 ident: self.ident.clone(),
237 version: self.version.clone(),
238 hash: self.hash.clone(),
239 kind: VersionedApiSpecKind::GitStub,
240 }
241 }
242
243 pub fn git_stub_basename(&self) -> String {
248 match self.kind {
249 VersionedApiSpecKind::GitStub => self.basename(),
250 VersionedApiSpecKind::Json => {
251 format!("{}.gitstub", self.basename())
252 }
253 }
254 }
255
256 pub fn json_basename(&self) -> String {
261 match self.kind {
262 VersionedApiSpecKind::Json => self.basename(),
263 VersionedApiSpecKind::GitStub => {
264 format!("{}-{}-{}.json", self.ident, self.version, self.hash)
265 }
266 }
267 }
268}
269
270impl fmt::Display for VersionedApiSpecFileName {
271 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
272 f.write_str(self.path().as_str())
273 }
274}
275
276#[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq)]
278pub enum VersionedApiSpecKind {
279 Json,
281 GitStub,
287}
288
289#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
292pub enum ApiSpecFileName {
293 Lockstep(LockstepApiSpecFileName),
295 Versioned(VersionedApiSpecFileName),
297}
298
299impl fmt::Display for ApiSpecFileName {
300 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
301 f.write_str(self.path().as_str())
302 }
303}
304
305impl ApiSpecFileName {
306 pub fn ident(&self) -> &ApiIdent {
308 match self {
309 ApiSpecFileName::Lockstep(l) => l.ident(),
310 ApiSpecFileName::Versioned(v) => v.ident(),
311 }
312 }
313
314 pub fn path(&self) -> Utf8PathBuf {
317 match self {
318 ApiSpecFileName::Lockstep(l) => l.path(),
319 ApiSpecFileName::Versioned(v) => v.path(),
320 }
321 }
322
323 pub fn basename(&self) -> String {
325 match self {
326 ApiSpecFileName::Lockstep(l) => l.basename(),
327 ApiSpecFileName::Versioned(v) => v.basename(),
328 }
329 }
330
331 pub fn version(&self) -> Option<&semver::Version> {
333 match self {
334 ApiSpecFileName::Lockstep(_) => None,
335 ApiSpecFileName::Versioned(v) => Some(v.version()),
336 }
337 }
338
339 pub fn hash(&self) -> Option<&str> {
341 match self {
342 ApiSpecFileName::Lockstep(_) => None,
343 ApiSpecFileName::Versioned(v) => Some(v.hash()),
344 }
345 }
346
347 pub fn is_git_stub(&self) -> bool {
349 match self {
350 ApiSpecFileName::Lockstep(_) => false,
351 ApiSpecFileName::Versioned(v) => v.is_git_stub(),
352 }
353 }
354
355 pub fn versioned_kind(&self) -> Option<VersionedApiSpecKind> {
357 match self {
358 ApiSpecFileName::Lockstep(_) => None,
359 ApiSpecFileName::Versioned(v) => Some(v.kind()),
360 }
361 }
362
363 pub fn to_json_filename(&self) -> ApiSpecFileName {
367 match self {
368 ApiSpecFileName::Lockstep(_) => self.clone(),
369 ApiSpecFileName::Versioned(v) => {
370 ApiSpecFileName::Versioned(v.to_json())
371 }
372 }
373 }
374
375 pub fn to_git_stub_filename(&self) -> ApiSpecFileName {
381 match self {
382 ApiSpecFileName::Lockstep(_) => self.clone(),
383 ApiSpecFileName::Versioned(v) => {
384 ApiSpecFileName::Versioned(v.to_git_stub())
385 }
386 }
387 }
388
389 pub fn git_stub_basename(&self) -> String {
396 match self {
397 ApiSpecFileName::Lockstep(l) => l.basename(),
398 ApiSpecFileName::Versioned(v) => v.git_stub_basename(),
399 }
400 }
401
402 pub fn json_basename(&self) -> String {
408 match self {
409 ApiSpecFileName::Lockstep(l) => l.basename(),
410 ApiSpecFileName::Versioned(v) => v.json_basename(),
411 }
412 }
413
414 pub fn as_versioned(&self) -> Option<&VersionedApiSpecFileName> {
417 match self {
418 ApiSpecFileName::Lockstep(_) => None,
419 ApiSpecFileName::Versioned(v) => Some(v),
420 }
421 }
422
423 pub fn into_versioned(self) -> Option<VersionedApiSpecFileName> {
426 match self {
427 ApiSpecFileName::Lockstep(_) => None,
428 ApiSpecFileName::Versioned(v) => Some(v),
429 }
430 }
431
432 pub fn as_lockstep(&self) -> Option<&LockstepApiSpecFileName> {
435 match self {
436 ApiSpecFileName::Lockstep(l) => Some(l),
437 ApiSpecFileName::Versioned(_) => None,
438 }
439 }
440
441 pub fn into_lockstep(self) -> Option<LockstepApiSpecFileName> {
444 match self {
445 ApiSpecFileName::Lockstep(l) => Some(l),
446 ApiSpecFileName::Versioned(_) => None,
447 }
448 }
449}
450
451impl From<LockstepApiSpecFileName> for ApiSpecFileName {
452 fn from(l: LockstepApiSpecFileName) -> Self {
453 ApiSpecFileName::Lockstep(l)
454 }
455}
456
457impl From<VersionedApiSpecFileName> for ApiSpecFileName {
458 fn from(v: VersionedApiSpecFileName) -> Self {
459 ApiSpecFileName::Versioned(v)
460 }
461}
462
463#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
465pub struct ApiIdent(String);
466
467impl fmt::Debug for ApiIdent {
468 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
469 self.0.fmt(f)
470 }
471}
472
473impl Deref for ApiIdent {
474 type Target = String;
475
476 fn deref(&self) -> &Self::Target {
477 &self.0
478 }
479}
480
481impl fmt::Display for ApiIdent {
482 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
483 self.0.fmt(f)
484 }
485}
486
487impl<S: Into<String>> From<S> for ApiIdent {
488 fn from(value: S) -> Self {
489 Self(value.into())
490 }
491}
492
493impl ApiIdent {
494 pub fn versioned_api_latest_symlink(&self) -> String {
496 format!("{self}-latest.json")
497 }
498
499 pub fn versioned_api_is_latest_symlink(&self, base_name: &str) -> bool {
502 base_name == self.versioned_api_latest_symlink()
503 }
504}