use std::{fmt, path::PathBuf, str::FromStr, sync::Arc};
use serde::Serialize;
use crate::{
Error, Result,
harness::{DeclinedStore, Harness},
integration::Integration,
};
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize)]
#[serde(rename_all = "lowercase")]
#[non_exhaustive]
pub enum Scope {
#[default]
Project,
User,
Local,
}
impl Scope {
pub fn as_str(self) -> &'static str {
match self {
Scope::Project => "project",
Scope::User => "user",
Scope::Local => "local",
}
}
}
impl fmt::Display for Scope {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl FromStr for Scope {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s {
"project" => Ok(Scope::Project),
"user" => Ok(Scope::User),
"local" => Ok(Scope::Local),
other => Err(Error::UnknownScope {
name: other.to_string(),
}),
}
}
}
pub trait Tool {
fn name(&self) -> &str;
fn harnesses(&self) -> Vec<Arc<dyn Harness>>;
fn integration(&self, scope: Scope) -> Integration;
fn root(&self, scope: Scope) -> Result<PathBuf>;
fn record_path(&self, scope: Scope) -> Result<PathBuf>;
fn declined_store(&self, scope: Scope) -> Result<Box<dyn DeclinedStore + '_>>;
fn record_header(&self) -> String {
format!("# Written by {} harness install. Do not edit.", self.name())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn scopes_parse_and_print() {
for s in [Scope::Project, Scope::User, Scope::Local] {
assert_eq!(s.as_str().parse::<Scope>().unwrap(), s);
assert_eq!(s.to_string(), s.as_str());
}
assert_eq!(
"global".parse::<Scope>().unwrap_err().to_string(),
"no scope named `global`"
);
assert_eq!(serde_json::to_string(&Scope::User).unwrap(), "\"user\"");
assert_eq!(Scope::default(), Scope::Project);
}
}