aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock4
-rw-r--r--Cargo.toml2
-rw-r--r--Makefile2
-rw-r--r--man/page.124
-rw-r--r--src/configuration.rs74
-rw-r--r--src/main.rs25
-rw-r--r--test_utilities/Cargo.toml2
-rw-r--r--tests/cli_test.rs42
8 files changed, 119 insertions, 56 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 1d4065d..ba28a7b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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"
diff --git a/Cargo.toml b/Cargo.toml
index f57f13e..0cb0796 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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."
diff --git a/Makefile b/Makefile
index b21e164..bcfa02a 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/man/page.1 b/man/page.1
index 172c963..959437d 100644
--- a/man/page.1
+++ b/man/page.1
@@ -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");