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.
|
||||
|
||||
import gleam/result
|
||||
import gloss/parser
|
||||
import gloss/rendering/database as render_database
|
||||
import gloss/config.{type Configuration}
|
||||
import gloss/models/database.{type Database}
|
||||
|
@ -10,7 +9,7 @@ import gloss/compiler.{type CompileDatabase}
|
|||
|
||||
/// Something failed when building the blog.
|
||||
pub type BuildError {
|
||||
ParseError(err: parser.ParseError)
|
||||
ParseError(err: config.ParseError)
|
||||
WriteError(err: config.WriteError)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//// The configuration is the main way to customize Gloss's behaviour.
|
||||
|
||||
import gleam/option
|
||||
import gloss/paths.{type PathConfiguration}
|
||||
import gloss/rendering/views.{
|
||||
|
@ -5,17 +7,36 @@ import gloss/rendering/views.{
|
|||
type SinglePostView,
|
||||
}
|
||||
import gloss/compiler.{type CompileDatabase, type Compiler}
|
||||
import gloss/parser.{type Parser}
|
||||
import gloss/models/database.{type Database}
|
||||
import gloss/rendering/database.{type RenderDatabase} as _
|
||||
|
||||
/// An error occurred when writing the rendered results into files.
|
||||
pub type WriteError {
|
||||
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 =
|
||||
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 {
|
||||
Views(
|
||||
base: fn(Database, Configuration) -> BaseView,
|
||||
|
@ -28,31 +49,54 @@ pub type Views {
|
|||
)
|
||||
}
|
||||
|
||||
/// Compiling related configuration.
|
||||
pub type Compiling {
|
||||
Compiling(
|
||||
/// The compiler to use for compiling the database.
|
||||
database_compiler: fn(Database, Compiler) -> CompileDatabase,
|
||||
/// The compiler to use for compiling individual items.
|
||||
item_compiler: Compiler,
|
||||
)
|
||||
}
|
||||
|
||||
/// Rendering related configuration.
|
||||
pub type Rendering {
|
||||
Rendering(
|
||||
renderer: fn(Database, CompileDatabase, Configuration) -> RenderDatabase,
|
||||
/// The renderer to use.
|
||||
renderer: Renderer,
|
||||
/// The view generators to use.
|
||||
views: Views,
|
||||
/// The copyright statement of the blog, used in the footer and the feed.
|
||||
copyright: String,
|
||||
/// How many posts to show per page.
|
||||
posts_per_page: Int,
|
||||
/// How many posts to render in the feed.
|
||||
posts_in_feed: Int,
|
||||
)
|
||||
}
|
||||
|
||||
/// Author related configuration.
|
||||
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 {
|
||||
Configuration(
|
||||
/// Name of the blog.
|
||||
blog_name: String,
|
||||
/// Absolute URL of the blog, this is where you are deploying it.
|
||||
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,
|
||||
author: Author,
|
||||
compiling: Compiling,
|
||||
|
@ -60,6 +104,7 @@ pub type Configuration {
|
|||
paths: PathConfiguration,
|
||||
parser: Parser,
|
||||
writer: Writer,
|
||||
/// The folder to write the output into.
|
||||
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 gloss/config.{type Configuration, Compiling, Configuration, Rendering}
|
||||
import gloss/rendering/views/single_post
|
||||
|
@ -12,6 +15,7 @@ import gloss/parser
|
|||
import gloss/writer
|
||||
import gloss/compiler
|
||||
|
||||
/// View generators that use the Gloss builtin views.
|
||||
const default_views = config.Views(
|
||||
base: base.generate,
|
||||
meta: meta.generate,
|
||||
|
@ -22,6 +26,7 @@ const default_views = config.Views(
|
|||
feed: feed.generate,
|
||||
)
|
||||
|
||||
/// Get a sensible default configuration based on the given arguments.
|
||||
pub fn default_config(
|
||||
blog_name: String,
|
||||
blog_url: String,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//// Parsing reads the input files into the database.
|
||||
|
||||
import gleam/result
|
||||
import gleam/list
|
||||
import gleam/regex
|
||||
|
@ -8,28 +10,25 @@ import gloss/parser/common
|
|||
import gloss/parser/post
|
||||
import gloss/parser/page
|
||||
import gloss/parser/menu
|
||||
import gloss/config.{type ParseError, ParseError}
|
||||
|
||||
/// The default path where input files are read from.
|
||||
const default_data_path = "./data"
|
||||
|
||||
pub type Parser =
|
||||
fn() -> Result(Database, ParseError)
|
||||
|
||||
pub type ParseError {
|
||||
FileError(path: String, err: simplifile.FileError)
|
||||
PostParseError(filename: String, err: common.ParseError)
|
||||
MenuParseError
|
||||
}
|
||||
|
||||
/// The default parser.
|
||||
pub fn default_parse() -> Result(Database, ParseError) {
|
||||
parse_posts(database.new(), post_path())
|
||||
|> result.try(parse_pages(_, page_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) {
|
||||
use filenames <- result.try(
|
||||
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) =
|
||||
|
@ -49,11 +48,15 @@ pub fn parse_posts(db: Database, path: String) -> Result(Database, ParseError) {
|
|||
list.map(filenames, fn(file) {
|
||||
use contents <- result.try(
|
||||
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)
|
||||
|> 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))
|
||||
}
|
||||
|
||||
/// Parse pages from the given path into the database.
|
||||
pub fn parse_pages(db: Database, path: String) -> Result(Database, ParseError) {
|
||||
use filenames <- result.try(
|
||||
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(
|
||||
|
@ -75,28 +81,37 @@ pub fn parse_pages(db: Database, path: String) -> Result(Database, ParseError) {
|
|||
|> list.map(fn(file) {
|
||||
use contents <- result.try(
|
||||
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)
|
||||
|> 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))
|
||||
}
|
||||
|
||||
/// Parse the menu from the given file into the database.
|
||||
pub fn parse_menu(db: Database, file: String) -> Result(Database, ParseError) {
|
||||
case simplifile.verify_is_file(file) {
|
||||
Ok(True) -> {
|
||||
use contents <- result.try(
|
||||
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(
|
||||
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))
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
//// Path configuration controls where files are generated and how links are
|
||||
//// formed.
|
||||
|
||||
import gleam/int
|
||||
import gleam/string
|
||||
import gleam/list
|
||||
|
@ -6,30 +9,55 @@ import gloss/models/page.{type Page}
|
|||
import gloss/utils/date.{type Month}
|
||||
import gloss/utils/ints/day
|
||||
|
||||
pub const default_root = ""
|
||||
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. `/gloss_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,
|
||||
|
@ -44,6 +72,7 @@ pub const defaults = PathConfiguration(
|
|||
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 =
|
||||
|
@ -59,18 +88,22 @@ pub fn default_single_post(post: Post) {
|
|||
"/" <> 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)
|
||||
<> "/"
|
||||
|
@ -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) {
|
||||
path <> ".html"
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue