You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

194 lines
5.0 KiB
Rust

use chrono::{DateTime, NaiveDateTime};
use clap::Parser;
use flate2::read::GzDecoder;
use fs_extra::copy_items;
use serde_json;
use std::{fs, fs::File, io::Read, path::Path, str::FromStr};
use tar::Archive;
#[cfg(debug_assertions)]
use crate::render::render_client;
#[cfg(not(debug_assertions))]
use crate::render::render_ssr;
use crate::{
cli::GeneratorOptions,
data::{Outbox, Person},
error::Ærror,
};
mod cli;
mod data;
mod error;
mod render;
fn main() {
let result = generate();
if let Err(err) = result {
eprintln!("{}", err);
std::process::exit(1);
}
}
fn generate() -> Result<(), Ærror> {
let args;
#[cfg(not(debug_assertions))]
{
args = GeneratorOptions::parse();
setup_dirs(&args)?;
}
#[cfg(debug_assertions)]
{
args = GeneratorOptions {
archive_file: String::from_str("")?,
title: String::from_str("")?,
output_dir: String::from_str("")?,
overwrite: true,
};
}
let (outbox_text, actor_text, archive_time) = read_archive(&args)?;
let (outbox, author) = parse_json(outbox_text, actor_text)?;
#[cfg(debug_assertions)]
{
render_client(outbox, author, archive_time)?;
}
#[cfg(not(debug_assertions))]
{
let output_str = render_ssr(outbox, author, archive_time)?;
fs::write("out/index.html", output_str);
copy_items(
&mut vec!["resources/"],
"out",
&fs_extra::dir::CopyOptions::new(),
);
}
Ok(())
}
fn setup_dirs(args: &GeneratorOptions) -> Result<(), crate::error::Ærror> {
let output_dir = Path::new(&args.output_dir);
let media_dir = output_dir.join("media");
// Create output dir
if !output_dir.exists() {
fs::create_dir(output_dir)?;
fs::create_dir(media_dir)?;
} else if !output_dir.read_dir()?.next().is_none() && !args.overwrite {
return Err(Ærror::new(format!(
"Output directory {} exists but --overwrite not given",
args.output_dir
)));
} else {
fs::remove_dir_all(output_dir)?;
fs::create_dir(output_dir)?;
fs::create_dir(media_dir)?;
}
Ok(())
}
fn read_archive(
args: &GeneratorOptions,
) -> Result<(String, String, DateTime<chrono::Utc>), crate::error::Ærror> {
#[cfg(not(debug_assertions))]
let path = &args.archive_file;
let output_dir = Path::new(&args.output_dir);
let media_dir = output_dir.join("media");
let tar;
#[cfg(not(debug_assertions))]
{
let tar_gz = File::open(path)?;
tar = GzDecoder::new(tar_gz);
}
#[cfg(debug_assertions)]
{
let tar_bytes = include_bytes!("../test-archive.tar.gz");
let tar_gz: &[u8] = tar_bytes.as_slice();
tar = GzDecoder::new(tar_gz);
}
let mut archive = Archive::new(tar);
let mut outbox_text = String::new();
let mut actor_text = String::new();
let mut archive_time: Option<DateTime<chrono::Utc>> = None;
for (_, file) in archive.entries()?.enumerate() {
let mut file = file?;
let path_str: String;
{
let path_result = file.path();
let path = path_result?.clone();
path_str = String::from_str(&path.to_string_lossy()).unwrap();
}
if archive_time.is_none() {
let mtime = file.header().mtime()?;
archive_time = Some(DateTime::<chrono::Utc>::from_utc(
NaiveDateTime::from_timestamp_opt(mtime as i64, 0).unwrap(),
chrono::Utc,
));
}
if path_str.starts_with("media_attachments") {
#[cfg(not(debug_assertions))]
file.unpack_in(&args.output_dir)?;
} else {
match path_str.as_str() {
"outbox.json" => {
file.read_to_string(&mut outbox_text)?;
}
"actor.json" => {
file.read_to_string(&mut actor_text)?;
}
"avatar.png" => {
#[cfg(not(debug_assertions))]
file.unpack_in(&media_dir)?;
}
"header.png" => {
#[cfg(not(debug_assertions))]
file.unpack_in(&media_dir)?;
}
_ => (),
};
}
}
if archive_time.is_none() {
return Err(Ærror::new_str("No files in archive"));
}
if outbox_text.len() < 1 {
return Err(Ærror::new_str("No outbox.json in archive"));
}
if actor_text.len() < 1 {
return Err(Ærror::new_str("No actor.json in archive"));
}
Ok((outbox_text, actor_text, archive_time.unwrap()))
}
fn parse_json(
outbox_text: String,
actor_text: String,
) -> Result<(Outbox, Person), crate::error::Ærror> {
let outbox: Outbox = serde_json::from_str(outbox_text.as_str())?;
let actor: Person = serde_json::from_str(actor_text.as_str())?;
Ok((outbox, actor))
}