Current section
Files
Jump to
Current section
Files
src/scriptorium/paths.gleam
//// Path configuration controls where files are generated and how links are
//// formed.
import gleam/int
import gleam/list
import gleam/string
import scriptorium/models/page.{type Page}
import scriptorium/models/post.{type Post}
import scriptorium/utils/date.{type Month}
import scriptorium/utils/ints/day
const default_root = ""
/// The default index path.
pub const default_index = "/index"
/// The default filename where the feed is written.
pub const default_feed_file = "/feed.xml"
/// The default path where the feed is accessible when hosted.
pub const default_feed = default_feed_file
/// The path configuration controls where files will be located and where links
/// will point to.
pub type PathConfiguration {
PathConfiguration(
/// The root path where the blog will be accessible. With starting slash but
/// without trailing slash, e.g. `/scriptorium_blog`. Note that if the blog is
/// accessible without a subpath, this value should be `""`.
root: String,
/// The index path. Note that the first page of the index is always written
/// into `"/"`, meaning `index.html`. This path is used for the rest of the
/// pages, e.g. `"/wibble"` would result in `/wibble/2.html`,
/// `/wibble/3.html` and so on.
index: String,
/// Path to a single post.
single_post: fn(Post) -> String,
/// Path to a page.
page: fn(Page) -> String,
/// Path to a tag archive.
tag: fn(String) -> String,
/// Path to a year archive.
year: fn(Int) -> String,
/// Path to a month archive of a given year.
month: fn(Int, Month) -> String,
/// List page path: given the original path such as `/tag/wibble` as a
/// string and the page number, forms the final path.
list_page: fn(String, Int) -> String,
/// HTML path: Append (or don't) the `.html` extension to the given path.
/// If you are using fancy URLs without extensions, override this to do
/// nothing.
html: fn(String) -> String,
/// Path to the feed as it is accessible from the browser.
feed: String,
/// Path and file name of the feed file that will be written.
feed_file: String,
)
}
/// Default path configuration.
pub const defaults = PathConfiguration(
root: default_root,
index: default_index,
single_post: default_single_post,
page: default_page,
tag: default_tag,
year: default_year_archive,
month: default_month_archive,
list_page: default_list_page,
html: default_html,
feed: default_feed,
feed_file: default_feed_file,
)
/// Post path in the format `/2024/12/31/slug`.
pub fn default_single_post(post: Post) {
let post_date = post.get_date(post)
let date_parts =
list.map(
[
post_date.year,
date.month_to_int(post_date.month),
day.to_int(post_date.day),
],
pad_int,
)
"/" <> string.join(date_parts, "/") <> "/" <> post.slug
}
/// Page path in the format `/slug`.
pub fn default_page(page: Page) {
"/" <> page.slug
}
/// Tag path in the format `/tag/tag`.
pub fn default_tag(tag: String) {
"/tag/" <> tag
}
/// Year archive path in the format `/archive/2024`.
pub fn default_year_archive(year: Int) {
"/archive" <> "/" <> int.to_string(year)
}
/// Month archive path in the format `/archive/2024/05`.
pub fn default_month_archive(year: Int, month: Month) {
default_year_archive(year)
<> "/"
<> string.pad_left(int.to_string(date.month_to_int(month)), 2, "0")
}
/// Get the given list path with a page number.
///
/// The first page does not get any appended page number.
pub fn default_list_page(path: String, page: Int) {
case page {
1 -> path
other -> path <> "/" <> int.to_string(other)
}
}
/// Get path with the .html extension.
pub fn default_html(path: String) {
path <> ".html"
}
fn pad_int(number: Int) -> String {
number
|> int.to_string()
|> string.pad_left(to: 2, with: "0")
}