use crate::{ManagedApiMetadata, Versions};
use camino::Utf8PathBuf;
use std::{fmt, ops::Deref};
pub struct ValidationContext<'a> {
backend: &'a mut dyn ValidationBackend,
}
impl<'a> ValidationContext<'a> {
#[doc(hidden)]
pub fn new(backend: &'a mut dyn ValidationBackend) -> Self {
Self { backend }
}
pub fn ident(&self) -> &ApiIdent {
self.backend.ident()
}
pub fn file_name(&self) -> &ApiSpecFileName {
self.backend.file_name()
}
pub fn versions(&self) -> &Versions {
self.backend.versions()
}
pub fn title(&self) -> &str {
self.backend.title()
}
pub fn metadata(&self) -> &ManagedApiMetadata {
self.backend.metadata()
}
pub fn report_error(&mut self, error: anyhow::Error) {
self.backend.report_error(error);
}
pub fn record_file_contents(
&mut self,
path: impl Into<Utf8PathBuf>,
contents: Vec<u8>,
) {
self.backend.record_file_contents(path.into(), contents);
}
}
#[doc(hidden)]
pub trait ValidationBackend {
fn ident(&self) -> &ApiIdent;
fn file_name(&self) -> &ApiSpecFileName;
fn versions(&self) -> &Versions;
fn title(&self) -> &str;
fn metadata(&self) -> &ManagedApiMetadata;
fn report_error(&mut self, error: anyhow::Error);
fn record_file_contents(&mut self, path: Utf8PathBuf, contents: Vec<u8>);
}
#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
pub struct ApiSpecFileName {
ident: ApiIdent,
kind: ApiSpecFileNameKind,
}
impl fmt::Display for ApiSpecFileName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.path().as_str())
}
}
impl ApiSpecFileName {
#[doc(hidden)]
pub fn new(ident: ApiIdent, kind: ApiSpecFileNameKind) -> ApiSpecFileName {
ApiSpecFileName { ident, kind }
}
pub fn ident(&self) -> &ApiIdent {
&self.ident
}
pub fn kind(&self) -> &ApiSpecFileNameKind {
&self.kind
}
pub fn path(&self) -> Utf8PathBuf {
match &self.kind {
ApiSpecFileNameKind::Lockstep => {
Utf8PathBuf::from_iter([self.basename()])
}
ApiSpecFileNameKind::Versioned { .. } => Utf8PathBuf::from_iter([
self.ident.deref().clone(),
self.basename(),
]),
}
}
pub fn basename(&self) -> String {
match &self.kind {
ApiSpecFileNameKind::Lockstep => format!("{}.json", self.ident),
ApiSpecFileNameKind::Versioned { version, hash } => {
format!("{}-{}-{}.json", self.ident, version, hash)
}
}
}
pub fn hash(&self) -> Option<&str> {
match &self.kind {
ApiSpecFileNameKind::Lockstep => None,
ApiSpecFileNameKind::Versioned { hash, .. } => Some(hash),
}
}
}
#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
pub enum ApiSpecFileNameKind {
Lockstep,
Versioned {
version: semver::Version,
hash: String,
},
}
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct ApiIdent(String);
impl fmt::Debug for ApiIdent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl Deref for ApiIdent {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl fmt::Display for ApiIdent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl From<String> for ApiIdent {
fn from(value: String) -> Self {
Self(value)
}
}
impl ApiIdent {
pub fn versioned_api_latest_symlink(&self) -> String {
format!("{self}-latest.json")
}
pub fn versioned_api_is_latest_symlink(&self, base_name: &str) -> bool {
base_name == self.versioned_api_latest_symlink()
}
}