diff --git a/src/app.rs b/src/app.rs index 720744b..129f90d 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,71 +1,157 @@ +use std::fs::read_to_string; + +use anyhow::Context; +use clap::{Arg, Command}; + +#[derive(serde::Serialize, serde::Deserialize)] +pub enum TemplateKind { + Builtin, + FromLua(String), + FromTemplate(String), +} + +impl Default for TemplateKind { + fn default() -> Self { + Self::Builtin + } +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct Config { + template: TemplateKind, + page_root: String, + store_in: String, + name: String, +} +impl Default for Config { + fn default() -> Self { + Self { + template: Default::default(), + page_root: Default::default(), + store_in: "pages".into(), + name: "Your_API".into(), + } + } +} + pub(crate) struct Paths { pub(crate) json: String, pub(crate) name: String, pub(crate) root: String, pub(crate) build_dir: String, + pub(crate) template_kind: TemplateKind, } pub(crate) enum Modes { Credits, GenerateDocs(Paths), + SelfDocs { + build_dir: String, + }, + GenFile { + file: String, + location: &'static str, + }, + Nothing, } -pub(crate) fn get_paths() -> Modes { - let matches = clap::App::new("tealr doc gen") - .arg( - clap::Arg::new("json") - .long("json") - .short('j') - .takes_value(true) - .help("Path to the json file") - .required_unless_present("credits"), - ) - .arg( - clap::Arg::new("name") - .long("name") - .short('n') - .takes_value(true) - .help("Name of the library") - .required_unless_present("credits"), - ) - .arg( - clap::Arg::new("root") - .long("root") - .short('r') - .takes_value(true) - .help("The root that the pages link to.") - .default_value("./") - .default_missing_value("./"), - ) - .arg( - clap::Arg::new("build_folder") - .long("build_folder") - .short('b') - .takes_value(true) - .help("In which folder to store the generated pages") - .default_value("./pages") - .default_missing_value("./pages"), - ) - .arg( - clap::Arg::new("credits") - .long("credits") - .takes_value(false) - .help("Displays the credits"), - ) - .get_matches(); +pub(crate) fn get_paths() -> Result { + let matches = + clap::App::new("tealr doc gen") + .subcommand( + Command::new("run") + .alias("gen") + .about("Generates the documentation pages"), + ) + .subcommand( + Command::new("gen_self") + .about("Generates files used to add custom behavior to tealr_doc_gen") + .arg(Arg::new("lua_docs").long("lua_docs").help( + "Generates the documentation for the lua api exposed by tealr_doc_gen.", + )) + .arg( + Arg::new("config") + .long("config") + .help("Generates a new config file"), + ) + .arg(Arg::new("doc_template").long("doc_template").help( + "Generates the default template used to generate the documentation pages", + )) + .arg( + Arg::new("doc_lua_kickstart") + .long("doc_lua_kickstart") + .help("Generates the lua code used to build the template"), + ) + .arg( + Arg::new("print") + .long("print") + .short('p') + .help("Prints the file instead of writing it directly to a file"), + ), + ) + .arg( + Arg::new("credits") + .long("credits") + .short('c') + .help("Shows who worked on tealr_doc_gen"), + ) + .get_matches(); if matches.contains_id("credits") { - return Modes::Credits; + return Ok(Modes::Credits); } - let json = matches.value_of("json").unwrap().to_owned(); - let name = matches.value_of("name").unwrap().to_owned(); - let root = matches.value_of("root").unwrap().to_owned(); - let build_dir = matches.value_of("build_folder").unwrap().to_owned(); + if matches.subcommand_matches("run").is_some() { + let config: Config = read_config()?; + return Ok(Modes::GenerateDocs(Paths { + json: config.name.clone() + ".json", + build_dir: config.store_in, + name: config.name, + root: config.page_root, + template_kind: config.template, + })); + } + if let Some(x) = matches.subcommand_matches("gen_self") { + return Ok(if x.contains_id("lua_docs") { + let config = read_config()?; + Modes::SelfDocs { + build_dir: config.store_in, + } + } else { + let (text, location) = if x.contains_id("config") { + ( + serde_json::to_string_pretty(&Config::default())?, + "./tealr_doc_gen_config.json", + ) + } else if x.contains_id("doc_template") { + ( + include_str!("../base_template.etlua").into(), + "./template.etlua", + ) + } else if x.contains_id("doc_lua_kickstart") { + ( + include_str!("../base_run_template.lua").into(), + "./run_template.lua", + ) + } else { + return Err(anyhow::anyhow!("Missing argument")); + }; + if x.contains_id("print") { + println!("{}", text); + Modes::Nothing + } else { + Modes::GenFile { + file: text, + location, + } + } + }); + }; + Ok(Modes::Nothing) +} - Modes::GenerateDocs(Paths { - name, - root, - json, - build_dir, - }) +fn read_config() -> Result { + serde_json::from_str( + &read_to_string("./tealr_doc_gen_config.json") + .context("Could not read tealr_doc_gen_config.json in current directory. Maybe generate one using `tealr_doc_gen gen_self --config`?")? + ).context("Error while parsing the config file. Use `tealr_doc_gen gen_self --config` to generate an example") } diff --git a/src/main.rs b/src/main.rs index 7f93344..e8f0db0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,6 @@ +use app::Paths; +use run_template::generate_self; + use crate::app::get_paths; mod app; @@ -7,14 +10,32 @@ mod doc_gen; mod markdown; mod run_template; fn main() -> anyhow::Result<()> { - match get_paths() { + match get_paths()? { app::Modes::Credits => { credits::show_credits(); - Ok(()) } app::Modes::GenerateDocs(x) => { //generate_docs::generate_docs(x) - run_template::run_template(x) + run_template::run_template(x)?; + } + app::Modes::SelfDocs { build_dir } => { + let walker = generate_self()?; + run_template::run_from_walker( + Paths { + json: "{}".into(), + name: "tealr_doc_gen".into(), + root: "".into(), + build_dir, + template_kind: app::TemplateKind::Builtin, + }, + walker, + )?; + } + app::Modes::GenFile { file, location } => { + //std::fs::create_dir_all(location)?; + std::fs::write(location, file)?; } + app::Modes::Nothing => (), } + Ok(()) } diff --git a/src/run_template.rs b/src/run_template.rs index 337e0b9..6515f9c 100644 --- a/src/run_template.rs +++ b/src/run_template.rs @@ -3,13 +3,15 @@ use std::{ path::{Path, PathBuf}, }; +use anyhow::Context; use tealr::{ mlu::{ExportInstances, FromToLua, TealData}, EnumGenerator, GlobalInstance, NameContainer, RecordGenerator, TypeGenerator, TypeName, + TypeWalker, }; use crate::{ - app::Paths, + app::{Paths, TemplateKind}, doc_gen::{get_type_name, type_should_be_inlined}, }; @@ -134,10 +136,7 @@ impl ExportInstances for GlobalInstances { } } -pub(crate) fn run_template(paths: Paths) -> Result<(), anyhow::Error> { - let json = read_to_string(paths.json)?; - let type_defs: tealr::TypeWalker = serde_json::from_str(&json)?; - +pub(crate) fn run_from_walker(paths: Paths, type_defs: TypeWalker) -> Result<(), anyhow::Error> { let link_path = Path::new("/").join(&paths.root); let sidebar: Vec = type_defs @@ -212,6 +211,7 @@ pub(crate) fn run_template(paths: Paths) -> Result<(), anyhow::Error> { None, None, link_path.clone(), + &paths.template_kind, )?; } run_and_write( @@ -221,10 +221,17 @@ pub(crate) fn run_template(paths: Paths) -> Result<(), anyhow::Error> { Some(type_defs.global_instances_off), Some(type_defs.given_types), link_path, + &paths.template_kind, )?; Ok(()) } +pub(crate) fn run_template(paths: Paths) -> Result<(), anyhow::Error> { + let json = read_to_string(&paths.json)?; + let type_defs: tealr::TypeWalker = serde_json::from_str(&json)?; + run_from_walker(paths, type_defs) +} + fn run_and_write( sidebar: Vec, type_def: &TypeGenerator, @@ -232,15 +239,29 @@ fn run_and_write( global_instances: Option>, all_types: Option>, link_path: PathBuf, + template_kind: &TemplateKind, ) -> Result<(), anyhow::Error> { let type_name = if type_should_be_inlined(type_def) { "index".into() } else { get_type_name(type_def) }; + let base_template = include_str!("../base_template.etlua"); + let base_runner = include_str!("../base_run_template.lua"); + let (template, template_runner) = match template_kind { + TemplateKind::Builtin => (base_template.to_string(), base_runner.to_string()), + TemplateKind::FromLua(x) => ( + base_template.to_string(), + std::fs::read_to_string(x) + .with_context(|| format!("Could not load doc template runner. File {x}"))?, + ), + TemplateKind::FromTemplate(x) => ( + std::fs::read_to_string(x) + .with_context(|| format!("Could not load doc template. File {x}"))?, + base_runner.to_string(), + ), + }; let etlua = include_str!("../etlua.lua").to_string(); - let template = include_str!("../base_template.etlua").to_string(); - let template_runner = include_str!("../base_run_template.lua"); let lua = unsafe { tealr::mlu::mlua::Lua::unsafe_new() }; let instance_setter = GlobalInstances { side_bar: sidebar, @@ -255,7 +276,7 @@ fn run_and_write( tealr::mlu::set_global_env(instance_setter, &lua)?; let document: tealr::mlu::mlua::String = lua - .load(template_runner) + .load(&template_runner) .set_name("template_runner")? .call(())?; let page_path = write_path.join(format!("{type_name}.html")); @@ -263,9 +284,7 @@ fn run_and_write( Ok(()) } -//will be used later :) -#[allow(dead_code)] -fn generate_self() -> Result<(), Box> { +pub fn generate_self() -> Result { let x = tealr::TypeWalker::new() .process_type::() .process_type::() @@ -282,7 +301,5 @@ fn generate_self() -> Result<(), Box> { .process_type::() .process_type_inline::() .document_global_instance::()?; - std::fs::write("./self.json", serde_json::to_string_pretty(&x)?)?; - std::fs::write("./self.d.tl", x.generate_global("tealr_doc_gen")?)?; - Ok(()) + Ok(x) }