A static site generator written in Rust
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

main.rs 8.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. extern crate clap;
  2. extern crate comrak;
  3. extern crate fs_extra;
  4. #[macro_use]
  5. extern crate lazy_static;
  6. extern crate regex;
  7. extern crate toml;
  8. extern crate uuid;
  9. use std::{env, fs};
  10. use clap::{App, Arg};
  11. use fs_extra::dir;
  12. use toml::Value;
  13. use config::Config;
  14. use post::{parse_post, read_posts_dir};
  15. use write::{write_post, write_post_listing};
  16. mod config;
  17. mod post;
  18. mod render;
  19. mod write;
  20. fn build() {
  21. let cwd = env::current_dir().expect("Couldn't read current directory");
  22. let config = match fs::read_to_string(cwd.join("tlon.toml")) {
  23. Ok(contents) => match contents.parse::<Value>() {
  24. Ok(config) => Config {
  25. site_name: String::from(config["site_name"].as_str().unwrap()),
  26. },
  27. Err(_) => panic!("Invalid tlon.toml"),
  28. },
  29. Err(_) => {
  30. panic!("Can't find tlon.toml");
  31. }
  32. };
  33. match fs::read_dir(cwd.join("public")) {
  34. Ok(_) => {
  35. fs::remove_dir_all(cwd.join("public")).unwrap();
  36. }
  37. Err(_) => {}
  38. }
  39. fs::create_dir(cwd.join("public")).expect("Couldn't create public directory");
  40. let layout_template = fs::read_to_string(&cwd.join("templates").join("layout.html"))
  41. .expect("Couldn't find layout template");
  42. let post_template = fs::read_to_string(cwd.join("templates").join("post.html"))
  43. .expect("Couldn't find post template");
  44. let post_listing_template = fs::read_to_string(cwd.join("templates").join("post_listing.html"))
  45. .expect("Couldn't find post listing item template");
  46. let post_item_template =
  47. fs::read_to_string(cwd.join("templates").join("post_listing_item.html"))
  48. .expect("Couldn't find post listing item template");
  49. let post_paths = read_posts_dir(&cwd.join("posts"));
  50. let posts = post_paths
  51. .into_iter()
  52. .map(|path| match path {
  53. Ok(p) => {
  54. let post = parse_post(p.path());
  55. write_post(&cwd, &layout_template, &post_template, &post);
  56. post
  57. }
  58. Err(err) => panic!(err),
  59. }).collect();
  60. write_post_listing(
  61. &cwd,
  62. &layout_template,
  63. &post_listing_template,
  64. &post_item_template,
  65. &posts,
  66. );
  67. fs_extra::copy_items(
  68. &vec![cwd.join("css"), cwd.join("js")],
  69. cwd.join("public"),
  70. &dir::CopyOptions::new(),
  71. ).expect("Couldn't copy css/js directories");
  72. }
  73. fn new(name: &str) {
  74. let cwd = env::current_dir().expect("Couldn't read current directory");
  75. let project_path = cwd.join(name);
  76. fs::create_dir(&project_path).expect(&format!("Couldn't create directory '{}'", &name));
  77. fs::write(
  78. project_path.join("tlon.toml"),
  79. format!("site_name = \"{}\"", &name),
  80. ).expect("Could not create tlon.toml");
  81. for dir in &["posts", "public", "templates", "css", "js"] {
  82. fs::create_dir(&project_path.join(&dir))
  83. .expect(&format!("Couldn't create {} directory", &dir));
  84. }
  85. fs::write(project_path.join("css").join("style.css"), "")
  86. .expect("Couldn't create css/style.css");
  87. fs::write(project_path.join("js").join("index.js"), "").expect("Couldn't create js/index.js");
  88. for file in &["layout", "post_listing", "post", "post_listing_item"] {
  89. fs::write(
  90. &project_path
  91. .join("templates")
  92. .join(format!("{}.html", file)),
  93. "",
  94. ).expect(&format!("Couldn't write templates/{}.html", file));
  95. }
  96. }
  97. fn main() {
  98. let matches = App::new("tlon")
  99. .version("0.1.0")
  100. .author("Dylan Baker")
  101. .about("Highly opinionated static site generator")
  102. .arg(
  103. Arg::with_name("command")
  104. .required(true)
  105. .possible_values(&["build", "new"])
  106. .index(1),
  107. ).arg(
  108. Arg::with_name("name")
  109. .required_if("command", "new")
  110. .index(2),
  111. ).get_matches();
  112. let command = matches.value_of("command").unwrap();
  113. if command == "build" {
  114. build();
  115. } else if command == "new" {
  116. new(&matches.value_of("name").unwrap());
  117. }
  118. }
  119. #[cfg(test)]
  120. mod tests {
  121. #[allow(unused_imports)]
  122. use super::*;
  123. #[allow(unused_imports)]
  124. use uuid::Uuid;
  125. #[test]
  126. fn test_new() {
  127. let temp_dir = env::temp_dir();
  128. env::set_current_dir(&temp_dir).unwrap();
  129. let uuid = Uuid::new_v4().to_string();
  130. let project_dir = temp_dir.join(&uuid);
  131. new(&uuid);
  132. for dir in &["public", "posts", "templates"] {
  133. fs::read_dir(&project_dir.join(dir)).unwrap();
  134. }
  135. assert_eq!(
  136. "",
  137. fs::read_to_string(&project_dir.join("templates").join("post_listing.html")).unwrap()
  138. );
  139. assert_eq!(
  140. "",
  141. fs::read_to_string(&project_dir.join("templates").join("layout.html")).unwrap()
  142. );
  143. assert_eq!(
  144. "",
  145. fs::read_to_string(&project_dir.join("templates").join("post_listing_item.html"))
  146. .unwrap()
  147. );
  148. assert_eq!(
  149. "",
  150. fs::read_to_string(&project_dir.join("templates").join("post.html")).unwrap()
  151. );
  152. assert_eq!(
  153. "",
  154. fs::read_to_string(&project_dir.join("css").join("style.css")).unwrap()
  155. );
  156. assert_eq!(
  157. "",
  158. fs::read_to_string(&project_dir.join("js").join("index.js")).unwrap()
  159. );
  160. assert_eq!(
  161. format!("site_name = \"{}\"", &uuid),
  162. fs::read_to_string(&project_dir.join("tlon.toml")).unwrap()
  163. );
  164. fs::remove_dir_all(project_dir).unwrap();
  165. }
  166. #[test]
  167. fn test_build() {
  168. let temp_dir = env::temp_dir();
  169. let uuid = Uuid::new_v4().to_string();
  170. let project_dir = temp_dir.join(&uuid);
  171. fs::create_dir(&project_dir).unwrap();
  172. env::set_current_dir(&project_dir).unwrap();
  173. fs::create_dir(project_dir.join("posts")).unwrap();
  174. fs::create_dir(project_dir.join("public")).unwrap();
  175. fs::create_dir(project_dir.join("templates")).unwrap();
  176. fs::create_dir(project_dir.join("css")).unwrap();
  177. fs::create_dir(project_dir.join("js")).unwrap();
  178. fs::write(
  179. project_dir.join("css").join("style.css"),
  180. "body { background: blue; }",
  181. ).unwrap();
  182. fs::write(
  183. project_dir.join("js").join("index.js"),
  184. "window.onload = function () { alert() }",
  185. ).unwrap();
  186. fs::write(
  187. project_dir.join("templates").join("layout.html"),
  188. "<html><head><title>Test</title></head><body>{{ contents }}</body></html>",
  189. ).unwrap();
  190. fs::write(
  191. project_dir.join("templates").join("post.html"),
  192. "<article><h1>{{ title }}</h1><div>{{ body }}</div></article>",
  193. ).unwrap();
  194. fs::write(
  195. project_dir.join("templates").join("post_listing.html"),
  196. "<ul>{{ post_listing }}</ul>",
  197. ).unwrap();
  198. fs::write(
  199. project_dir.join("templates").join("post_listing_item.html"),
  200. "<li><a href=\"/{{ slug }}\">{{ title }}</a></li>",
  201. ).unwrap();
  202. fs::write(
  203. project_dir.join("posts").join("first-post.md"),
  204. "# First post\n\nThis is the first post\n\nIt has multiple paragraphs",
  205. ).unwrap();
  206. fs::write(project_dir.join("tlon.toml"), "site_name = \"Test Site\"").unwrap();
  207. build();
  208. assert_eq!(
  209. "<html><head><title>Test</title></head><body><ul><li><a href=\"/fir\
  210. st-post\">First post</a></li></ul></body></html>",
  211. fs::read_to_string(project_dir.join("public").join("index.html")).unwrap(),
  212. );
  213. assert_eq!(
  214. "<html><head><title>Test</title></head><body><article><h1>First pos\
  215. t</h1><div><p>This is the first post</p><p>It has multiple paragra\
  216. phs</p></div></article></body></html>",
  217. fs::read_to_string(
  218. project_dir
  219. .join("public")
  220. .join("first-post")
  221. .join("index.html")
  222. ).unwrap()
  223. .replace("\n", ""),
  224. );
  225. assert_eq!(
  226. "body { background: blue; }",
  227. fs::read_to_string(project_dir.join("public").join("css").join("style.css")).unwrap()
  228. );
  229. assert_eq!(
  230. "window.onload = function () { alert() }",
  231. fs::read_to_string(project_dir.join("public").join("js").join("index.js")).unwrap()
  232. );
  233. fs::remove_dir_all(project_dir).unwrap();
  234. }
  235. }