diff --git a/src/app/build/mod.rs b/src/app/build/mod.rs index d14dde7..118a2e8 100644 --- a/src/app/build/mod.rs +++ b/src/app/build/mod.rs @@ -1,49 +1 @@ -use std::fs; - -use self::asciidoctor::{build_doc, build_slide}; - -use super::fs_util; - pub mod asciidoctor; - -pub fn docki_build(in_path: &str, offline_reveal: bool, docs_dir: &str) -> DockiBuildResult { - let out_path = in_path.replace(docs_dir, "./dist"); - let convert_out_path = out_path.replace(".adoc", ".html"); - - if in_path.starts_with(format!("{}/slides/", docs_dir).as_str()) && in_path.ends_with(".adoc") { - if let Err(err) = build_slide(&in_path, &convert_out_path, offline_reveal) { - return DockiBuildResult::Err(err); - } - - DockiBuildResult::Slide(convert_out_path) - } else if in_path.ends_with(".adoc") { - if let Err(err) = build_doc(&in_path, &convert_out_path) { - return DockiBuildResult::Err(err); - } - - DockiBuildResult::Doc(convert_out_path) - } else { - if let Err(err) = copy(&in_path, &out_path) { - return DockiBuildResult::Err(err); - } - - DockiBuildResult::Copy(out_path) - } -} - -fn copy(in_path: &str, out_path: &str) -> Result<(), String> { - fs_util::create_parent_dir_recursive(out_path); - - if let Err(err) = fs::copy(in_path, out_path) { - return Err(err.to_string()); - } - - Ok(()) -} - -pub enum DockiBuildResult { - Slide(String), - Doc(String), - Copy(String), - Err(String), -} diff --git a/src/app/commands/build.rs b/src/app/commands/build.rs index 45fd69d..13d5977 100644 --- a/src/app/commands/build.rs +++ b/src/app/commands/build.rs @@ -3,6 +3,8 @@ use crate::app::config::config::Config; use super::executions::build_execution::BuildExecution; pub async fn build(config: &Config) -> () { - let mut build_execution = BuildExecution::new(&config.input_dir); - build_execution.execute(&config).await.expect("build failed") + let mut build_execution = BuildExecution::new(config); + + build_execution.prepare().await.expect("could not prepare for build"); + build_execution.build_dir().expect("build failed") } diff --git a/src/app/commands/executions/build_execution.rs b/src/app/commands/executions/build_execution.rs index 0628d93..0fb156b 100644 --- a/src/app/commands/executions/build_execution.rs +++ b/src/app/commands/executions/build_execution.rs @@ -1,52 +1,41 @@ use std::{ - io::Cursor, path::PathBuf + fs, io::Cursor, path::PathBuf }; use crate::app::{ - build::{DockiBuildResult, docki_build}, config::config::Config, fs_util::{self, create_dir_recursive}, log::display_status + build::{asciidoctor::build_slide, asciidoctor::build_doc}, config::config::Config, fs_util::{self, create_dir_recursive}, log::display_status }; -pub struct BuildExecution { +pub struct BuildExecution<'a> { progress: usize, goal: usize, - docs_dir: String + config: &'a Config } -impl BuildExecution { - pub fn new(docs_dir: &str) -> Self { - return BuildExecution { +impl <'a> BuildExecution <'a> { + pub fn new(config: &'a Config) -> Self { + return Self { progress: 0, goal: 0, - docs_dir: docs_dir.to_string() + config: config }; } - pub async fn execute(&mut self, config: &Config) -> Result<(), String> { - let path = self.docs_dir.to_string(); - - if !fs_util::directory_exists(&path) { + pub async fn prepare(&self) -> Result<(), String> { + if !fs_util::directory_exists(&self.config.input_dir) { return Err( "docs directory does not exist it. Create it or use the template".to_string(), ); } - if let Err(error) = self.prepare(config.offline_reveal).await { - return Err(error); - } - - return self.build_dir(&path, config.offline_reveal); - } - - - async fn prepare(&self, offline_reveal: bool) -> Result<(), String> { - if !offline_reveal { + if !self.config.offline_reveal { return Ok(()) } let reveal_version = "5.2.1"; let target = format!("https://github.com/hakimel/reveal.js/archive/{reveal_version}.zip"); - create_dir_recursive(format!("{}/slides", self.docs_dir).as_str()); + create_dir_recursive(format!("{}/slides", self.config.input_dir).as_str()); reqwest::get(target.clone()).await.unwrap(); let Ok(response) = reqwest::get(target).await else { @@ -57,7 +46,7 @@ impl BuildExecution { return Err("could not extract bytes".to_string()) }; - let out = PathBuf::from(format!("{}/slides/revealjs", self.docs_dir)); + let out = PathBuf::from(format!("{}/slides/revealjs", self.config.input_dir)); if zip_extract::extract(Cursor::new(bytes), &out, true).is_err() { return Err("could not write extracted archive to disk".to_string()); @@ -66,23 +55,22 @@ impl BuildExecution { return Ok(()); } - fn build_dir(&mut self, path: &str, offline_reveal: bool) -> Result<(), String> { - let result = fs_util::fetch_paths_recursive(&path); - + pub fn build_dir(&mut self) -> Result<(), String> { + let result = fs_util::fetch_paths_recursive(&self.config.input_dir); let Ok(paths) = result else { return Err(result.unwrap_err()) }; - let reveal_dir = format!("{}/slides/revealjs", path); + let reveal_dir = format!("{}/slides/revealjs", self.config.input_dir); let paths = paths.into_iter() - .filter(|path| offline_reveal || !path.starts_with(reveal_dir.as_str())) + .filter(|path| self.config.offline_reveal || !path.starts_with(reveal_dir.as_str())) .collect::>(); self.goal = paths.len(); for (index, in_path) in paths.iter().enumerate() { self.progress = index + 1; - let result = docki_build(&in_path, offline_reveal, &self.docs_dir); + let result = self.build_file(&in_path); match result { DockiBuildResult::Err(err) => { @@ -98,9 +86,52 @@ impl BuildExecution { return Ok(()); } + pub fn build_file(&mut self, path: &str) -> DockiBuildResult { + let out_path = path.replace(&self.config.input_dir, "./dist"); + let convert_out_path = out_path.replace(".adoc", ".html"); + + if path.starts_with(format!("{}/slides/", &self.config.input_dir).as_str()) && path.ends_with(".adoc") { + if let Err(err) = build_slide(&path, &convert_out_path, self.config.offline_reveal) { + return DockiBuildResult::Err(err); + } + + DockiBuildResult::Slide(convert_out_path) + } else if path.ends_with(".adoc") { + if let Err(err) = build_doc(&path, &convert_out_path) { + return DockiBuildResult::Err(err); + } + + DockiBuildResult::Doc(convert_out_path) + } else { + if let Err(err) = Self::copy(&path, &out_path) { + return DockiBuildResult::Err(err); + } + + DockiBuildResult::Copy(out_path) + } + } + + fn copy(in_path: &str, out_path: &str) -> Result<(), String> { + fs_util::create_parent_dir_recursive(out_path); + + if let Err(err) = fs::copy(in_path, out_path) { + return Err(err.to_string()); + } + + Ok(()) + } + fn display_building_status(&self, status_type: &str, in_path: &str, out_path: &str) -> () { let progress_str = format!("{} / {}", self.progress, self.goal); display_status(&progress_str, status_type, in_path, out_path); } } + + +pub enum DockiBuildResult { + Slide(String), + Doc(String), + Copy(String), + Err(String), +} diff --git a/src/app/commands/serve.rs b/src/app/commands/serve.rs index 3726c30..fdf0267 100644 --- a/src/app/commands/serve.rs +++ b/src/app/commands/serve.rs @@ -7,78 +7,104 @@ use notify::{ }; use std::{env, path::Path}; -use crate::app::{ build::{DockiBuildResult, docki_build}, commands::build::build, config::config::Config, log::display_status, watcher::watcher}; - +use crate::app::{ commands::executions::build_execution::{self, BuildExecution, DockiBuildResult}, config::config::Config, log::display_status, watcher::watcher}; pub async fn serve(config: &Config) { - build(config).await; - tokio::join!(watch_and_build(&config.input_dir), start_server(config.port)); + let build_execution = BuildExecution::new(config); + let mut server = Server::new(build_execution, config); + server.serve().await; } -async fn watch_and_build(docs_dir: &str) { - watch(Path::new(docs_dir), docs_dir) - .await - .expect("something went wrong") +struct Server<'a> { + build_execution: BuildExecution<'a>, + config: &'a Config, +} + +impl <'a> Server <'a> { + pub fn new(build_execution: BuildExecution<'a>, config: &'a Config) -> Self { + return Self { + build_execution: build_execution, + config: config + } + } + + async fn serve(&mut self) { + self.build_execution.prepare().await.expect("could not prepare for build"); + self.build_execution.build_dir().expect("build failed"); + tokio::join!(self.start_server(), self.watch_and_build()); + } + + async fn start_server(&self) { + let link = &format!("http://localhost:{}", self.config.port); + let hyperlink = Green.paint(link).hyperlink(link); + + println!( + "\nServing at {}", + hyperlink + ); + + let Ok(()) = listen("localhost", self.config.port, "./dist").await else { + panic!("could not start server") + }; + } + + async fn watch_and_build(&mut self) { + self.watch() + .await + .expect("something went wrong") + } + + async fn watch(&mut self) -> notify::Result<()> { + let path = Path::new(&self.config.input_dir); + let (mut watcher, mut rx) = watcher()?; + + watcher.watch(path.as_ref(), RecursiveMode::Recursive)?; + + while let Some(res) = rx.next().await { + let event = res.expect("watching failed"); + self.file_change(event) + } + + Ok(()) + + } + + fn file_change(&mut self, event: Event) { + match event.kind { + EventKind::Modify(ModifyKind::Data(_)) => self.build_file(event.paths), + _ => (), + } + } + + fn build_file(&mut self, paths: Vec) { + let invalid_path_message = "changed path is invalid"; + let in_path = paths + .first() + .expect(invalid_path_message) + .strip_prefix(¤t_dir()) + .expect(invalid_path_message) + .to_str() + .expect(invalid_path_message); + + let in_path = format!("./{}", in_path); + let result = self.build_execution.build_file(&in_path); + + match result { + DockiBuildResult::Slide(out_path) => display_rebuilding_status("Slide", &in_path, &out_path), + DockiBuildResult::Doc(out_path) => display_rebuilding_status("Doc", &in_path, &out_path), + DockiBuildResult::Copy(out_path) => display_rebuilding_status("Copy", &in_path, &out_path), + DockiBuildResult::Err(err) => { + display_rebuilding_status("Error", &in_path, ""); + println!("{}", err); + }, + } + } } -async fn start_server(port: u16) { - let link = &format!("http://localhost:{}", port); - let hyperlink = Green.paint(link).hyperlink(link); - println!( - "\nServing at {}", - hyperlink - ); - let Ok(()) = listen("localhost", port, "./dist").await else { - panic!("could not start server") - }; -} -async fn watch(path: &Path, docs_dir: &str) -> notify::Result<()> { - let (mut watcher, mut rx) = watcher()?; - - watcher.watch(path.as_ref(), RecursiveMode::Recursive)?; - - while let Some(res) = rx.next().await { - let event = res.expect("watching failed"); - file_change(event, docs_dir) - } - - Ok(()) -} - -fn file_change(event: Event, docs_dir: &str) { - match event.kind { - EventKind::Modify(ModifyKind::Data(_)) => build_file(event.paths, docs_dir), - _ => (), - } -} - -fn build_file(paths: Vec, docs_dir: &str) { - let invalid_path_message = "changed path is invalid"; - let in_path = paths - .first() - .expect(invalid_path_message) - .strip_prefix(¤t_dir()) - .expect(invalid_path_message) - .to_str() - .expect(invalid_path_message); - - let in_path = format!("./{}", in_path); - let result = docki_build(&in_path, false, docs_dir); - - match result { - DockiBuildResult::Slide(out_path) => display_rebuilding_status("Slide", &in_path, &out_path), - DockiBuildResult::Doc(out_path) => display_rebuilding_status("Doc", &in_path, &out_path), - DockiBuildResult::Copy(out_path) => display_rebuilding_status("Copy", &in_path, &out_path), - DockiBuildResult::Err(err) => { - display_rebuilding_status("Error", &in_path, ""); - println!("{}", err); - }, - } -} fn display_rebuilding_status(context: &str, in_path: &str, out_path: &str) { display_status("Rebuildng", context, in_path, out_path)