use std::path::PathBuf;
#[derive(Debug, Clone)]
pub struct PublishOptions {
pub single_file: bool,
pub title: Option<String>,
pub audience: Option<String>,
pub force: bool,
pub copy_attachments: bool,
pub base_url: Option<String>,
pub generate_seo: bool,
pub generate_feeds: bool,
}
impl Default for PublishOptions {
fn default() -> Self {
Self {
single_file: false,
title: None,
audience: None,
force: false,
copy_attachments: true,
base_url: None,
generate_seo: true,
generate_feeds: true,
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum PageLayout {
#[default]
Site,
Bare,
Verbatim,
}
impl PageLayout {
pub fn parse(value: Option<&str>) -> Self {
match value.map(str::trim) {
Some("bare") => Self::Bare,
Some("verbatim") => Self::Verbatim,
_ => Self::Site,
}
}
pub fn is_verbatim(self) -> bool {
matches!(self, Self::Verbatim)
}
}
#[derive(Debug, Clone)]
pub struct NavLink {
pub href: String,
pub title: String,
}
#[derive(Debug, Clone)]
pub struct PublishedPage {
pub source_path: PathBuf,
pub dest_filename: String,
pub title: String,
pub rendered_body: String,
pub markdown_body: String,
pub contents_links: Vec<NavLink>,
pub parent_link: Option<NavLink>,
pub is_root: bool,
pub description: Option<String>,
pub author: Option<String>,
pub created: Option<String>,
pub updated: Option<String>,
pub date_of_document: Option<String>,
pub group_keys: Vec<String>,
pub attachments: Vec<String>,
pub styles: Vec<String>,
pub scripts: Vec<String>,
pub layout: PageLayout,
pub shell: Option<String>,
pub nav_title: Option<String>,
pub nav_order: Option<i32>,
pub hide_from_nav: bool,
pub hide_from_feed: bool,
pub id: Option<String>,
pub source_markdown: String,
}
impl PublishedPage {
pub fn published_date(&self) -> Option<&str> {
self.date_of_document
.as_deref()
.or(self.created.as_deref())
.or(self.updated.as_deref())
.filter(|d| !d.is_empty())
}
pub fn modified_date(&self) -> Option<&str> {
self.updated
.as_deref()
.filter(|d| !d.is_empty())
.or_else(|| self.published_date())
}
}
#[derive(Debug, Clone, Default)]
pub struct OutlineNode {
pub path: String,
pub label: Option<String>,
pub children: Vec<OutlineNode>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct LinkEdge {
pub relation: Option<String>,
pub path: String,
}
#[derive(Debug, Clone)]
pub struct SiteNavNode {
pub title: String,
pub href: String,
pub is_current: bool,
pub is_ancestor_of_current: bool,
pub children: Vec<SiteNavNode>,
}
#[derive(Debug, Clone)]
pub struct SiteNavigation {
pub tree: Vec<SiteNavNode>,
pub breadcrumbs: Vec<NavLink>,
}
#[derive(Debug)]
pub struct PublishResult {
pub pages: Vec<PublishedPage>,
pub files_processed: usize,
pub attachments_copied: usize,
}
pub use prov::views::{Grain, Grouping};
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum Arrangement {
#[default]
Containment,
Grouped(Grouping),
}
pub fn serve_at_dest(value: &str) -> Option<String> {
let rest = value.trim().strip_prefix('/')?;
let mut parts: Vec<String> = Vec::new();
for part in rest.split('/') {
if part.is_empty() || part == "." || part == ".." {
continue;
}
let cleaned = crate::links::sanitize_path_component(part);
if !cleaned.is_empty() {
parts.push(cleaned);
}
}
if parts.is_empty() {
return None;
}
let mut dest = parts.join("/");
if !dest.ends_with(".html") {
dest.push_str(".html");
}
Some(dest)
}