use std::str::FromStr;
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
pub enum Remote {
Github,
Filesystem,
Mock,
}
impl Remote {
pub fn into_path(
self,
owner: Option<String>,
repository: Option<String>,
name: Option<String>,
commit: Option<String>,
) -> Option<crate::RemotePathCommitOptional> {
match self {
Remote::Github => Some(crate::RemotePathCommitOptional::Github {
owner: owner?,
repository: repository?,
commit,
}),
Remote::Filesystem => Some(crate::RemotePathCommitOptional::Filesystem {
owner: owner?,
repository: repository?,
commit,
}),
Remote::Mock => Some(crate::RemotePathCommitOptional::Mock { name: name? }),
}
}
fn parse_keyword(s: &str) -> Result<Self, String> {
match s {
"github" => Ok(Self::Github),
"filesystem" => Ok(Self::Filesystem),
"mock" => Ok(Self::Mock),
other => Err(format!(
"unknown remote: {other} (expected github, filesystem, or mock)"
)),
}
}
}
pub(crate) fn tokenize(s: &str) -> Result<Vec<(&str, &str)>, String> {
s.split(',')
.map(|pair| {
pair.split_once('=')
.map(|(k, v)| (k.trim(), v.trim()))
.ok_or_else(|| format!("expected key=value, got: {pair}"))
})
.collect()
}
impl FromStr for crate::RemotePathCommitOptional {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut remote: Option<Remote> = None;
let mut owner: Option<String> = None;
let mut repository: Option<String> = None;
let mut name: Option<String> = None;
let mut commit: Option<String> = None;
for (k, v) in tokenize(s)? {
match k {
"remote" => remote = Some(Remote::parse_keyword(v)?),
"owner" => owner = Some(v.to_string()),
"repository" => repository = Some(v.to_string()),
"name" => name = Some(v.to_string()),
"commit" => commit = Some(v.to_string()),
other => return Err(format!("unknown key: {other}")),
}
}
let remote = remote.ok_or_else(|| "remote is required".to_string())?;
remote
.into_path(owner, repository, name, commit)
.ok_or_else(|| {
"owner and repository are required for github/filesystem, name for mock"
.to_string()
})
}
}
pub fn remote_path_to_arg_string(path: &crate::RemotePathCommitOptional) -> String {
match path {
crate::RemotePathCommitOptional::Github {
owner,
repository,
commit,
} => {
let mut s = format!("remote=github,owner={owner},repository={repository}");
if let Some(c) = commit {
s.push_str(&format!(",commit={c}"));
}
s
}
crate::RemotePathCommitOptional::Filesystem {
owner,
repository,
commit,
} => {
let mut s = format!("remote=filesystem,owner={owner},repository={repository}");
if let Some(c) = commit {
s.push_str(&format!(",commit={c}"));
}
s
}
crate::RemotePathCommitOptional::Mock { name } => format!("remote=mock,name={name}"),
}
}