mod config;
mod fetch_documents;
mod generate_html_pages;
mod inventory;
mod server;
use std::fs;
use std::path::Path;
use std::path::PathBuf;
use std::process;
use anyhow::Context;
use anyhow::Result;
use clap::Parser;
use clap::Subcommand;
use clap::crate_version;
use flexi_logger::Cleanup;
use flexi_logger::Criterion;
use flexi_logger::Duplicate;
use flexi_logger::FileSpec;
use flexi_logger::Logger;
use flexi_logger::Naming;
use flexi_logger::WriteMode;
use flexi_logger::colored_default_format;
use flexi_logger::detailed_format;
use log::error;
use log::info;
use crate::config::Config;
use crate::config::Site;
use crate::config::Source;
use crate::fetch_documents::Document;
use crate::fetch_documents::fetch_documents;
use crate::generate_html_pages::generate_html_error_pages;
use crate::generate_html_pages::generate_html_index_pages;
use crate::inventory::Inventory;
use crate::inventory::Module;
use crate::server::serve;
#[derive(Debug, Parser)]
#[command(version, about, long_about = None)]
struct Cli {
#[command(subcommand)]
command: Command,
}
#[derive(Debug, Subcommand)]
enum Command {
ValidateConfig {
#[arg(long, value_name = "PATH")]
file: PathBuf,
},
ValidateInventory {
#[arg(long, value_name = "PATH")]
file: PathBuf,
},
Serve {
#[arg(long, default_value = "0.0.0.0:8080")]
address: String,
#[arg(long, default_value = "config.toml", value_name = "PATH")]
config: PathBuf,
},
}
#[tokio::main]
async fn main() {
let cli = Cli::parse();
let _logger = Logger::try_with_str("info")
.unwrap()
.log_to_file(
FileSpec::default()
.directory("logs")
.basename("typst4ei")
.suffix("log"),
)
.rotate(
Criterion::Size(30 * 1024 * 1024), Naming::Timestamps,
Cleanup::KeepLogFiles(15),
)
.duplicate_to_stderr(Duplicate::Info)
.format_for_files(detailed_format)
.format_for_stderr(colored_default_format)
.write_mode(WriteMode::Async)
.print_message()
.start()
.unwrap();
info!("starting Typst4EI v{}", crate_version!());
match run(cli).await {
Ok(_) => process::exit(0),
Err(err) => {
error!("{:?}", err);
process::exit(1);
}
}
}
static CSS: &str = include_str!("../static/pico.min.css");
async fn run(cli: Cli) -> Result<()> {
match cli.command {
Command::ValidateConfig { file } => {
Config::load(&file)?;
info!("config `{}` is valid", file.display());
Ok(())
}
Command::ValidateInventory { file } => {
let data = fs::read(&file)
.with_context(|| format!("unable to read from `{}`", file.display()))?;
Inventory::load(
String::from_utf8(data)
.with_context(|| format!("unable to parse `{}` as UTF-8", file.display()))?,
)?;
info!("inventory `{}` is valid", file.display());
Ok(())
}
Command::Serve { address, config } => {
let config = Config::load(&config)?;
fs::create_dir_all(config.site.static_dir.clone()).with_context(|| {
format!("unable to create directory `{}`", config.site.static_dir)
})?;
fs::create_dir_all(config.site.files_dir.clone()).with_context(|| {
format!("unable to create directory `{}`", config.site.files_dir)
})?;
let css_path = Path::new(&config.site.static_dir).join("pico.min.css");
fs::write(&css_path, CSS)
.with_context(|| format!("unable to create file `{}`", css_path.display()))?;
serve(config, address).await
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn verify_cli() {
use clap::CommandFactory;
Cli::command().debug_assert();
}
}