summaryrefslogtreecommitdiff
path: root/src/collect_files.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/collect_files.rs')
-rw-r--r--src/collect_files.rs84
1 files changed, 76 insertions, 8 deletions
diff --git a/src/collect_files.rs b/src/collect_files.rs
index 4eee1dc..ccdfc49 100644
--- a/src/collect_files.rs
+++ b/src/collect_files.rs
@@ -1,13 +1,16 @@
use crate::*;
+use highlight::*;
use vagabond::*;
use std::collections::HashMap;
+use std::fmt::Debug;
pub struct Website {
pub name: String,
pub config: HashMap<String, String>,
+ pub highlighters: Highlighters,
pub pages: Vec<Page>,
pub redirects: Vec<Redirect>,
pub static_files: Vec<StaticItem>, // Redirects, !-prefixed-dir contents
@@ -47,7 +50,7 @@ pub struct Redirect {
pub last_modified: Option<SystemTime>, // last-modified time of source file
}
-pub trait LinkFrom {
+pub trait LinkFrom: Debug {
fn name(&self) -> &str;
fn parent_url(&self) -> &str;
fn parents(&self) -> &[String];
@@ -58,8 +61,18 @@ pub trait LinkFrom {
}
return root;
}
+ fn qualified_name(&self) -> String {
+ match self.parents().last() {
+ Some(parent) => format!("{parent}/{}", self.name()),
+ None => format!("/{}", self.name()),
+ }
+ }
}
+pub struct Highlighters {
+ pub languages: HashMap<String, usize>,
+ pub highlighters: Vec<Highlighter>,
+}
impl Page {
pub fn root(&self) -> String {
@@ -71,6 +84,18 @@ impl Page {
}
}
+impl Debug for Page {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ write!(f, "\"{}\"", self.qualified_name())
+ }
+}
+
+impl Debug for Redirect {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ write!(f, "\"{}\"", self.qualified_name())
+ }
+}
+
impl LinkFrom for Page {
fn name(&self) -> &str { &self.name }
fn parent_url(&self) -> &str { &self.parent_url }
@@ -93,11 +118,16 @@ impl Website {
static_dirs: Vec::new(),
name: match Entry::from_path(path) {
Ok(entry) => entry.name,
- Err(err) => error!("Couldn't open {:?}: {:?}", &path, err),
+ Err(err) => fatal!("Couldn't open {:?}: {:?}", &path, err),
},
config: HashMap::new(),
+ highlighters: Highlighters {
+ languages: HashMap::new(),
+ highlighters: Vec::new(),
+ },
};
new.collect_entry(path, path);
+ new.parse_highlighters();
return new;
}
@@ -118,7 +148,7 @@ impl Website {
// Generate parent URL, used only for files.
let source_path = entry.original_path.clone();
let relative_path = source_path.strip_prefix(prefix).unwrap_or_else(
- |_| error!("Path doesn't start with {prefix:?}: {source_path:?}"));
+ |_| fatal!("Path doesn't start with {prefix:?}: {source_path:?}"));
let mut parents: Vec<_> = relative_path.components()
.map(|c| c.as_os_str().to_string_lossy().to_string()).collect();
parents.pop(); // Remove file segment.
@@ -130,7 +160,7 @@ impl Website {
for child in traverse_directory(&entry).unwrap() {
let source_path = child.original_path;
let relative_path = source_path.strip_prefix(&entry.original_path).unwrap_or_else(
- |_| error!("Path doesn't start with {prefix:?}: {source_path:?}"))
+ |_| fatal!("Path doesn't start with {prefix:?}: {source_path:?}"))
.as_os_str().to_string_lossy().to_string();
let full_url = format!("{stripped}/{relative_path}");
self.static_files.push(StaticItem { full_url, source_path, last_modified })
@@ -143,13 +173,13 @@ impl Website {
}
}
} else if parents.is_empty() && entry.name.to_lowercase() == "toaster.conf" {
- verbose!("Reading configuration file at {path:?}");
+ info!("Reading configuration file at {path:?}");
// Parse the config file.
let config = std::fs::read_to_string(&source_path).unwrap();
let mut key = None;
let mut value = String::new();
for line in config.lines() {
- if line.starts_with(" ") {
+ if line.starts_with(" ") || line.trim().is_empty() {
value.push_str(line.trim());
value.push('\n');
} else {
@@ -163,6 +193,10 @@ impl Website {
self.config.insert(key, std::mem::take(&mut value));
}
} else {
+ let full_name = match parents.last() {
+ Some(parent) => format!("{parent}/{name}"),
+ None => name.to_string(),
+ };
match extension.as_str() {
"md" => {
let markdown = std::fs::read_to_string(&source_path).unwrap();
@@ -182,7 +216,7 @@ impl Website {
None
}).collect();
for url in duplicates {
- warn!("Page {name:?} contains multiple headings with ID \"#{url}\"");
+ warn!("Page {full_name:?} contains multiple headings with ID \"#{url}\"");
}
if name_url == "+index" {
if parents.is_empty() {
@@ -268,6 +302,40 @@ impl Website {
}
}
+ pub fn parse_highlighters(&mut self) {
+ let mut languages = Vec::new();
+ let mut source = String::new();
+ for line in self.get_config("highlighters").lines() {
+ if let Some(line) = line.trim().strip_prefix('[') {
+ if let Some(line) = line.strip_suffix(']') {
+ // Bank the current source.
+ if !languages.is_empty() {
+ let i = self.highlighters.highlighters.len();
+ for language in languages {
+ self.highlighters.languages.insert(language, i);
+ }
+ let highlighter = Highlighter::from_str(&source);
+ self.highlighters.highlighters.push(highlighter);
+ }
+ languages = line.split('/').map(|s| s.trim().to_string()).collect();
+ source.clear();
+ continue;
+ }
+ }
+ source.push_str(line);
+ source.push('\n');
+ }
+ // Bank the current source.
+ if !languages.is_empty() {
+ let i = self.highlighters.highlighters.len();
+ for language in languages {
+ self.highlighters.languages.insert(language, i);
+ }
+ let highlighter = Highlighter::from_str(&source);
+ self.highlighters.highlighters.push(highlighter);
+ }
+ }
+
// Ext is extension without a dot.
// Checks if a relative link to an internal page name can be reached from
// the current page, and returns a resolved absolute link to the page with extension.
@@ -294,7 +362,7 @@ impl Website {
if let Some(heading) = heading {
let heading = make_url_safe(strip_appendix(heading));
if !page.headings.iter().any(|h| h.url == heading) {
- warn!("Page {:?} contains link to nonexistent heading {heading:?} on page {path:?}", from.name());
+ warn!("Page {from:?} contains link to nonexistent heading {heading:?} on page {path:?}");
}
return Some(format!("{root}{path}.{ext}#{heading}"));
} else {