cobalt/cobalt_model/
permalink.rs

1use std::sync::LazyLock;
2
3use liquid;
4
5use crate::error::Result;
6
7pub fn explode_permalink<S: AsRef<str>>(
8    permalink: S,
9    attributes: &liquid::Object,
10) -> Result<String> {
11    explode_permalink_string(permalink.as_ref(), attributes)
12}
13
14fn explode_permalink_string(permalink: &str, attributes: &liquid::Object) -> Result<String> {
15    static PERMALINK_PARSER: LazyLock<liquid::Parser> = LazyLock::new(liquid::Parser::new);
16    let p = PERMALINK_PARSER.parse(permalink)?;
17    let mut p = p.render(attributes)?;
18
19    // Handle the user doing windows-style
20    p = p.replace('\\', "/");
21
22    // Handle cases where substutions were blank
23    p = p.replace("//", "/");
24
25    if p.starts_with('/') {
26        p.remove(0);
27    }
28
29    Ok(p)
30}
31
32pub fn format_url_as_file<S: AsRef<str>>(permalink: S) -> relative_path::RelativePathBuf {
33    format_url_as_file_str(permalink.as_ref())
34}
35
36fn format_url_as_file_str(permalink: &str) -> relative_path::RelativePathBuf {
37    let mut path = std::path::Path::new(&permalink);
38
39    // remove the root prefix (leading slash on unix systems)
40    if path.has_root() {
41        let mut components = path.components();
42        components.next();
43        path = components.as_path();
44    }
45
46    let mut path_buf = relative_path::RelativePathBuf::from_path(path).unwrap();
47
48    // explode the url if no extension was specified
49    if path_buf.extension().is_none() {
50        path_buf.push("index.html");
51    }
52
53    path_buf
54}
55
56#[cfg(test)]
57mod test {
58    use super::*;
59
60    #[test]
61    fn explode_permalink_relative() {
62        let attributes = liquid::Object::new();
63        let actual = explode_permalink("relative/path", &attributes).unwrap();
64        assert_eq!(actual, "relative/path");
65    }
66
67    #[test]
68    fn explode_permalink_absolute() {
69        let attributes = liquid::Object::new();
70        let actual = explode_permalink("/abs/path", &attributes).unwrap();
71        assert_eq!(actual, "abs/path");
72    }
73
74    #[test]
75    fn explode_permalink_blank_substitution() {
76        let attributes = liquid::Object::new();
77        let actual = explode_permalink("//path/middle//end", &attributes).unwrap();
78        assert_eq!(actual, "path/middle/end");
79    }
80
81    #[test]
82    fn format_url_as_file_absolute() {
83        let actual = format_url_as_file("/hello/world.html");
84        assert_eq!(
85            actual,
86            relative_path::RelativePath::from_path("hello/world.html").unwrap()
87        );
88    }
89
90    #[test]
91    fn format_url_as_file_no_explode() {
92        let actual = format_url_as_file("/hello/world.custom");
93        assert_eq!(
94            actual,
95            relative_path::RelativePath::from_path("hello/world.custom").unwrap()
96        );
97    }
98
99    #[test]
100    fn format_url_as_file_explode() {
101        let actual = format_url_as_file("/hello/world");
102        assert_eq!(
103            actual,
104            relative_path::RelativePath::from_path("hello/world/index.html").unwrap()
105        );
106    }
107}