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
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))
|
|
}
|