diff options
author | Thomas Letan <contact@thomasletan.fr> | 2020-02-02 20:15:40 +0100 |
---|---|---|
committer | Thomas Letan <contact@thomasletan.fr> | 2020-02-02 20:15:40 +0100 |
commit | 52afa59c1c25e48d34aa37419f17a19ac2e49e45 (patch) | |
tree | c019872a68a9ca3fe5934d1e0c523f44c9bd69b8 | |
parent | refactor: Rework libceltchar organization (diff) |
feature: Add a command to generate a static website
-rw-r--r-- | app/src/main.rs | 19 | ||||
-rw-r--r-- | lib/src/lib.rs | 3 | ||||
-rw-r--r-- | lib/src/wstatic.rs | 142 | ||||
-rw-r--r-- | templates/static/chapter.html | 64 | ||||
-rw-r--r-- | templates/static/index.html | 15 | ||||
-rw-r--r-- | templates/static/style.css | 76 |
6 files changed, 317 insertions, 2 deletions
diff --git a/app/src/main.rs b/app/src/main.rs index 8b2f48e..ba38fcd 100644 --- a/app/src/main.rs +++ b/app/src/main.rs @@ -9,7 +9,7 @@ use std::path::PathBuf; use clap::{App, SubCommand}; -use libceltchar::{EpubWriter, Error, Loader, Project, Zip}; +use libceltchar::{EpubWriter, Error, Loader, Project, Zip, Static}; #[cfg(debug_assertions)] use libceltchar::Raise; @@ -49,6 +49,18 @@ fn build_epub(assets : &PathBuf) -> Result<(), Error> { Ok(()) } +fn build_static(assets : &PathBuf) -> Result<(), Error> { + let root = find_root()?; + let loader = Fs; + + let project = Project::load_and_render(&root, &loader)?; + + let mut static_website = Static::init(&PathBuf::from("out"))?; + static_website.generate_static_website(&project, assets)?; + + Ok(()) +} + #[cfg(debug_assertions)] fn get_assets() -> Result<PathBuf, Error> { current_dir().or_raise("cannot get current directory") @@ -65,6 +77,8 @@ fn main() -> Result<(), Error> { .author("Thomas Letan") .about("A tool to generate novels") .subcommand(SubCommand::with_name("new").about("Create a new celtchar document")) + .subcommand(SubCommand::with_name("epub").about("Build a epub")) + .subcommand(SubCommand::with_name("static").about("Build a static website")) .subcommand(SubCommand::with_name("build").about("Build a celtchar document")) .subcommand(SubCommand::with_name("deps").about("List dependencies of a celtchar document")) .get_matches(); @@ -74,7 +88,8 @@ fn main() -> Result<(), Error> { let assets : PathBuf = get_assets()?; match subcommand { - "build" => build_epub(&assets)?, + "epub" => build_epub(&assets)?, + "static" => build_static(&assets)?, "deps" => deps()?, _ => unimplemented!(), } diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 9859f0d..dbcceea 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -10,7 +10,10 @@ mod error; mod project; mod render; mod writer; +mod wstatic; +pub use writer::BookWriter; pub use epub::{EpubWriter, Zip}; pub use error::{Error, Raise}; pub use project::{Chapter, Cover, Loader, Project}; +pub use wstatic::Static; diff --git a/lib/src/wstatic.rs b/lib/src/wstatic.rs new file mode 100644 index 0000000..da0e8ac --- /dev/null +++ b/lib/src/wstatic.rs @@ -0,0 +1,142 @@ +use std::path::PathBuf; +use std::fs::{create_dir, read_to_string}; +use serde_json::json; +use tera::{Tera, Context}; + +use crate::error::{Raise, Error}; +use crate::project::{Cover, Project, Language, Chapter}; +use crate::BookWriter; +use crate::assets::template_dir; + +pub struct Static { + base : PathBuf, +} + +impl BookWriter for Static { + fn write_bytes(&mut self, dst : &PathBuf, input : &[u8]) -> Result<(), Error> { + + std::fs::write(&self.base.join(dst), input) + .or_raise(&format!("Could not write content to file {:?}", dst))?; + + Ok(()) + } + + fn write_file(&mut self, dst : &PathBuf, src : &PathBuf) -> Result<(), Error> { + let input = read_to_string(src) + .or_raise(&format!("Could not read content of file {:?}", src))?; + + self.write_bytes(dst, input.as_bytes())?; + + Ok(()) + } +} + +impl Static { + pub fn init(base : &PathBuf) -> Result<Static, Error> { + if !base.exists() { + create_dir(base).or_raise("Could not create output directory.")?; + } + + if base.is_dir() { + Ok(Static { + base : base.to_owned() + }) + } else { + Err(Error::new(&format!("{:?} already exists and is not a directory", base))) + } + } + + fn generate_index( + &mut self, + project : &Project<Cover, String>, + tera : &Tera, + ) -> Result<(), Error> { + let chaps : Vec<_> = project + .chapters + .iter() + .enumerate() + .map(|(idx, chapter)| { + json!({ + "index": idx, + "title": chapter.title, + }) + }) + .collect(); + + let mut ctx = Context::new(); + ctx.insert("chapters", &chaps); + ctx.insert("language", &project.language); + ctx.insert("title", &project.title); + + self.write_template( + &PathBuf::from("index.html"), + tera, + "static/index.html", + &ctx, + )?; + + Ok(()) + } + + fn generate_chapters( + &mut self, + tera : &Tera, + chapters : &Vec<Chapter<String>>, + numbering : bool, + lang : &Language, + ) -> Result<(), Error> { + let max = chapters.len(); + + chapters + .iter() + .enumerate() + .map(|(idx, c)| { + let mut ctx = Context::new(); + ctx.insert("number", &(idx + 1)); + ctx.insert("chapter", &c); + ctx.insert("numbering", &numbering); + ctx.insert("language", &lang); + ctx.insert("chapters_number", &max); + + let path : PathBuf = PathBuf::from(format!("{}.html", idx)); + + self.write_template( + &path, + tera, + "static/chapter.html", + &ctx, + )?; + + Ok(()) + }) + .collect::<Result<Vec<()>, Error>>()?; + + Ok(()) + + } + + pub fn generate_static_website( + &mut self, + project : &Project<Cover, String>, + assets : &PathBuf, + ) -> Result<(), Error> { + let tera = + Tera::new(template_dir(assets)?.as_str()) + .or_raise("Could not build templates")?; + + self.generate_index(project, &tera)?; + self.generate_chapters( + &tera, + &project.chapters, + project.numbering.unwrap_or(false), + &project.language, + )?; + + self.write_file( + &PathBuf::from("style.css"), + &assets.join(PathBuf::from("templates/static/style.css")), + )?; + + Ok(()) + } +} diff --git a/templates/static/chapter.html b/templates/static/chapter.html new file mode 100644 index 0000000..a988c76 --- /dev/null +++ b/templates/static/chapter.html @@ -0,0 +1,64 @@ +<!DOCTYPE html> +<html lang="{{ language | lower }}"> + <head> + <title>{{ chapter.title }}</title> + <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fork-awesome@1.1.7/css/fork-awesome.min.css" integrity="sha256-gsmEoJAws/Kd3CjuOQzLie5Q3yshhvmo7YNtBG7aaEY=" crossorigin="anonymous"> + <link rel="stylesheet" href="style.css"> + </head> + <body> + <nav> + {% if 1 < number %} + <a href="{{ number - 2 }}.html" class="nav_item"> + <i class="fa fa-arrow-left" aria-hidden="true"></i> + </a> + {% else %} + <div class="nav_item"></div> + {% endif %} + <a href="index.html" class="nav_item"> + <i class="fa fa-home" aria-hidden="true"></i> + </a> + {% if number < chapters_number %} + <a href="{{ number }}.html" class="nav_item"> + <i class="fa fa-arrow-right" aria-hidden="true"></i> + </a> + {% else %} + <div class="nav_item"></div> + {% endif %} + </nav> + + <h1> + {% if numbering %} + – {{ number }} – + <span class="title"> + {{ chapter.title | upper }} + </span> + {% else %} + {{ chapter.title | upper }} + {% endif %} + </h1> + + <article class="ogmarkup"> + {{ chapter.content | safe }} + </article> + + <nav> + {% if 1 < number %} + <a href="{{ number - 2 }}.html" class="nav_item"> + <i class="fa fa-arrow-left" aria-hidden="true"></i> + </a> + {% else %} + <div class="nav_item"></div> + {% endif %} + <a href="index.html" class="nav_item"> + <i class="fa fa-home" aria-hidden="true"></i> + </a> + {% if number < chapters_number %} + <a href="{{ number }}.html" class="nav_item"> + <i class="fa fa-arrow-right" aria-hidden="true"></i> + </a> + {% else %} + <div class="nav_item"></div> + {% endif %} + </nav> + </body> +</html> diff --git a/templates/static/index.html b/templates/static/index.html new file mode 100644 index 0000000..6240ffa --- /dev/null +++ b/templates/static/index.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html lang="{{ language | lower }}"> + <head> + <title>{{ title }}</title> + <link rel="stylesheet" href="style.css"> + </head> + <body> + <h1>{{ title | upper }}</h1> + <ul> + {% for info in chapters %} + <li><a href="{{info.index}}.html">{{ info.index + 1 }}. {{ info.title }}</a></li> + {% endfor %} + </ul> + </body> +</html> diff --git a/templates/static/style.css b/templates/static/style.css new file mode 100644 index 0000000..c767a5f --- /dev/null +++ b/templates/static/style.css @@ -0,0 +1,76 @@ +html, body { + font-size: 120%; + font-family: serif; + color: #222; + hyphens: auto; +} + +body { + max-width: 600px; + margin: auto; +} + +h1 { + margin-top: 5rem; + margin-bottom: 5rem; + text-align: center; +} + +h1 .title { + font-size: 120%; + display: block; +} + + +p { + text-indent: 1.5rem; + padding: 0; + margin: 0; + line-height: 1.5em; + padding-bottom: .1em; +} + +.ogmarkup { + text-align: justify; +} + +.ogmarkup .date { + text-indent: 0; + text-align: right; + font-style: italic; + padding-bottom: 1rem; +} + +.ogmarkup .date em { + font-style: normal; +} + +.reply { + color: black; +} + +.thought .reply { + font-style: italic; +} + +.thought .reply em { + font-style: normal; +} + +.story { + padding-bottom: 1.5em; +} + +nav { + display: flex; + margin-top: 1rem; + margin-bottom: 1rem; +} + +.nav_item { + margin: 0; + width: 33%; + text-align: center; + font-size: 130%; + color: #333; +} |