123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545 |
- use std::sync::mpsc::channel;
- use std::time::Duration;
- use std::{env, fs, path};
-
- use fs_extra::dir;
- use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher};
- use toml::Value;
-
- use config::Config;
- use entry::{parse_entry, read_entry_dir, write_entry, write_entry_listing, Entry};
-
- pub fn build(include_drafts: bool) {
- let cwd = env::current_dir().expect("Couldn't read current directory");
- let config = match fs::read_to_string(cwd.join("casaubon.toml")) {
- Ok(contents) => match contents.parse::<Value>() {
- Ok(config) => Config {
- site_name: String::from(config["site_name"].as_str().unwrap()),
- },
- Err(_) => panic!("Invalid casaubon.toml"),
- },
- Err(_) => {
- panic!("Can't find casaubon.toml");
- }
- };
-
- match fs::read_dir(cwd.join("public")) {
- Ok(_) => {
- fs::remove_dir_all(cwd.join("public")).unwrap();
- }
- Err(_) => {}
- }
-
- fs::create_dir(cwd.join("public")).expect("Couldn't create public directory");
- fs::create_dir(cwd.join("public").join("posts")).expect("Couldn't create posts directory");
-
- let layout_template = fs::read_to_string(&cwd.join("templates").join("layout.html"))
- .expect("Couldn't find layout template");
- let post_template = fs::read_to_string(cwd.join("templates").join("post.html"))
- .expect("Couldn't find post template");
- let post_listing_template = fs::read_to_string(cwd.join("templates").join("post_listing.html"))
- .expect("Couldn't find post listing item template");
- let post_item_template =
- fs::read_to_string(cwd.join("templates").join("post_listing_item.html"))
- .expect("Couldn't find post listing item template");
- let page_template = fs::read_to_string(cwd.join("templates").join("page.html"))
- .expect("Couldn't find page template");
-
- let post_paths = match include_drafts {
- true => {
- let mut posts = read_entry_dir(&cwd.join("posts"));
- posts.append(&mut read_entry_dir(&cwd.join("drafts")));
- posts
- }
- false => read_entry_dir(&cwd.join("posts")),
- };
-
- let page_paths = read_entry_dir(&cwd.join("pages"));
-
- let mut posts: Vec<Entry> = post_paths
- .into_iter()
- .map(|entry| parse_entry(entry.path()))
- .collect::<Vec<Entry>>();
-
- posts.sort_by(|a, b| b.date.cmp(&a.date));
-
- for post in &posts {
- write_entry(&cwd, &layout_template, &post_template, &post, &config);
- }
-
- for entry in page_paths.into_iter() {
- let page = parse_entry(entry.path());
- write_entry(&cwd, &layout_template, &page_template, &page, &config);
- }
-
- write_entry_listing(
- &cwd,
- &layout_template,
- &post_listing_template,
- &post_item_template,
- &posts,
- &config,
- );
-
- fs_extra::copy_items(
- &vec![cwd.join("css"), cwd.join("js")],
- cwd.join("public"),
- &dir::CopyOptions::new(),
- )
- .expect("Couldn't copy css/js directories");
- }
-
- pub fn new(name: &str) {
- let cwd = env::current_dir().expect("Couldn't read current directory");
- let project_path = cwd.join(name);
-
- fs::create_dir(&project_path).expect(&format!("Couldn't create directory '{}'", &name));
- fs::write(
- project_path.join("casaubon.toml"),
- format!("site_name = \"{}\"", &name),
- )
- .expect("Could not create casaubon.toml");
-
- for dir in &[
- "drafts",
- "posts",
- "pages",
- "public",
- "templates",
- "css",
- "js",
- ] {
- fs::create_dir(&project_path.join(&dir))
- .expect(&format!("Couldn't create {} directory", &dir));
- }
-
- fs::write(project_path.join("css").join("style.css"), "")
- .expect("Couldn't create css/style.css");
- fs::write(project_path.join("js").join("index.js"), "").expect("Couldn't create js/index.js");
-
- let default_layout_template = format!(
- "<html>
- <head>
- <title>{}</title>
- </head>
- <body>
- <h1>{}</h1>
- {{{{ contents }}}}
- </body>
- </html>\n",
- name, name
- );
- let default_post_listing_template = format!(
- "<div>
- <h3>Posts</h3>
- <ul>{{{{ post_listing }}}}</ul>
- </div>\n"
- );
- let default_post_template = format!(
- "<article>
- <h1>{{{{ title }}}}</h1>
- <div>{{{{ body }}}}</div>
- </article>\n"
- );
- let default_page_template = format!(
- "<article>
- <h1>{{{{ title }}}}</h1>
- <div>{{{{ body }}}}</div>
- </article>\n"
- );
- let default_post_listing_item_template = format!(
- "<li>
- {{ date }} <a href=\"/posts/{{{{ slug }}}}/\">{{{{ title }}}}</a>
- </li>\n"
- );
-
- for (filename, contents) in &[
- ("layout", &default_layout_template),
- ("post_listing", &default_post_listing_template),
- ("post", &default_post_template),
- ("page", &default_page_template),
- ("post_listing_item", &default_post_listing_item_template),
- ] {
- fs::write(
- &project_path
- .join("templates")
- .join(format!("{}.html", filename)),
- &contents,
- )
- .expect(&format!("Couldn't write templates/{}.html", filename));
- }
- }
-
- fn should_rebuild(cwd: &path::PathBuf, path: &path::PathBuf) -> bool {
- let path_string = path.to_str().unwrap().to_string();
- let change_is_from_public = path_string.contains(cwd.join("public").to_str().unwrap());
- let change_is_from_git = path_string.contains(cwd.join(".git").to_str().unwrap());
-
- !change_is_from_public && !change_is_from_git
- }
-
- pub fn watch(include_drafts: bool) -> notify::Result<()> {
- let cwd = env::current_dir().expect("Couldn't read current directory");
- let (tx, rx) = channel();
- let mut watcher: RecommendedWatcher = try!(Watcher::new(tx, Duration::from_secs(2)));
- try!(watcher.watch(&cwd, RecursiveMode::Recursive));
- println!("Watching {}", cwd.to_str().unwrap());
-
- let handle_event = |path: &path::PathBuf| {
- if should_rebuild(&cwd, &path) {
- println!("Rebuilding");
- build(include_drafts);
- }
- };
-
- loop {
- match rx.recv() {
- Ok(e) => match e {
- DebouncedEvent::Create(path) => {
- handle_event(&path);
- }
- DebouncedEvent::Write(path) => {
- handle_event(&path);
- }
- _ => {}
- },
- Err(e) => println!("watch error: {:?}", e),
- }
- }
- }
-
- #[cfg(test)]
- mod tests {
- #[allow(unused_imports)]
- use super::*;
- #[allow(unused_imports)]
- use uuid::Uuid;
-
- #[test]
- fn test_new() {
- let temp_dir = env::temp_dir();
- env::set_current_dir(&temp_dir).unwrap();
-
- let uuid = Uuid::new_v4().to_string();
- let project_dir = temp_dir.join(&uuid);
- new(&uuid);
-
- for dir in &["public", "pages", "posts", "templates"] {
- fs::read_dir(&project_dir.join(dir)).unwrap();
- }
-
- assert_eq!(
- format!(
- "<html><head><title>{}</title></head><body><h1>{}</h1>{{{{ contents }}}}</body></html>",
- uuid, uuid
- ),
- fs::read_to_string(&project_dir.join("templates").join("layout.html"))
- .unwrap()
- .replace("\n", "")
- .replace(" ", "")
- );
- assert_eq!(
- format!("<div><h3>Posts</h3><ul>{{{{ post_listing }}}}</ul></div>"),
- fs::read_to_string(&project_dir.join("templates").join("post_listing.html"))
- .unwrap()
- .replace("\n", "")
- .replace(" ", "")
- );
- assert_eq!(
- format!("<li>{{ date }} <a href=\"/posts/{{{{ slug }}}}/\">{{{{ title }}}}</a></li>"),
- fs::read_to_string(&project_dir.join("templates").join("post_listing_item.html"))
- .unwrap()
- .replace("\n", "")
- .replace(" ", "")
- );
- assert_eq!(
- format!("<article><h1>{{{{ title }}}}</h1><div>{{{{ body }}}}</div></article>"),
- fs::read_to_string(&project_dir.join("templates").join("post.html"))
- .unwrap()
- .replace("\n", "")
- .replace(" ", "")
- );
- assert_eq!(
- format!("<article><h1>{{{{ title }}}}</h1><div>{{{{ body }}}}</div></article>"),
- fs::read_to_string(&project_dir.join("templates").join("page.html"))
- .unwrap()
- .replace("\n", "")
- .replace(" ", "")
- );
- assert_eq!(
- "",
- fs::read_to_string(&project_dir.join("css").join("style.css")).unwrap()
- );
- assert_eq!(
- "",
- fs::read_to_string(&project_dir.join("js").join("index.js")).unwrap()
- );
- assert_eq!(
- format!("site_name = \"{}\"", &uuid),
- fs::read_to_string(&project_dir.join("casaubon.toml")).unwrap()
- );
-
- fs::remove_dir_all(project_dir).unwrap();
- }
-
- #[test]
- fn test_build() {
- let temp_dir = env::temp_dir();
- let uuid = Uuid::new_v4().to_string();
- let project_dir = temp_dir.join(&uuid);
- fs::create_dir(&project_dir).unwrap();
- env::set_current_dir(&project_dir).unwrap();
-
- fs::create_dir(project_dir.join("posts")).unwrap();
- fs::create_dir(project_dir.join("pages")).unwrap();
- fs::create_dir(project_dir.join("public")).unwrap();
- fs::create_dir(project_dir.join("public").join("posts")).unwrap();
- fs::create_dir(project_dir.join("templates")).unwrap();
- fs::create_dir(project_dir.join("css")).unwrap();
- fs::create_dir(project_dir.join("js")).unwrap();
-
- fs::write(
- project_dir.join("css").join("style.css"),
- "body { background: blue; }",
- )
- .unwrap();
- fs::write(
- project_dir.join("js").join("index.js"),
- "window.onload = function () { alert() }",
- )
- .unwrap();
- fs::write(
- project_dir.join("templates").join("layout.html"),
- "<html><head><title>{{ page_title }}</title></head><body>{{ contents }}</body></html>",
- )
- .unwrap();
- fs::write(
- project_dir.join("templates").join("post.html"),
- "<article><h1>{{ title }}</h1><div>{{ body }}</div></article>",
- )
- .unwrap();
- fs::write(
- project_dir.join("templates").join("page.html"),
- "<article class=\"page\"><h1>{{ title }}</h1><div>{{ body }}</div></article>",
- )
- .unwrap();
- fs::write(
- project_dir.join("templates").join("post_listing.html"),
- "<ul>{{ post_listing }}</ul>",
- )
- .unwrap();
- fs::write(
- project_dir.join("templates").join("post_listing_item.html"),
- "<li><a href=\"/{{ slug }}\">{{ title }}</a></li>",
- )
- .unwrap();
- fs::write(
- project_dir.join("posts").join("first-post.md"),
- "# First post | 2019-01-01\n\nThis is the first post\n\nIt has multiple paragraphs",
- )
- .unwrap();
- fs::write(
- project_dir.join("pages").join("first-page.md"),
- "# First page\n\nThis is the first page\n\nIt has multiple paragraphs",
- )
- .unwrap();
- fs::write(
- project_dir.join("casaubon.toml"),
- "site_name = \"Test Site\"",
- )
- .unwrap();
-
- build(false);
-
- assert_eq!(
- "<html><head><title>Test Site</title></head><body><ul><li><a href=\"/fir\
- st-post\">First post</a></li></ul></body></html>",
- fs::read_to_string(project_dir.join("public").join("index.html")).unwrap(),
- );
-
- assert_eq!(
- "<html><head><title>First post | Test Site</title></head><body><article><h1>First pos\
- t</h1><div><p>This is the first post</p><p>It has multiple paragra\
- phs</p></div></article></body></html>",
- fs::read_to_string(
- project_dir
- .join("public")
- .join("posts")
- .join("first-post")
- .join("index.html")
- )
- .unwrap()
- .replace("\n", ""),
- );
-
- assert_eq!(
- "<html><head><title>First page | Test Site</title></head><body><article class=\"page\"><h1>First pag\
- e</h1><div><p>This is the first page</p><p>It has multiple paragra\
- phs</p></div></article></body></html>",
- fs::read_to_string(
- project_dir
- .join("public")
- .join("first-page")
- .join("index.html")
- )
- .unwrap()
- .replace("\n", ""),
- );
-
- assert_eq!(
- "body { background: blue; }",
- fs::read_to_string(project_dir.join("public").join("css").join("style.css")).unwrap()
- );
-
- assert_eq!(
- "window.onload = function () { alert() }",
- fs::read_to_string(project_dir.join("public").join("js").join("index.js")).unwrap()
- );
-
- fs::remove_dir_all(project_dir).unwrap();
- }
-
- #[test]
- fn test_build_drafts() {
- let temp_dir = env::temp_dir();
- let uuid = Uuid::new_v4().to_string();
- let project_dir = temp_dir.join(&uuid);
- fs::create_dir(&project_dir).unwrap();
- env::set_current_dir(&project_dir).unwrap();
-
- fs::create_dir(project_dir.join("drafts")).unwrap();
- fs::create_dir(project_dir.join("posts")).unwrap();
- fs::create_dir(project_dir.join("pages")).unwrap();
- fs::create_dir(project_dir.join("public")).unwrap();
- fs::create_dir(project_dir.join("public").join("posts")).unwrap();
- fs::create_dir(project_dir.join("templates")).unwrap();
- fs::create_dir(project_dir.join("css")).unwrap();
- fs::create_dir(project_dir.join("js")).unwrap();
-
- fs::write(
- project_dir.join("css").join("style.css"),
- "body { background: blue; }",
- )
- .unwrap();
- fs::write(
- project_dir.join("js").join("index.js"),
- "window.onload = function () { alert() }",
- )
- .unwrap();
- fs::write(
- project_dir.join("templates").join("layout.html"),
- "<html><head><title>{{ page_title }}</title></head><body>{{ contents }}</body></html>",
- )
- .unwrap();
- fs::write(
- project_dir.join("templates").join("post.html"),
- "<article><h1>{{ title }}</h1><div>{{ body }}</div></article>",
- )
- .unwrap();
- fs::write(
- project_dir.join("templates").join("page.html"),
- "<article class=\"page\"><h1>{{ title }}</h1><div>{{ body }}</div></article>",
- )
- .unwrap();
- fs::write(
- project_dir.join("templates").join("post_listing.html"),
- "<ul>{{ post_listing }}</ul>",
- )
- .unwrap();
- fs::write(
- project_dir.join("templates").join("post_listing_item.html"),
- "<li><a href=\"/{{ slug }}\">{{ title }}</a></li>",
- )
- .unwrap();
- fs::write(
- project_dir.join("posts").join("first-post.md"),
- "# First post | 2019-01-01\n\nThis is the first post",
- )
- .unwrap();
- fs::write(
- project_dir.join("pages").join("first-page.md"),
- "# First page\n\nThis is the first page",
- )
- .unwrap();
- fs::write(
- project_dir.join("drafts").join("first-draft.md"),
- "# First draft | 2019-01-01\n\nThis is the first draft",
- )
- .unwrap();
- fs::write(
- project_dir.join("casaubon.toml"),
- "site_name = \"Test Site\"",
- )
- .unwrap();
-
- build(true);
-
- assert_eq!(
- "<html><head><title>Test Site</title></head><body><ul><li><a href=\"/first-post\">First post</a></li><li><a href=\"/first-draft\">First draft</a></li></ul></body></html>",
- fs::read_to_string(project_dir.join("public").join("index.html")).unwrap().replace("\n", ""),
- );
-
- assert_eq!(
- "<html><head><title>First post | Test Site</title></head><body><article><h1>First post</h1><div><p>This is the first post</p></div></article></body></html>",
- fs::read_to_string(
- project_dir
- .join("public")
- .join("posts")
- .join("first-post")
- .join("index.html")
- ).unwrap()
- .replace("\n", ""),
- );
-
- assert_eq!(
- "<html><head><title>First page | Test Site</title></head><body><article class=\"page\"><h1>First page</h1><div><p>This is the first page</p></div></article></body></html>",
- fs::read_to_string(
- project_dir
- .join("public")
- .join("first-page")
- .join("index.html")
- ).unwrap()
- .replace("\n", ""),
- );
-
- assert_eq!(
- "<html><head><title>First draft | Test Site</title></head><body><article><h1>First draft</h1><div><p>This is the first draft</p></div></article></body></html>",
- fs::read_to_string(
- project_dir
- .join("public")
- .join("posts")
- .join("first-draft")
- .join("index.html")
- ).unwrap()
- .replace("\n", ""),
- );
-
- fs::remove_dir_all(project_dir).unwrap();
- }
-
- #[test]
- fn test_should_rebuild() {
- let cwd = env::current_dir().unwrap();
- assert_eq!(
- false,
- should_rebuild(&cwd, &cwd.join("public").join("index.html"))
- );
- assert_eq!(
- false,
- should_rebuild(&cwd, &cwd.join(".git").join("index.html"))
- );
- assert_eq!(
- true,
- should_rebuild(&cwd, &cwd.join("posts").join("test.md"))
- );
- assert_eq!(
- true,
- should_rebuild(&cwd, &cwd.join("drafts").join("test.md"))
- );
- assert_eq!(
- true,
- should_rebuild(&cwd, &cwd.join("css").join("style.css"))
- );
- assert_eq!(true, should_rebuild(&cwd, &cwd.join("js").join("index.js")));
- }
- }
|