1use crate::{
2 errors::SpecError,
3 toml_parser::{Config, Metadata, Specification},
4};
5use miette::{IntoDiagnostic, Result, WrapErr};
6use std::{
7 collections::HashMap,
8 env,
9 fs::{create_dir, File},
10 io::Write,
11 path::PathBuf,
12};
13
14pub const DEFAULT_MANIFEST: &str = "Specification.toml";
15pub const DEFAULT_TEMPLATE: &str = "specification_template.md";
16
17pub fn new(name: String) -> Result<()> {
18 let path = env::current_dir().into_diagnostic()?;
19 init(Some(name), path)
20}
21
22pub fn init(name: Option<String>, path: PathBuf) -> Result<()> {
23 let mut name = if let Some(name) = name {
25 name
26 } else {
27 match path.file_name() {
28 Some(dir_name) => dir_name.to_string_lossy().to_string(),
29 None => {
30 return Err(SpecError::BadPath(path)).into_diagnostic();
31 }
32 }
33 };
34
35 if !path.is_dir() {
37 create_dir(&path).into_diagnostic().wrap_err_with(|| {
38 format!(
39 "cannot create the specification directory {}",
40 path.display()
41 )
42 })?;
43 } else {
44 let read_dir = path.read_dir().into_diagnostic()?;
46
47 for dir_entry in read_dir.flatten() {
48 let spec_file_detected = dir_entry.file_name().to_string_lossy() == DEFAULT_MANIFEST;
49 let template_file_detected =
50 dir_entry.file_name().to_string_lossy() == DEFAULT_TEMPLATE;
51 if spec_file_detected || template_file_detected {
52 return Err(SpecError::SpecAlreadyExists(path)).into_diagnostic();
53 }
54 }
55 }
56
57 let manifest_path = path.join(DEFAULT_MANIFEST);
59 let mut manifest_file = File::create(&manifest_path).into_diagnostic().wrap_err_with(|| format!("cannot create the specification file {}, make sure you pass a specification toml file via --specification-path", manifest_path.display()))?;
60
61 let template_path = path.join(DEFAULT_TEMPLATE);
62 let mut template_file = File::create(&template_path).into_diagnostic().wrap_err_with(|| format!("cannot create the specification template file {}, make sure you pass a specification toml file via --specification-path", template_path.display()))?;
63
64 let metadata = Metadata {
66 name: name.clone(),
67 description: Some("some description".to_string()),
68 version: None,
69 authors: vec!["your name".to_string()],
70 };
71 let config = Config {
72 template: DEFAULT_TEMPLATE.to_string(),
73 };
74 let specification = Specification {
75 metadata,
76 config,
77 sections: HashMap::new(),
78 };
79
80 let manifest_content =
81 toml::to_vec(&specification).expect("couldn't serialize the default manifest");
82
83 manifest_file
84 .write_all(&manifest_content)
85 .into_diagnostic()
86 .wrap_err_with(|| {
87 format!(
88 "couldn't write to the specification file {}",
89 manifest_path.display()
90 )
91 })?;
92
93 let title = {
95 let first_char = name.remove(0);
96 let first_char = first_char.to_uppercase();
97 format!("{first_char}{name}")
98 };
99 let template_content = format!("# {title}\n\n My specification\n");
100
101 template_file
102 .write_all(template_content.as_bytes())
103 .into_diagnostic()
104 .wrap_err_with(|| {
105 format!(
106 "couldn't write to the specification template file {}",
107 template_path.display()
108 )
109 })?;
110
111 Ok(())
112}