Add more docs
This commit is contained in:
parent
289d4d647d
commit
5f23dadde1
5 changed files with 121 additions and 24 deletions
|
@ -2,7 +2,6 @@
|
||||||
//// parts of gloss, using the configuration to control what is done.
|
//// parts of gloss, using the configuration to control what is done.
|
||||||
|
|
||||||
import gleam/result
|
import gleam/result
|
||||||
import gloss/parser
|
|
||||||
import gloss/rendering/database as render_database
|
import gloss/rendering/database as render_database
|
||||||
import gloss/config.{type Configuration}
|
import gloss/config.{type Configuration}
|
||||||
import gloss/models/database.{type Database}
|
import gloss/models/database.{type Database}
|
||||||
|
@ -10,7 +9,7 @@ import gloss/compiler.{type CompileDatabase}
|
||||||
|
|
||||||
/// Something failed when building the blog.
|
/// Something failed when building the blog.
|
||||||
pub type BuildError {
|
pub type BuildError {
|
||||||
ParseError(err: parser.ParseError)
|
ParseError(err: config.ParseError)
|
||||||
WriteError(err: config.WriteError)
|
WriteError(err: config.WriteError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//// The configuration is the main way to customize Gloss's behaviour.
|
||||||
|
|
||||||
import gleam/option
|
import gleam/option
|
||||||
import gloss/paths.{type PathConfiguration}
|
import gloss/paths.{type PathConfiguration}
|
||||||
import gloss/rendering/views.{
|
import gloss/rendering/views.{
|
||||||
|
@ -5,17 +7,36 @@ import gloss/rendering/views.{
|
||||||
type SinglePostView,
|
type SinglePostView,
|
||||||
}
|
}
|
||||||
import gloss/compiler.{type CompileDatabase, type Compiler}
|
import gloss/compiler.{type CompileDatabase, type Compiler}
|
||||||
import gloss/parser.{type Parser}
|
|
||||||
import gloss/models/database.{type Database}
|
import gloss/models/database.{type Database}
|
||||||
import gloss/rendering/database.{type RenderDatabase} as _
|
import gloss/rendering/database.{type RenderDatabase} as _
|
||||||
|
|
||||||
|
/// An error occurred when writing the rendered results into files.
|
||||||
pub type WriteError {
|
pub type WriteError {
|
||||||
WriteError(err: String)
|
WriteError(err: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An error occurred when parsing input files.
|
||||||
|
pub type ParseError {
|
||||||
|
ParseError(err: String)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The parser
|
||||||
|
pub type Parser =
|
||||||
|
fn() -> Result(Database, ParseError)
|
||||||
|
|
||||||
|
/// Renders the content of the database into HTML.
|
||||||
|
pub type Renderer =
|
||||||
|
fn(Database, CompileDatabase, Configuration) -> RenderDatabase
|
||||||
|
|
||||||
|
/// Writes the rendered HTML into files.
|
||||||
pub type Writer =
|
pub type Writer =
|
||||||
fn(RenderDatabase, Configuration) -> Result(Nil, WriteError)
|
fn(RenderDatabase, Configuration) -> Result(Nil, WriteError)
|
||||||
|
|
||||||
|
/// View generators for the blog. These take the database and configuration and
|
||||||
|
/// must return a view function that is used for rendering.
|
||||||
|
///
|
||||||
|
/// See the `gloss/rendering/views` documentation for descriptions of the
|
||||||
|
/// individual views.
|
||||||
pub type Views {
|
pub type Views {
|
||||||
Views(
|
Views(
|
||||||
base: fn(Database, Configuration) -> BaseView,
|
base: fn(Database, Configuration) -> BaseView,
|
||||||
|
@ -28,31 +49,54 @@ pub type Views {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compiling related configuration.
|
||||||
pub type Compiling {
|
pub type Compiling {
|
||||||
Compiling(
|
Compiling(
|
||||||
|
/// The compiler to use for compiling the database.
|
||||||
database_compiler: fn(Database, Compiler) -> CompileDatabase,
|
database_compiler: fn(Database, Compiler) -> CompileDatabase,
|
||||||
|
/// The compiler to use for compiling individual items.
|
||||||
item_compiler: Compiler,
|
item_compiler: Compiler,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Rendering related configuration.
|
||||||
pub type Rendering {
|
pub type Rendering {
|
||||||
Rendering(
|
Rendering(
|
||||||
renderer: fn(Database, CompileDatabase, Configuration) -> RenderDatabase,
|
/// The renderer to use.
|
||||||
|
renderer: Renderer,
|
||||||
|
/// The view generators to use.
|
||||||
views: Views,
|
views: Views,
|
||||||
|
/// The copyright statement of the blog, used in the footer and the feed.
|
||||||
copyright: String,
|
copyright: String,
|
||||||
|
/// How many posts to show per page.
|
||||||
posts_per_page: Int,
|
posts_per_page: Int,
|
||||||
|
/// How many posts to render in the feed.
|
||||||
posts_in_feed: Int,
|
posts_in_feed: Int,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Author related configuration.
|
||||||
pub type Author {
|
pub type Author {
|
||||||
Author(name: String, email: option.Option(String), url: option.Option(String))
|
Author(
|
||||||
|
/// Name of the author.
|
||||||
|
name: String,
|
||||||
|
/// Email address of the author, used in the feed's author information.
|
||||||
|
email: option.Option(String),
|
||||||
|
/// Website URL of the author, used in the feed's author information and
|
||||||
|
/// added as a `rel="me"` link in the base layout.
|
||||||
|
url: option.Option(String),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Configuration {
|
pub type Configuration {
|
||||||
Configuration(
|
Configuration(
|
||||||
|
/// Name of the blog.
|
||||||
blog_name: String,
|
blog_name: String,
|
||||||
|
/// Absolute URL of the blog, this is where you are deploying it.
|
||||||
blog_url: String,
|
blog_url: String,
|
||||||
|
/// Language code of the blog, meaning the language of the content you are
|
||||||
|
/// writing. The language code must be according to
|
||||||
|
/// <https://datatracker.ietf.org/doc/html/rfc5646>.
|
||||||
language: String,
|
language: String,
|
||||||
author: Author,
|
author: Author,
|
||||||
compiling: Compiling,
|
compiling: Compiling,
|
||||||
|
@ -60,6 +104,7 @@ pub type Configuration {
|
||||||
paths: PathConfiguration,
|
paths: PathConfiguration,
|
||||||
parser: Parser,
|
parser: Parser,
|
||||||
writer: Writer,
|
writer: Writer,
|
||||||
|
/// The folder to write the output into.
|
||||||
output_path: String,
|
output_path: String,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
//// This module contains default configurations that can be used to create a
|
||||||
|
//// blog quickly.
|
||||||
|
|
||||||
import gleam/uri
|
import gleam/uri
|
||||||
import gloss/config.{type Configuration, Compiling, Configuration, Rendering}
|
import gloss/config.{type Configuration, Compiling, Configuration, Rendering}
|
||||||
import gloss/rendering/views/single_post
|
import gloss/rendering/views/single_post
|
||||||
|
@ -12,6 +15,7 @@ import gloss/parser
|
||||||
import gloss/writer
|
import gloss/writer
|
||||||
import gloss/compiler
|
import gloss/compiler
|
||||||
|
|
||||||
|
/// View generators that use the Gloss builtin views.
|
||||||
const default_views = config.Views(
|
const default_views = config.Views(
|
||||||
base: base.generate,
|
base: base.generate,
|
||||||
meta: meta.generate,
|
meta: meta.generate,
|
||||||
|
@ -22,6 +26,7 @@ const default_views = config.Views(
|
||||||
feed: feed.generate,
|
feed: feed.generate,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/// Get a sensible default configuration based on the given arguments.
|
||||||
pub fn default_config(
|
pub fn default_config(
|
||||||
blog_name: String,
|
blog_name: String,
|
||||||
blog_url: String,
|
blog_url: String,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//// Parsing reads the input files into the database.
|
||||||
|
|
||||||
import gleam/result
|
import gleam/result
|
||||||
import gleam/list
|
import gleam/list
|
||||||
import gleam/regex
|
import gleam/regex
|
||||||
|
@ -8,28 +10,25 @@ import gloss/parser/common
|
||||||
import gloss/parser/post
|
import gloss/parser/post
|
||||||
import gloss/parser/page
|
import gloss/parser/page
|
||||||
import gloss/parser/menu
|
import gloss/parser/menu
|
||||||
|
import gloss/config.{type ParseError, ParseError}
|
||||||
|
|
||||||
|
/// The default path where input files are read from.
|
||||||
const default_data_path = "./data"
|
const default_data_path = "./data"
|
||||||
|
|
||||||
pub type Parser =
|
/// The default parser.
|
||||||
fn() -> Result(Database, ParseError)
|
|
||||||
|
|
||||||
pub type ParseError {
|
|
||||||
FileError(path: String, err: simplifile.FileError)
|
|
||||||
PostParseError(filename: String, err: common.ParseError)
|
|
||||||
MenuParseError
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn default_parse() -> Result(Database, ParseError) {
|
pub fn default_parse() -> Result(Database, ParseError) {
|
||||||
parse_posts(database.new(), post_path())
|
parse_posts(database.new(), post_path())
|
||||||
|> result.try(parse_pages(_, page_path()))
|
|> result.try(parse_pages(_, page_path()))
|
||||||
|> result.try(parse_menu(_, menu_path()))
|
|> result.try(parse_menu(_, menu_path()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse posts from the given path into the database.
|
||||||
pub fn parse_posts(db: Database, path: String) -> Result(Database, ParseError) {
|
pub fn parse_posts(db: Database, path: String) -> Result(Database, ParseError) {
|
||||||
use filenames <- result.try(
|
use filenames <- result.try(
|
||||||
simplifile.read_directory(path)
|
simplifile.read_directory(path)
|
||||||
|> result.map_error(fn(err) { FileError(path, err) }),
|
|> result.map_error(fn(err) {
|
||||||
|
ParseError(path <> ": " <> string.inspect(err))
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
let assert Ok(filename_regex) =
|
let assert Ok(filename_regex) =
|
||||||
|
@ -49,11 +48,15 @@ pub fn parse_posts(db: Database, path: String) -> Result(Database, ParseError) {
|
||||||
list.map(filenames, fn(file) {
|
list.map(filenames, fn(file) {
|
||||||
use contents <- result.try(
|
use contents <- result.try(
|
||||||
simplifile.read(path <> "/" <> file)
|
simplifile.read(path <> "/" <> file)
|
||||||
|> result.map_error(fn(err) { FileError(file, err) }),
|
|> result.map_error(fn(err) {
|
||||||
|
ParseError(file <> ": " <> string.inspect(err))
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
post.parse(file, contents)
|
post.parse(file, contents)
|
||||||
|> result.map_error(fn(err) { PostParseError(file, err) })
|
|> result.map_error(fn(err) {
|
||||||
|
ParseError(file <> ": " <> string.inspect(err))
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -61,10 +64,13 @@ pub fn parse_posts(db: Database, path: String) -> Result(Database, ParseError) {
|
||||||
Ok(list.fold(posts, db, database.add_post))
|
Ok(list.fold(posts, db, database.add_post))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse pages from the given path into the database.
|
||||||
pub fn parse_pages(db: Database, path: String) -> Result(Database, ParseError) {
|
pub fn parse_pages(db: Database, path: String) -> Result(Database, ParseError) {
|
||||||
use filenames <- result.try(
|
use filenames <- result.try(
|
||||||
simplifile.read_directory(path)
|
simplifile.read_directory(path)
|
||||||
|> result.map_error(fn(err) { FileError(path, err) }),
|
|> result.map_error(fn(err) {
|
||||||
|
ParseError(path <> ": " <> string.inspect(err))
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
use pages <- result.try(result.all(
|
use pages <- result.try(result.all(
|
||||||
|
@ -75,28 +81,37 @@ pub fn parse_pages(db: Database, path: String) -> Result(Database, ParseError) {
|
||||||
|> list.map(fn(file) {
|
|> list.map(fn(file) {
|
||||||
use contents <- result.try(
|
use contents <- result.try(
|
||||||
simplifile.read(path <> "/" <> file)
|
simplifile.read(path <> "/" <> file)
|
||||||
|> result.map_error(fn(err) { FileError(file, err) }),
|
|> result.map_error(fn(err) {
|
||||||
|
ParseError(file <> ": " <> string.inspect(err))
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
page.parse(file, contents)
|
page.parse(file, contents)
|
||||||
|> result.map_error(fn(err) { PostParseError(file, err) })
|
|> result.map_error(fn(err) {
|
||||||
|
ParseError(file <> ": " <> string.inspect(err))
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
|
|
||||||
Ok(list.fold(pages, db, database.add_page))
|
Ok(list.fold(pages, db, database.add_page))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse the menu from the given file into the database.
|
||||||
pub fn parse_menu(db: Database, file: String) -> Result(Database, ParseError) {
|
pub fn parse_menu(db: Database, file: String) -> Result(Database, ParseError) {
|
||||||
case simplifile.verify_is_file(file) {
|
case simplifile.verify_is_file(file) {
|
||||||
Ok(True) -> {
|
Ok(True) -> {
|
||||||
use contents <- result.try(
|
use contents <- result.try(
|
||||||
simplifile.read(file)
|
simplifile.read(file)
|
||||||
|> result.map_error(fn(err) { FileError(file, err) }),
|
|> result.map_error(fn(err) {
|
||||||
|
ParseError(file <> ": " <> string.inspect(err))
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
use menu <- result.try(
|
use menu <- result.try(
|
||||||
menu.parse(contents)
|
menu.parse(contents)
|
||||||
|> result.replace_error(MenuParseError),
|
|> result.map_error(fn(err) {
|
||||||
|
ParseError("Menu parsing failed: " <> string.inspect(err))
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
Ok(database.set_menu(db, menu))
|
Ok(database.set_menu(db, menu))
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
//// Path configuration controls where files are generated and how links are
|
||||||
|
//// formed.
|
||||||
|
|
||||||
import gleam/int
|
import gleam/int
|
||||||
import gleam/string
|
import gleam/string
|
||||||
import gleam/list
|
import gleam/list
|
||||||
|
@ -6,30 +9,55 @@ import gloss/models/page.{type Page}
|
||||||
import gloss/utils/date.{type Month}
|
import gloss/utils/date.{type Month}
|
||||||
import gloss/utils/ints/day
|
import gloss/utils/ints/day
|
||||||
|
|
||||||
pub const default_root = ""
|
const default_root = ""
|
||||||
|
|
||||||
|
/// The default index path.
|
||||||
pub const default_index = "/index"
|
pub const default_index = "/index"
|
||||||
|
|
||||||
|
/// The default filename where the feed is written.
|
||||||
pub const default_feed_file = "/feed.xml"
|
pub const default_feed_file = "/feed.xml"
|
||||||
|
|
||||||
|
/// The default path where the feed is accessible when hosted.
|
||||||
pub const default_feed = default_feed_file
|
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 {
|
pub type PathConfiguration {
|
||||||
PathConfiguration(
|
PathConfiguration(
|
||||||
|
/// The root path where the blog will be accessible. With starting slash but
|
||||||
|
/// without trailing slash, e.g. `/gloss_blog`. Note that if the blog is
|
||||||
|
/// accessible without a subpath, this value should be `""`.
|
||||||
root: String,
|
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,
|
index: String,
|
||||||
|
/// Path to a single post.
|
||||||
single_post: fn(Post) -> String,
|
single_post: fn(Post) -> String,
|
||||||
|
/// Path to a page.
|
||||||
page: fn(Page) -> String,
|
page: fn(Page) -> String,
|
||||||
|
/// Path to a tag archive.
|
||||||
tag: fn(String) -> String,
|
tag: fn(String) -> String,
|
||||||
|
/// Path to a year archive.
|
||||||
year: fn(Int) -> String,
|
year: fn(Int) -> String,
|
||||||
|
/// Path to a month archive of a given year.
|
||||||
month: fn(Int, Month) -> String,
|
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,
|
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,
|
html: fn(String) -> String,
|
||||||
|
/// Path to the feed as it is accessible from the browser.
|
||||||
feed: String,
|
feed: String,
|
||||||
|
/// Path and file name of the feed file that will be written.
|
||||||
feed_file: String,
|
feed_file: String,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Default path configuration.
|
||||||
pub const defaults = PathConfiguration(
|
pub const defaults = PathConfiguration(
|
||||||
root: default_root,
|
root: default_root,
|
||||||
index: default_index,
|
index: default_index,
|
||||||
|
@ -44,6 +72,7 @@ pub const defaults = PathConfiguration(
|
||||||
feed_file: default_feed_file,
|
feed_file: default_feed_file,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/// Post path in the format `/2024/12/31/slug`.
|
||||||
pub fn default_single_post(post: Post) {
|
pub fn default_single_post(post: Post) {
|
||||||
let post_date = post.get_date(post)
|
let post_date = post.get_date(post)
|
||||||
let date_parts =
|
let date_parts =
|
||||||
|
@ -59,18 +88,22 @@ pub fn default_single_post(post: Post) {
|
||||||
"/" <> string.join(date_parts, "/") <> "/" <> post.slug
|
"/" <> string.join(date_parts, "/") <> "/" <> post.slug
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Page path in the format `/slug`.
|
||||||
pub fn default_page(page: Page) {
|
pub fn default_page(page: Page) {
|
||||||
"/" <> page.slug
|
"/" <> page.slug
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tag path in the format `/tag/tag`.
|
||||||
pub fn default_tag(tag: String) {
|
pub fn default_tag(tag: String) {
|
||||||
"/tag/" <> tag
|
"/tag/" <> tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Year archive path in the format `/archive/2024`.
|
||||||
pub fn default_year_archive(year: Int) {
|
pub fn default_year_archive(year: Int) {
|
||||||
"/archive" <> "/" <> int.to_string(year)
|
"/archive" <> "/" <> int.to_string(year)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Month archive path in the format `/archive/2024/05`.
|
||||||
pub fn default_month_archive(year: Int, month: Month) {
|
pub fn default_month_archive(year: Int, month: Month) {
|
||||||
default_year_archive(year)
|
default_year_archive(year)
|
||||||
<> "/"
|
<> "/"
|
||||||
|
@ -87,7 +120,7 @@ pub fn default_list_page(path: String, page: Int) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get path with the .html extension
|
/// Get path with the .html extension.
|
||||||
pub fn default_html(path: String) {
|
pub fn default_html(path: String) {
|
||||||
path <> ".html"
|
path <> ".html"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue