Skip to content

Commit

Permalink
feat: add single file support
Browse files Browse the repository at this point in the history
Signed-off-by: Ryan Brue <[email protected]>
  • Loading branch information
ryanabx committed Sep 9, 2024
1 parent b1bc54a commit 7d28d26
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 83 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ authors = ["Ryan Brue <[email protected]>"]
repository = "https://github.com/ryanabx/simple-ssg"
license = "MIT"
readme = "README.md"
version = "3.1.2"
version = "3.2.0"
edition = "2021"
description = "Plain and simple static site generator for Djot and Markdown light markup languages"

Expand Down
6 changes: 4 additions & 2 deletions docs/command_reference.dj
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ cargo build --release
## Usage

```shell
simple-ssg <PATH_TO_TARGET>
simple-ssg [OPTIONS] <TARGET_PATH>
```

### Options

```
-o <OUTPUT_PATH> Optional output path override. Defaults to ./output
Options:
-f Process a single file instead of a directory
-o <OUTPUT_PATH> Optional output path override. Defaults to ./output for directories
--clean Clean the output directory before generating the site. Useful for multiple runs
--no-warn Disallow any warnings
--web-prefix <WEB_PREFIX> Specify the website prefix (defaults to local paths i.e. `./`)
Expand Down
201 changes: 122 additions & 79 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use anyhow::anyhow;
use errors::SsgError;
use jotdown::{Container, Event};
use pulldown_cmark::CowStr;
Expand All @@ -23,11 +24,14 @@ mod utils;
struct ConsoleArgs {
/// Path to the directory to use to generate the site
target_path: PathBuf,
/// Optional output path override. Defaults to ./output
/// Process a single file instead of a directory
#[arg(short, conflicts_with = "clean")]
file: bool,
/// Optional output path override. Defaults to ./output for directories
#[arg(short)]
output_path: Option<PathBuf>,
/// Clean the output directory before generating the site. Useful for multiple runs
#[arg(long)]
#[arg(long, conflicts_with = "file")]
clean: bool,
/// Disallow any warnings
#[arg(long)]
Expand All @@ -50,11 +54,19 @@ fn main() -> anyhow::Result<()> {
}

fn run_program(args: ConsoleArgs) -> anyhow::Result<()> {
let output_path = args
.output_path
.unwrap_or(env::current_dir()?.join("output"));
let output_path = args.output_path.unwrap_or(if args.file {
env::current_dir()?
} else {
env::current_dir()?.join("output")
});
if args.target_path.is_file() && !args.file {
return Err(anyhow!(
"Target path {} is a file! If you meant to specify a file, please specify the -f flag.",
args.target_path.display()
));
}
// Clean the output directory if clean is specified
if args.clean {
if args.clean && args.target_path.is_dir() {
log::debug!(
"Clean argument specified, cleaning output path {:?}...",
&output_path
Expand Down Expand Up @@ -100,86 +112,49 @@ fn generate_site(
"Created output directory {:?} if it didn't exist...",
output_path
);
if !utils::check_has_index(target_path) {
warn_or_error(SsgError::IndexPageNotFound, no_warn)?;
}

let mut first_pass_results = Vec::new();

log::info!("1/3: Site generation and indexing...");
for entry in WalkDir::new(target_path) {
match entry {
Ok(direntry) => {
let relative = match direntry.path().strip_prefix(target_path) {
Ok(relative) => relative.to_path_buf(),
Err(_) => {
warn_or_error(
SsgError::PathNotRelative(direntry.path().to_path_buf()),
no_warn,
)?;
continue;
}
};
log::debug!("{:?} :: {}", &relative, direntry.depth());
if direntry.path().is_dir() {
log::trace!("Path {:?} is a directory, continuing...", direntry.path());
first_pass_results.push(FirstPassResult::Dir {
depth: direntry.depth(),
relative_path: relative,
});
continue;
} else if direntry.path().ends_with("template.html") {
log::trace!("Path {:?} is a template, continuing...", direntry.path());
continue;
}
log::trace!("Path: {:?}", direntry.path());
let new_path = output_path.join(&relative);
let _ = std::fs::create_dir_all(new_path.parent().unwrap());
match direntry.path().extension().map(|x| x.to_str().unwrap()) {
Some("dj") | Some("djot") | Some("md") => {
let html_template = template.clone().map_or(
utils::get_template_if_exists(direntry.path(), target_path)?,
|template| Some(template.get_template()),
);
let result_path = new_path.with_extension("html");
log::debug!(
"Generating .html from {:?} and moving to {:?}",
direntry.path(),
&result_path
);
let input_str = std::fs::read_to_string(direntry.path())?;
let html = match direntry.path().extension().map(|x| x.to_str().unwrap()) {
Some("md") => process_markdown(
&input_str,
direntry.path().parent().unwrap(),
no_warn,
web_prefix,
)?,
Some("dj") | Some("djot") => process_djot(
&input_str,
direntry.path().parent().unwrap(),
no_warn,
web_prefix,
)?,
_ => unreachable!(),
};
let html_formatted =
utils::wrap_html_content(&html, html_template.as_deref());
first_pass_results.push(FirstPassResult::HtmlOutput {
depth: direntry.depth(),
html: html_formatted,
relative_path: relative.with_extension("html"),
});
}
_ => {
std::fs::copy(direntry.path(), &new_path)?;
}
if target_path.is_dir() && output_path.is_dir() {
if !utils::check_has_index(target_path) {
warn_or_error(SsgError::IndexPageNotFound, no_warn)?;
}
for entry in WalkDir::new(target_path) {
match entry {
Ok(direntry) => process_path(
direntry.path(),
target_path,
output_path,
&template,
web_prefix,
no_warn,
direntry.depth(),
&mut first_pass_results,
)?,
Err(e) => {
warn_or_error(SsgError::DirEntryError(e), no_warn)?;
}
}
Err(e) => {
warn_or_error(SsgError::DirEntryError(e), no_warn)?;
}
}
} else if target_path.is_file() {
process_path(
target_path,
target_path.parent().unwrap(),
output_path,
&template,
web_prefix,
no_warn,
1,
&mut first_pass_results,
)?;
} else {
return Err(anyhow!(
"Target path {} is not a file or a directory.",
target_path.display()
));
}

// Validation pass
log::info!("2/3: Generating additional site content (if necessary) and saving...");

Expand Down Expand Up @@ -211,6 +186,74 @@ fn generate_site(
Ok(())
}

fn process_path(
entity: &Path,
target_path: &Path,
output_path: &Path,
template: &Option<BuiltInTemplate>,
web_prefix: Option<&str>,
no_warn: bool,
depth: usize,
first_pass_results: &mut Vec<FirstPassResult>,
) -> anyhow::Result<()> {
let relative = match entity.strip_prefix(target_path) {
Ok(relative) => relative.to_path_buf(),
Err(_) => {
warn_or_error(SsgError::PathNotRelative(entity.to_path_buf()), no_warn)?;
return Ok(());
}
};
log::debug!("{:?} :: {}", &relative, depth);
if entity.is_dir() {
log::trace!("Path {:?} is a directory, continuing...", entity);
first_pass_results.push(FirstPassResult::Dir {
depth,
relative_path: relative,
});
return Ok(());
} else if entity.ends_with("template.html") {
log::trace!("Path {:?} is a template, continuing...", entity);
return Ok(());
}
log::trace!("Path: {:?}", entity);
let new_path = output_path.join(&relative);
let _ = std::fs::create_dir_all(new_path.parent().unwrap());
match entity.extension().map(|x| x.to_str().unwrap()) {
Some("dj") | Some("djot") | Some("md") => {
let html_template = template.clone().map_or(
utils::get_template_if_exists(entity, target_path)?,
|template| Some(template.get_template()),
);
let result_path = new_path.with_extension("html");
log::debug!(
"Generating .html from {:?} and moving to {:?}",
entity,
&result_path
);
let input_str = std::fs::read_to_string(entity)?;
let html = match entity.extension().map(|x| x.to_str().unwrap()) {
Some("md") => {
process_markdown(&input_str, entity.parent().unwrap(), no_warn, web_prefix)?
}
Some("dj") | Some("djot") => {
process_djot(&input_str, entity.parent().unwrap(), no_warn, web_prefix)?
}
_ => unreachable!(),
};
let html_formatted = utils::wrap_html_content(&html, html_template.as_deref());
first_pass_results.push(FirstPassResult::HtmlOutput {
depth: depth,
html: html_formatted,
relative_path: relative.with_extension("html"),
});
}
_ => {
std::fs::copy(entity, &new_path)?;
}
}
Ok(())
}

fn process_markdown(
markdown_input: &str,
file_parent_dir: &Path,
Expand Down
2 changes: 2 additions & 0 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ fn site_with_links() -> anyhow::Result<()> {
no_warn: true,
web_prefix: None,
template: Some(crate::templates::BuiltInTemplate::ForceNone),
file: false,
};
log::trace!("Running program");
crate::run_program(args)?;
Expand Down Expand Up @@ -103,6 +104,7 @@ fn site_warn_without_index() -> anyhow::Result<()> {
no_warn: true,
web_prefix: None,
template: Some(crate::templates::BuiltInTemplate::ForceNone),
file: false,
};
crate::run_program(args)?;
Ok(())
Expand Down

0 comments on commit 7d28d26

Please sign in to comment.