1
2use std::{
3 error::Error,
4 fs::{self},
5 process::Command,
6 str::FromStr,
7};
8
9use clap::Args;
10use strum::IntoEnumIterator;
11
12use crate::{config, template::Template};
13
14#[derive(Args, Debug)]
16pub struct InitOptions {
17 #[arg(long)]
18 pub title: Option<String>,
19
20 #[arg(long, num_args = 1.., value_delimiter=' ')]
21 pub tags: Option<Vec<String>>,
22
23 #[arg(long)]
24 pub color: Option<String>,
25
26 #[arg(long)]
27 pub template: Option<String>,
28}
29
30const MD_VITE_CONFIG_JS: &str = include_str!("../static/md-templates/vite.config.js");
32const MD_INDEX_HTML: &str = include_str!("../static/md-templates/index.html");
33const MD_MAIN_JS: &str = include_str!("../static/md-templates/main.js");
34const MD_SLIDES: &str = include_str!("../static/md-templates/slides.md");
35const MD_CUSTOM_CSS: &str = include_str!("../static/md-templates/custom.css");
36const MD_FERRIS: &[u8] = include_bytes!("../static/md-templates/ferris.png");
37
38const MD_GITIGNORE: &str = include_str!("../static/md-templates/.gitignore");
39
40const HTML_VITE_CONFIG_JS: &str = include_str!("../static/html-templates/vite.config.js");
42const HTML_INDEX_HTML: &str = include_str!("../static/html-templates/index.html");
43const HTML_MAIN_JS: &str = include_str!("../static/html-templates/main.js");
44const HTML_SLIDES: &str = include_str!("../static/html-templates/slides.html");
45const HTML_CUSTOM_CSS: &str = include_str!("../static/html-templates/custom.css");
46const HTML_FERRIS: &[u8] = include_bytes!("../static/html-templates/ferris.png");
47const HTML_GITIGNORE: &str = include_str!("../static/html-templates/.gitignore");
48
49pub fn init(opts: InitOptions) -> Result<(), Box<dyn Error>> {
63 let template = match opts.template {
64 Some(ref arg_template) => {
65 Template::from_str(arg_template).map_err(|_| "Invalid template".to_string())?
66 }
67 None => prompt_template()?,
68 };
69
70 let conf = config::create_conf(opts)?;
71
72 std::fs::create_dir_all(&conf.title)?;
73
74 let output = Command::new("npm")
75 .args(["init", "-y", "--prefix", &conf.title])
76 .current_dir(&conf.title)
77 .output()?;
78
79 if !output.status.success() {
81 eprintln!(
82 "npm init failed: {}",
83 String::from_utf8_lossy(&output.stderr)
84 );
85 return Err("npm init failed".into());
86 }
87
88 let npm_commands = vec![
89 format!("install --save-dev vite@5.4.21 http-server@14.1.1"),
90 format!("install reveal.js@5.2.1 serve@14.2.6 highlight.js@11.12.0"),
91 format!("install patch-package@8.0.1"),
92 ];
93
94 for c in npm_commands {
95 let args: Vec<&str> = c.split(' ').collect();
96 let output = Command::new("npm")
97 .args(&args)
98 .current_dir(&conf.title)
99 .output()?;
100
101 if !output.status.success() {
102 eprintln!(
103 "npm {} failed: {}",
104 c,
105 String::from_utf8_lossy(&output.stderr)
106 );
107 return Err(format!("npm {} failed", c).into());
108 }
109 println!("Bootstrapped npm {}", c);
110 }
111
112 println!("Saving config file...");
113
114 config::write_config(&conf.title, &conf)?;
115
116 match template {
118 Template::Markdown => {
119 fs::write(format!("{}/vite.config.js", &conf.title), MD_VITE_CONFIG_JS)?;
121 fs::write(format!("{}/index.html", &conf.title), MD_INDEX_HTML)?;
122 fs::write(format!("{}/.gitignore", &conf.title), MD_GITIGNORE)?;
123
124 std::fs::create_dir_all(format!("{}/src", &conf.title))?;
125 fs::write(format!("{}/src/main.js", &conf.title), MD_MAIN_JS)?;
126
127 std::fs::create_dir_all(format!("{}/slides", &conf.title))?;
128 fs::write(format!("{}/slides/slides.md", &conf.title), MD_SLIDES)?;
129
130 std::fs::create_dir_all(format!("{}/css", &conf.title))?;
131 fs::write(format!("{}/css/custom.css", &conf.title), MD_CUSTOM_CSS)?;
132
133 std::fs::create_dir_all(format!("{}/public", &conf.title))?;
134 fs::write(format!("{}/public/ferris.png", &conf.title), MD_FERRIS)?;
135 }
136 Template::HTML => {
137 fs::write(
139 format!("{}/vite.config.js", &conf.title),
140 HTML_VITE_CONFIG_JS,
141 )?;
142 fs::write(format!("{}/index.html", &conf.title), HTML_INDEX_HTML)?;
143 fs::write(format!("{}/.gitignore", &conf.title), HTML_GITIGNORE)?;
144
145 std::fs::create_dir_all(format!("{}/src", &conf.title))?;
146 fs::write(format!("{}/src/main.js", &conf.title), HTML_MAIN_JS)?;
147
148 std::fs::create_dir_all(format!("{}/slides", &conf.title))?;
149 fs::write(format!("{}/slides/slides.html", &conf.title), HTML_SLIDES)?;
150
151 std::fs::create_dir_all(format!("{}/css", &conf.title))?;
152 fs::write(format!("{}/css/custom.css", &conf.title), HTML_CUSTOM_CSS)?;
153
154 std::fs::create_dir_all(format!("{}/public", &conf.title))?;
155 fs::write(format!("{}/public/ferris.png", &conf.title), HTML_FERRIS)?
156 }
157 }
158
159 Ok(())
160}
161
162fn prompt_template() -> Result<Template, Box<dyn Error>> {
163 let template_opts: Vec<Template> = Template::iter().collect();
164
165 let chosen_template = inquire::Select::new("Select a template:", template_opts).prompt()?;
166
167 Ok(chosen_template)
168}