diff options
Diffstat (limited to 'src/collect_files.rs')
-rw-r--r-- | src/collect_files.rs | 84 |
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 { |