use std::{error::Error, ffi::OsString, fmt::Display, path::Path, str::FromStr};
use dxm_manifest::Manifest;
use git2::Repository;
pub const GITIGNORE_NAME: &str = ".gitignore";
pub const TEMPLATE_EXTENSION: &str = "example";
const GIT_README: &str = include_str!("../templates/git/README.md");
const ROOT_GITIGNORE: &str = include_str!("../templates/git/root.gitignore");
const DATA_GITIGNORE: &str = include_str!("../templates/git/data.gitignore");
#[derive(Default, Debug, PartialEq, Eq, Clone)]
pub enum VcsOption {
#[default]
None,
Git,
}
#[derive(Debug)]
pub struct ParseVcsOptionError {
option: String,
}
impl Display for ParseVcsOptionError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "unknown vsc option {}", self.option)?;
Ok(())
}
}
impl Error for ParseVcsOptionError {}
impl FromStr for VcsOption {
type Err = ParseVcsOptionError;
fn from_str(option: &str) -> Result<Self, Self::Err> {
match option {
"none" => Ok(Self::None),
"git" => Ok(Self::Git),
_ => Err(ParseVcsOptionError {
option: option.to_owned(),
}),
}
}
}
impl VcsOption {
pub fn init<P>(&self, path: P, manifest: &Manifest) -> Result<(), Box<dyn Error>>
where
P: AsRef<Path>,
{
let path = path.as_ref();
let data_path = manifest.server.data(path);
match self {
VcsOption::None => Ok(()),
VcsOption::Git => {
Repository::init(path)?;
fs_err::write(path.join(crate::README_NAME), GIT_README)?;
fs_err::write(path.join(GITIGNORE_NAME), ROOT_GITIGNORE)?;
fs_err::write(data_path.join(GITIGNORE_NAME), DATA_GITIGNORE)?;
create_template(data_path.join(crate::ENV_CFG_NAME))?;
create_template(data_path.join(crate::SECRETS_CFG_NAME))?;
Ok(())
}
}
}
}
fn create_template<P>(path: P) -> std::io::Result<u64>
where
P: AsRef<Path>,
{
let path = path.as_ref();
let name = path.file_name().ok_or_else(|| {
std::io::Error::new(
std::io::ErrorKind::InvalidFilename,
"template path has no filename",
)
})?;
let mut new_name = OsString::with_capacity(name.len() + TEMPLATE_EXTENSION.len() + 1);
new_name.push(name);
new_name.push(".");
new_name.push(TEMPLATE_EXTENSION);
let dest = path.with_file_name(new_name);
fs_err::copy(path, dest)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_returns_value_when_valid() {
assert_eq!(VcsOption::from_str("none").unwrap(), VcsOption::None);
}
#[test]
#[should_panic]
fn parse_returns_error_when_invalid() {
VcsOption::from_str("").unwrap();
}
}