diff options
| -rw-r--r-- | Cargo.lock | 4 | ||||
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | man/page.1 | 24 | ||||
| -rw-r--r-- | src/configuration.rs | 74 | ||||
| -rw-r--r-- | src/main.rs | 25 | ||||
| -rw-r--r-- | test_utilities/Cargo.toml | 2 | ||||
| -rw-r--r-- | tests/cli_test.rs | 42 |
8 files changed, 119 insertions, 56 deletions
@@ -10,7 +10,7 @@ checksum = "ff101c368b563c1aace5a5f18255a52fcd150b6e97fadd5dd970b6f912f66571" [[package]] name = "page" -version = "1.5.0" +version = "2.0.0" dependencies = [ "gema_texto", "test_utilities", @@ -18,4 +18,4 @@ dependencies = [ [[package]] name = "test_utilities" -version = "1.4.4" +version = "2.0.0" @@ -1,6 +1,6 @@ [package] name = "page" -version = "1.5.0" +version = "2.0.0" edition = "2024" license = "AGPL-3.0-or-later" description = "Command line tool to generate a static website and gemini capsule from a directory with gemtext." @@ -2,7 +2,7 @@ profile := dev target = $(shell rustc -vV | grep host | awk '{print $$2}') architectures := x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu app_name := page -deploy_host := deploy@conchos.unlimited.pizza +deploy_host := deploy@conchos.bdr.sh deploy_path := /srv/http/build.r.bdr.sh/$(app_name) define sign_and_deploy @@ -1,4 +1,4 @@ -.TH PAGE 1 "2024-03-13" "1.4.1" "Page Manual" +.TH PAGE 1 "2025-10-01" "2.0.0" "Page Manual" .SH NAME page \- gemtext based static website generation tool for gemini and http. .SH SYNOPSIS @@ -9,8 +9,24 @@ page is a tool that lets you create a static website from a directory of gemtext files. .PP To use, go to the directory containing the site, and run \fBpage\fR. It will -create two directories in the parent directory using the same name but -appending \fB_html\fR and \fB_gemini\fR. +create +.B html +and +.B gemini +directories inside +.B $PAGE_OUTPUT_DIRECTORY/page/<basename>\fR, +.B $XDG_CACHE_HOME/page/<basename>\fR, +or +.B ~/.cache/page/<basename> +in that order of precedence, where +.B <basename> +is the name of the current directory. +.SH ENVIRONMENT VARIABLES +You can configure the output location of the page using environment variables. +.TP +.B PAGE_OUTPUT_DIRECTORY +Sets the location for the generated static files ready to be published. +Defaults to \fI$XDG_CACHE_HOME/blog\fR, and falls back to \fI~/.cache/blog\fR. .SH FOLDER STRUCTURE The website directory has some requirements for page to work correctly. It must contain \fIgemtext\fR and a \fIlayout\fR, and optionally may contain @@ -86,7 +102,7 @@ For example: Any file that isn't gemtext or the layout will be copied as-is. This includes hidden files! The only ones that are excluded are .git and .gitignore. .SH VERSION -.BR 1.4.1 +.BR 2.0.0 .SH HOMEPAGE .I https://r.bdr.sh/page.html .SH AUTHORS diff --git a/src/configuration.rs b/src/configuration.rs index a09d780..333a27f 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -1,4 +1,5 @@ use std::env; +use std::io::{Error, ErrorKind, Result}; use std::path::PathBuf; pub struct Configuration { @@ -7,31 +8,70 @@ pub struct Configuration { } impl Configuration { + pub fn new() -> Result<Self> { + let output_directory = + Configuration::directory("PAGE_OUTPUT_DIRECTORY", "XDG_CACHE_HOME", ".cache", "page")?; - pub fn new() -> Self { - let output_directory = Configuration::directory( - "PAGE_OUTPUT_DIRECTORY", - "XDG_CACHE_HOME", - ".cache", - "page" - ); - - Configuration { - output_directory, - } + Ok(Configuration { output_directory }) } - fn directory(user_override: &str, default_value: &str, home_fallback: &str, path: &str) -> PathBuf { + fn directory( + user_override: &str, + default_value: &str, + home_fallback: &str, + path: &str, + ) -> Result<PathBuf> { match env::var(user_override) { - Ok(directory) => PathBuf::from(directory), + Ok(directory) => Ok(PathBuf::from(directory).join(path)), Err(_) => match env::var(default_value) { - Ok(directory) => PathBuf::from(directory), + Ok(directory) => Ok(PathBuf::from(directory).join(path)), Err(_) => match env::var("HOME") { - Ok(directory) => PathBuf::from(directory).join(home_fallback), - Err(_) => panic!("Could not find required directory, {} or {} should be set and readable.", user_override, default_value), + Ok(directory) => Ok(PathBuf::from(directory).join(home_fallback).join(path)), + Err(_) => Err(Error::new( + ErrorKind::NotFound, + format!( + "Could not find required directory, {user_override} or {default_value} should be set and readable" + ), + )), }, }, - }.join(path) + } } } +#[cfg(test)] +mod tests { + use super::*; + use std::env; + + use test_utilities::*; + + #[test] + fn test_sets_correct_configuration_directories() { + let default_configuration = Configuration::new().unwrap(); + + let test_dir = setup_test_dir(); + + // SAFETY: Run only in single-threaded mode + unsafe { + env::set_var("PAGE_OUTPUT_DIRECTORY", test_dir.join("output")); + } + let override_configuration = Configuration::new().unwrap(); + + // Ensure our overrides were applied + + assert_eq!( + override_configuration.output_directory, + test_dir.join("output/page") + ); + + // Ensure all the defaults are different from defaults + + assert_ne!( + default_configuration.output_directory, + override_configuration.output_directory + ); + + cleanup_test_dir(&test_dir); + } +} diff --git a/src/main.rs b/src/main.rs index 9dd296a..d0ed57c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +mod configuration; mod file_finder; mod file_handler; @@ -5,10 +6,12 @@ use std::env::current_dir; use std::fs::{create_dir_all, remove_dir_all}; use std::io::{Error, ErrorKind, Result}; -use crate::file_finder::find_files; -use crate::file_handler::FileHandler; +use configuration::Configuration; +use file_finder::find_files; +use file_handler::FileHandler; fn main() -> Result<()> { + let configuration = Configuration::new()?; let source = current_dir()?; let source_name = source .file_name() @@ -19,16 +22,14 @@ fn main() -> Result<()> { ) })? .to_string_lossy(); - let parent = source.parent().ok_or_else(|| { - Error::new( - ErrorKind::InvalidData, - "Current directory parent was not readable.", - ) - })?; - let gemini_destination_name = format!("{source_name}_gemini"); - let gemini_destination = parent.join(gemini_destination_name); - let html_destination_name = format!("{source_name}_html"); - let html_destination = parent.join(html_destination_name); + let gemini_destination = configuration + .output_directory + .join(format!("{source_name}")) + .join("gemini"); + let html_destination = configuration + .output_directory + .join(format!("{source_name}")) + .join("html"); // Step 1. Identify the files let files = find_files(&source); diff --git a/test_utilities/Cargo.toml b/test_utilities/Cargo.toml index accfcde..9da0052 100644 --- a/test_utilities/Cargo.toml +++ b/test_utilities/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "test_utilities" -version = "1.4.4" +version = "2.0.0" edition = "2024" license = "AGPL-3.0-or-later" description = "Shared test utilities for page" diff --git a/tests/cli_test.rs b/tests/cli_test.rs index 641d6f7..5c5615a 100644 --- a/tests/cli_test.rs +++ b/tests/cli_test.rs @@ -1,5 +1,5 @@ use std::env; -use std::fs::read_to_string; +use std::fs::{create_dir_all, read_to_string}; use std::path::PathBuf; use std::process::Command; use test_utilities::*; @@ -19,18 +19,20 @@ impl Drop for TestDir { #[test] fn test_basic_generation() { let test_dir = setup_test_dir(); - let dir_name = test_dir.file_name().unwrap().to_string_lossy(); - let parent = test_dir.parent().unwrap(); - let html_dir = parent.join(format!("{dir_name}_html")); - let gemini_dir = parent.join(format!("{dir_name}_gemini")); + let input_dir = test_dir.join("tezt"); + let output_dir = test_dir.join("output"); + let html_dir = output_dir.join("page").join("tezt").join("html"); + let gemini_dir = output_dir.join("page").join("tezt").join("gemini"); + create_dir_all(&input_dir).expect("Could not create input directory"); + create_dir_all(&output_dir).expect("Could not create output directory"); let _cleanup = TestDir { - paths: vec![test_dir.clone(), html_dir.clone(), gemini_dir.clone()], + paths: vec![test_dir.clone()], }; // Create test input files create_test_file( - &test_dir.join("_layout.html"), + &input_dir.join("_layout.html"), "\ <html> <head><title>{{ title }}</title></head> @@ -39,18 +41,19 @@ fn test_basic_generation() { ", ); create_test_file( - &test_dir.join("test.gmi"), + &input_dir.join("test.gmi"), "\ --- title: Page Is Cool! # Test Hello world ", ); - create_test_file(&test_dir.join("test.png"), "A picture of a cute cat"); + create_test_file(&input_dir.join("test.png"), "A picture of a cute cat"); // Run the program from the test directory let status = Command::new(env!("CARGO_BIN_EXE_page")) - .current_dir(&test_dir) + .current_dir(&input_dir) + .env("PAGE_OUTPUT_DIRECTORY", &output_dir) .status() .expect("Failed to execute command"); @@ -95,29 +98,32 @@ Hello world #[test] fn test_missing_layout() { let test_dir = setup_test_dir(); - let dir_name = test_dir.file_name().unwrap().to_string_lossy(); - let parent = test_dir.parent().unwrap(); - let html_dir = parent.join(format!("{dir_name}_html")); - let gemini_dir = parent.join(format!("{dir_name}_gemini")); + let input_dir = test_dir.join("tezt2"); + let output_dir = test_dir.join("output"); + let html_dir = output_dir.join("page").join("tezt2").join("html"); + let gemini_dir = output_dir.join("page").join("tezt2").join("gemini"); + create_dir_all(&input_dir).expect("Could not create input directory"); + create_dir_all(&output_dir).expect("Could not create output directory"); let _cleanup = TestDir { - paths: vec![test_dir.clone(), html_dir.clone(), gemini_dir.clone()], + paths: vec![test_dir.clone()], }; // Create test input files create_test_file( - &test_dir.join("test.gmi"), + &input_dir.join("test.gmi"), "\ --- title: Page Is Cool! # Test Hello world ", ); - create_test_file(&test_dir.join("test.png"), "A picture of a cute cat"); + create_test_file(&input_dir.join("test.png"), "A picture of a cute cat"); // Run the program from the test directory let status = Command::new(env!("CARGO_BIN_EXE_page")) - .current_dir(&test_dir) + .current_dir(&input_dir) + .env("PAGE_OUTPUT_DIRECTORY", &output_dir) .status() .expect("Failed to execute command"); |