1pub mod config;
18pub mod errors;
19
20use std::io::Write;
21use std::path::PathBuf;
22
23use std::{
24 env::{temp_dir, var},
25 fs::File,
26 io::Read,
27 process::Command,
28};
29
30use chrono::Local;
31use config::Config;
32
33pub fn get_title(config: &mut Config) {
35 println!("\nEnter title: ");
36 println!("------------");
37
38 let mut input = String::new();
39
40 std::io::stdin()
41 .read_line(&mut input)
42 .expect("Failed to read title");
43
44 if input.trim().is_empty() {
45 eprintln!("Error: Title cannot be empty");
46 panic!("Empty title");
47 }
48
49 println!("");
50
51 config.title = input.trim().to_string();
52}
53
54pub fn get_tags(config: &mut Config) {
56 if let Some(output_dir) = &config.zola.output_dir {
57 let mut out_path = PathBuf::from(output_dir);
58
59 if out_path.is_relative() {
60 out_path = config.zola.dir.join(out_path);
61 }
62
63 let output = Command::new("ls")
64 .arg(out_path.join("tags"))
65 .output()
66 .expect("Failed to list tags");
67
68 let tags = String::from_utf8(output.stdout).unwrap();
70 let tags = parse_invalid_tags(tags.split('\n').collect());
71
72 if tags.len() > 0 {
73 println!("Enter tags [{}]:", tags.join(", "));
74 println!("----------");
75 } else {
76 println!("Enter tags (comma separated): ");
77 println!("----------");
78 }
79 }
80
81 let mut input = String::new();
82
83 std::io::stdin()
84 .read_line(&mut input)
85 .expect("Failed to read tags");
86
87 if !input.trim().is_empty() {
88 config.tags = input.split(',').map(|tag| tag.trim().to_string()).collect();
89 }
90}
91
92pub fn get_content(config: &mut Config) {
94 let mut file_path = temp_dir();
95 let mut content = String::new();
96 let file_name = config.title.to_lowercase().replace(" ", "-") + ".md";
97 let editor = var("EDITOR").unwrap();
98
99 file_path.push(file_name);
100 File::create(&file_path).expect("Could not create temp file");
101
102 Command::new(editor)
103 .arg(&file_path)
104 .status()
105 .expect("Failed opening editor");
106
107 let _result = File::open(file_path)
108 .expect("Could not open file")
109 .read_to_string(&mut content);
110
111 config.content = content;
112}
113
114pub fn write(config: &Config) {
116 let file_name = config.title.to_lowercase().replace(" ", "-") + ".md";
117 let file_path = config.zola.dir.join("content").join(file_name);
118
119 let mut file =
120 File::create(&file_path).expect("Could not create file at zola content directory");
121
122 let mut result = String::new();
124 let mut prefix = String::new();
125 prefix.push_str("+++\n");
126 prefix.push_str(&format!("title = \"{}\"\n", config.title));
127 prefix.push_str(&format!("date = {}\n", Local::now().format("%Y-%m-%d")));
128
129 if config.tags.len() > 0 {
130 prefix.push_str(&format!("\n[taxonomies]\ntags = {:?}\n", config.tags));
131 }
132
133 prefix.push_str("+++\n\n");
134
135 result.insert_str(0, &prefix);
136 result.insert_str(prefix.len(), &config.content);
137
138 match file.write_all(result.as_bytes()) {
139 Ok(_) => println!(
140 "\n> Successfully wrote to file:\n> {}\n",
141 file_path.display()
142 ),
143 Err(e) => eprintln!("Error: {}", e),
144 }
145}
146
147pub fn build(config: &Config) {
149 let mut zola = Command::new("zola");
150
151 zola.arg("build")
152 .current_dir(&config.zola.dir)
153 .status()
154 .expect("Failed to build site");
155}
156
157pub fn publish(config: &Config) {
159 let mut git_add = Command::new("git");
160 let mut git_commit = Command::new("git");
161 let mut git_push = Command::new("git");
162
163 git_add
164 .arg("add")
165 .arg("-A")
166 .current_dir(&config.zola.dir)
167 .status()
168 .expect("Failed to add files");
169
170 git_commit
171 .arg("commit")
172 .arg("-m")
173 .arg(&format!("New post: \"{}\"", config.title))
174 .current_dir(&config.zola.dir)
175 .status()
176 .expect("Failed to commit changes");
177
178 match &config.zola.base_url {
179 Some(url) => {
180 println!("\n> Changes to be published at: {}", url);
181 }
182 None => (),
183 }
184
185 git_push
186 .arg("push")
187 .current_dir(&config.zola.dir)
188 .status()
189 .expect("Failed to publish changes");
190}
191
192fn parse_invalid_tags(tags: Vec<&str>) -> Vec<String> {
194 const INVALID_TAGS: [&str; 5] = ["", ".", "..", "index.md", "index.html"];
195
196 tags.iter()
197 .filter(|tag| !INVALID_TAGS.contains(tag))
198 .map(|tag| tag.to_string())
199 .collect()
200}