/* * Copyright (c) 2023 Emma Tebibyte * SPDX-License-Identifier: AGPL-3.0-or-later * * This file is part of Plaque. * * Plaque is free software: you can redistribute it and/or modify it under the * terms of the GNU Affero General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Plaque is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more * details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see https://www.gnu.org/licenses/. */ mod config; use std::{ env::args, io::{ Read, Write }, fs::{ File, ReadDir, read_dir }, }; use config::*; use pulldown_cmark::{ Parser, Options, html }; use toml::Value; use tl::ParserOptions; use yacexits::*; fn get_contents(dir: ReadDir) -> Result, (String, u32)> { let mut out: Vec<(String, Value)> = Vec::new(); let mut md: String; let mut toml = String::new(); for entry in dir { let mut buf = Vec::new(); let file = match entry { Ok(file) => file, Err(_) => break, }; let file_name = match file.file_name().into_string() { Ok(name) => name, Err(_) => { return Err((format!("Invalid file name."), EX_DATAERR)); }, }; let contents = match File::open(&file_name) { Ok(mut md_file) => { md_file.read_to_end(&mut buf).unwrap(); String::from_utf8(buf).unwrap() }, Err(_) => { return Err(( format!("{}: No such file or directory.", file_name), EX_UNAVAILABLE )); }, }; let mut lines = contents.lines(); if lines.next() == Some("+++\n") { let mut line = lines.next(); while line.unwrap() != "+++\n" { toml.push_str(&mut line.unwrap()); line = lines.next(); } md = lines.collect::>().as_mut_slice().join(""); let parsed_toml = match toml.parse::() { Ok(val) => val, Err(_) => { return Err(( format!( "{}: Could not parse TOML.", file_name, ), EX_DATAERR, )); }, }; out.push((md, parsed_toml)); } } Ok(out) } fn main() { let argv = args().collect::>(); let mut options = Options::empty(); options.insert(Options::all()); let config_path = match get_path("XDG_CONFIG_DIR") { Ok(path) => path, Err((err, code)) => { eprintln!("{}: {}", argv[0], err); exit(code); }, }; let config = match Config::read_config(config_path) { Ok(val) => val, Err((err, code)) => { eprintln!("{}: {}", argv[0], err); exit(code); } }; let content_files = match read_dir(&config.content_dir) { Ok(files) => files, Err(_) => { eprintln!("{}: {}: Directory not found.", argv[0], &config.content_dir); exit(EX_UNAVAILABLE); }, }; let files = match get_contents(content_files) { Ok(files) => files, Err((err, code)) => { eprintln!("{}: {}", argv[0], err); exit(code); }, }; for element in files.iter() { let (md, toml) = element; let meta_info = match PageMeta::read_meta(toml.to_owned()) { Ok(info) => info, Err((err, code)) => { eprintln!("{}: {}", argv[0], err); exit(code); }, }; let mut template = match File::open(format!( "{}/{}.html", config.template_dir, meta_info.template, )) { Ok(file) => file, /* * TODO: better matching: * Err(err) => { * match err { * whatever_message => File does not exist * whichever_message => whatever else */ Err(_) => { eprintln!("{}: {}: File does not exist.", argv[0], meta_info.template); exit(EX_OSERR); }, }; let mut html: Vec = Vec::new(); match template.read_to_end(&mut html) { Ok(_) => {}, Err(err) => { eprintln!("{}: {}", argv[0], err); exit(1); }, }; let parsed_html = match String::from_utf8(html) { Ok(html) => html, Err(err) => { eprintln!("{}: {}", argv[0], err); exit(1); }, }; let dom = match tl::parse(&parsed_html, ParserOptions::default()) { Ok(dom) => dom, Err(err) => { eprintln!("{}: {}", argv[0], err); exit(1); }, }; let parser = dom.parser(); let mut html_file = match File::create(format!( "{}/{}.html", config.output_dir, meta_info.template, )) { Ok(file) => file, Err(_) => { eprintln!( "{}: {}: Unable to create output file.", argv[0], config.output_dir, ); exit(EX_OSERR); }, }; let mut output = String::new(); html::push_html(&mut output, Parser::new_ext(&md, options)); match html_file.write(output.as_bytes()) { Ok(_) => {}, Err(err) => { eprintln!("{}: {}", argv[0], err); exit(EX_SOFTWARE); }, }; } }