1use std::{error::Error, ffi::OsString, fmt::Display, path::Path, str::FromStr};
4
5use dxm_manifest::Manifest;
6use git2::Repository;
7
8pub const GITIGNORE_NAME: &str = ".gitignore";
9pub const TEMPLATE_EXTENSION: &str = "example";
10
11const GIT_README: &str = include_str!("../templates/git/README.md");
12const ROOT_GITIGNORE: &str = include_str!("../templates/git/root.gitignore");
13const DATA_GITIGNORE: &str = include_str!("../templates/git/data.gitignore");
14
15#[derive(Default, Debug, PartialEq, Eq, Clone)]
17pub enum VcsOption {
18 #[default]
19 None,
20 Git,
21}
22
23#[derive(Debug)]
24pub struct ParseVcsOptionError {
25 option: String,
26}
27
28impl Display for ParseVcsOptionError {
29 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 write!(f, "unknown vsc option {}", self.option)?;
31
32 Ok(())
33 }
34}
35
36impl Error for ParseVcsOptionError {}
37
38impl FromStr for VcsOption {
39 type Err = ParseVcsOptionError;
40
41 fn from_str(option: &str) -> Result<Self, Self::Err> {
42 match option {
43 "none" => Ok(Self::None),
44 "git" => Ok(Self::Git),
45 _ => Err(ParseVcsOptionError {
46 option: option.to_owned(),
47 }),
48 }
49 }
50}
51
52impl VcsOption {
53 pub fn init<P>(&self, path: P, manifest: &Manifest) -> Result<(), Box<dyn Error>>
55 where
56 P: AsRef<Path>,
57 {
58 let path = path.as_ref();
59 let data_path = manifest.server.data(path);
60
61 match self {
62 VcsOption::None => Ok(()),
63 VcsOption::Git => {
64 Repository::init(path)?;
65
66 fs_err::write(path.join(crate::README_NAME), GIT_README)?;
67 fs_err::write(path.join(GITIGNORE_NAME), ROOT_GITIGNORE)?;
68 fs_err::write(data_path.join(GITIGNORE_NAME), DATA_GITIGNORE)?;
69
70 create_template(data_path.join(crate::ENV_CFG_NAME))?;
71 create_template(data_path.join(crate::SECRETS_CFG_NAME))?;
72
73 Ok(())
74 }
75 }
76 }
77}
78
79fn create_template<P>(path: P) -> std::io::Result<u64>
80where
81 P: AsRef<Path>,
82{
83 let path = path.as_ref();
84 let name = path.file_name().ok_or_else(|| {
85 std::io::Error::new(
86 std::io::ErrorKind::InvalidFilename,
87 "template path has no filename",
88 )
89 })?;
90
91 let mut new_name = OsString::with_capacity(name.len() + TEMPLATE_EXTENSION.len() + 1);
92 new_name.push(name);
93 new_name.push(".");
94 new_name.push(TEMPLATE_EXTENSION);
95
96 let dest = path.with_file_name(new_name);
97 fs_err::copy(path, dest)
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 #[test]
105 fn parses_returns_value_when_valid() {
106 assert_eq!(VcsOption::from_str("none").unwrap(), VcsOption::None);
107 }
108
109 #[test]
110 #[should_panic]
111 fn parse_returns_error_when_invalid() {
112 VcsOption::from_str("").unwrap();
113 }
114}