aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Letan <contact@thomasletan.fr>2020-02-02 20:15:40 +0100
committerThomas Letan <contact@thomasletan.fr>2020-02-02 20:15:40 +0100
commit52afa59c1c25e48d34aa37419f17a19ac2e49e45 (patch)
treec019872a68a9ca3fe5934d1e0c523f44c9bd69b8
parentrefactor: Rework libceltchar organization (diff)
feature: Add a command to generate a static website
-rw-r--r--app/src/main.rs19
-rw-r--r--lib/src/lib.rs3
-rw-r--r--lib/src/wstatic.rs142
-rw-r--r--templates/static/chapter.html64
-rw-r--r--templates/static/index.html15
-rw-r--r--templates/static/style.css76
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;
+}