diff --git a/src/app/build/mod.rs b/src/app/build/mod.rs index 118a2e8..9e6a7a7 100644 --- a/src/app/build/mod.rs +++ b/src/app/build/mod.rs @@ -1 +1,150 @@ pub mod asciidoctor; + +use std::{ + fs, io::Cursor, path::PathBuf +}; + +use crate::app::{ + build::{asciidoctor::build_slide, asciidoctor::build_doc}, config::config::Config, fs_util::{self, create_dir_recursive}, log::display_status +}; + +pub struct DockiBuilder<'a> { + progress: usize, + goal: usize, + config: &'a Config +} + +impl <'a> DockiBuilder <'a> { + pub fn new(config: &'a Config) -> Self { + return Self { + progress: 0, + goal: 0, + config: config + }; + } + + + /// + /// Prepares everything for building the documentation + /// + /// 1. Checks if the input directory exists and if not, returns an error + /// 2. When offline_reveal is set to true, it downloads revealjs. When it fails, it returns an error + 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 !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.config.input_dir).as_str()); + + reqwest::get(target.clone()).await.unwrap(); + let Ok(response) = reqwest::get(target).await else { + return Err("could not downlaod revealjs".to_string()) + }; + + let Ok(bytes) = response.bytes().await else { + return Err("could not extract bytes".to_string()) + }; + + 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()); + } + + return Ok(()); + } + + + /// Builds all files in the input directory with a pretty output + pub fn build_docs(&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", self.config.input_dir); + let paths = paths.into_iter() + .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 = self.build_file(&in_path); + + match result { + DockiBuildResult::Err(err) => { + self.display_building_status("Error", &in_path, ""); + println!("{}", err) + }, + DockiBuildResult::Copy(out_path) => self.display_building_status("Copy", &in_path, &out_path), + DockiBuildResult::Slide(out_path) => self.display_building_status("Slide", &in_path, &out_path), + DockiBuildResult::Doc(out_path) => self.display_building_status("Doc", &in_path, &out_path), + } + } + + return Ok(()); + } + + + /// Builds a single file without a pretty output + pub fn build_file(&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); + } + +} + + +/// Used for the Result of build_file. This way it is known what the builder did +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 13d5977..37dee22 100644 --- a/src/app/commands/build.rs +++ b/src/app/commands/build.rs @@ -1,10 +1,10 @@ use crate::app::config::config::Config; -use super::executions::build_execution::BuildExecution; +use crate::app::build::DockiBuilder; pub async fn build(config: &Config) -> () { - let mut build_execution = BuildExecution::new(config); + let mut builder = DockiBuilder::new(config); - build_execution.prepare().await.expect("could not prepare for build"); - build_execution.build_dir().expect("build failed") + builder.prepare().await.expect("could not prepare for build"); + builder.build_docs().expect("build failed") } diff --git a/src/app/commands/executions/build_execution.rs b/src/app/commands/executions/build_execution.rs deleted file mode 100644 index 0fb156b..0000000 --- a/src/app/commands/executions/build_execution.rs +++ /dev/null @@ -1,137 +0,0 @@ -use std::{ - fs, io::Cursor, path::PathBuf -}; - -use crate::app::{ - build::{asciidoctor::build_slide, asciidoctor::build_doc}, config::config::Config, fs_util::{self, create_dir_recursive}, log::display_status -}; - -pub struct BuildExecution<'a> { - progress: usize, - goal: usize, - config: &'a Config -} - -impl <'a> BuildExecution <'a> { - pub fn new(config: &'a Config) -> Self { - return Self { - progress: 0, - goal: 0, - config: config - }; - } - - 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 !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.config.input_dir).as_str()); - - reqwest::get(target.clone()).await.unwrap(); - let Ok(response) = reqwest::get(target).await else { - return Err("could not downlaod revealjs".to_string()) - }; - - let Ok(bytes) = response.bytes().await else { - return Err("could not extract bytes".to_string()) - }; - - 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()); - } - - return Ok(()); - } - - 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", self.config.input_dir); - let paths = paths.into_iter() - .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 = self.build_file(&in_path); - - match result { - DockiBuildResult::Err(err) => { - self.display_building_status("Error", &in_path, ""); - println!("{}", err) - }, - DockiBuildResult::Copy(out_path) => self.display_building_status("Copy", &in_path, &out_path), - DockiBuildResult::Slide(out_path) => self.display_building_status("Slide", &in_path, &out_path), - DockiBuildResult::Doc(out_path) => self.display_building_status("Doc", &in_path, &out_path), - } - } - - 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/executions/mod.rs b/src/app/commands/executions/mod.rs deleted file mode 100644 index ea118d9..0000000 --- a/src/app/commands/executions/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod build_execution; diff --git a/src/app/commands/mod.rs b/src/app/commands/mod.rs index ad710a8..2f81ee1 100644 --- a/src/app/commands/mod.rs +++ b/src/app/commands/mod.rs @@ -1,6 +1,5 @@ pub mod build; pub mod completions; -pub mod executions; pub mod health; pub mod install_reveal; pub mod serve; diff --git a/src/app/commands/serve.rs b/src/app/commands/serve.rs index fdf0267..541a187 100644 --- a/src/app/commands/serve.rs +++ b/src/app/commands/serve.rs @@ -7,30 +7,33 @@ use notify::{ }; use std::{env, path::Path}; -use crate::app::{ commands::executions::build_execution::{self, BuildExecution, DockiBuildResult}, config::config::Config, log::display_status, watcher::watcher}; +use crate::app::watcher::watcher; +use crate::app::log::display_status; +use crate::app::config::config::Config; +use crate::app::build::{DockiBuildResult, DockiBuilder}; pub async fn serve(config: &Config) { - let build_execution = BuildExecution::new(config); - let mut server = Server::new(build_execution, config); + let builder = DockiBuilder::new(config); + let mut server = Server::new(builder, config); server.serve().await; } struct Server<'a> { - build_execution: BuildExecution<'a>, + builder: DockiBuilder<'a>, config: &'a Config, } impl <'a> Server <'a> { - pub fn new(build_execution: BuildExecution<'a>, config: &'a Config) -> Self { + fn new(builder: DockiBuilder<'a>, config: &'a Config) -> Self { return Self { - build_execution: build_execution, + builder, 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"); + self.builder.prepare().await.expect("could not prepare for build"); + self.builder.build_docs().expect("build failed"); tokio::join!(self.start_server(), self.watch_and_build()); } @@ -48,13 +51,13 @@ impl <'a> Server <'a> { }; } - async fn watch_and_build(&mut self) { + async fn watch_and_build(&self) { self.watch() .await .expect("something went wrong") } - async fn watch(&mut self) -> notify::Result<()> { + async fn watch(&self) -> notify::Result<()> { let path = Path::new(&self.config.input_dir); let (mut watcher, mut rx) = watcher()?; @@ -69,54 +72,50 @@ impl <'a> Server <'a> { } - fn file_change(&mut self, event: Event) { + fn file_change(&self, event: Event) { match event.kind { - EventKind::Modify(ModifyKind::Data(_)) => self.build_file(event.paths), + EventKind::Modify(ModifyKind::Data(_)) => self.build_valid_files(event.paths), _ => (), } } - fn build_file(&mut self, paths: Vec) { + fn build_valid_files(&self, paths: Vec) { let invalid_path_message = "changed path is invalid"; let in_path = paths .first() .expect(invalid_path_message) - .strip_prefix(¤t_dir()) + .strip_prefix(&Self::current_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); + let result = self.builder.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::Slide(out_path) => Self::display_rebuilding_status("Slide", &in_path, &out_path), + DockiBuildResult::Doc(out_path) => Self::display_rebuilding_status("Doc", &in_path, &out_path), + DockiBuildResult::Copy(out_path) => Self::display_rebuilding_status("Copy", &in_path, &out_path), DockiBuildResult::Err(err) => { - display_rebuilding_status("Error", &in_path, ""); + Self::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) + } + + fn current_dir() -> String { + let err_message = "something went wrong"; + return String::from( + env::current_dir() + .expect(err_message) + .to_str() + .expect(err_message), + ); + } } - - - - -fn display_rebuilding_status(context: &str, in_path: &str, out_path: &str) { - display_status("Rebuildng", context, in_path, out_path) -} - -fn current_dir() -> String { - let err_message = "something went wrong"; - return String::from( - env::current_dir() - .expect(err_message) - .to_str() - .expect(err_message), - ); -} - diff --git a/src/app/config/arguments.rs b/src/app/config/arguments.rs index 8ea6819..8d50510 100644 --- a/src/app/config/arguments.rs +++ b/src/app/config/arguments.rs @@ -37,7 +37,7 @@ pub enum CommandArg { Build { /// When set to true, docki will download revealjs before building the documentation. /// Otherwise it will use the cdn for revealjs - #[arg(short, long)] + #[arg(long)] offline_reveal: bool, }, /// Checks if everything required for docki is installed