This repository has been archived on 2023-12-14. You can view files and clone it, but cannot push or open issues or pull requests.
plaque/src/main.rs

223 lines
5.2 KiB
Rust

/*
* Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
* 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<Vec<(String, Value)>, (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::<Vec<&str>>().as_mut_slice().join("");
let parsed_toml = match toml.parse::<Value>() {
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::<Vec<String>>();
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<u8> = 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);
},
};
}
}