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