use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use ulid::Ulid; use crate::{state::FileId, vfs::VPathBuf}; /// Top-level `%%` root attributes. #[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)] pub struct RootAttributes { /// Template to use for generating the page. /// Defaults to `_tree.hbs`. #[serde(default)] pub template: Option, /// Title of the generated .html page. /// /// The page's tree path is used if empty. #[serde(default)] pub title: String, /// Page icon used in indexes. /// This is an emoji name, such as `page` (default). #[serde(default = "default_icon")] pub icon: String, /// ID of picture attached to the page, to be used as a thumbnail. #[serde(default)] pub thumbnail: Option, /// Additional scripts to load into to the page. /// These are relative to the /static/js directory. #[serde(default)] pub scripts: Vec, /// Additional styles to load into to the page. /// These are relative to the /static/css directory. #[serde(default)] pub styles: Vec, /// Visibility of a page in the parent page's index. #[serde(default)] pub visibility: Visibility, /// The page's timestamps. These are automatically populated if a page has at least one branch /// with an ID that includes a timestamp. #[serde(default)] pub timestamps: Option, /// When specified, this page will have a corresponding Atom feed under `feed/{feed}.atom`. /// /// In feeds, top-level branches are expected to have a single heading containing the post title. /// Their children are turned into the post description #[serde(default)] pub feed: Option, } /// A picture reference. #[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)] pub struct Picture { /// ID of the picture. pub id: String, /// Optional alt text. #[serde(default)] pub alt: Option, } /// Visibility of a page. #[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)] pub enum Visibility { #[default] Public, /// Hidden from the parent page's index. Private, } /// Timestamps for a page. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)] pub struct Timestamps { /// When the page was created. By default, this is the timestamp of the least recent branch. pub created: DateTime, /// When the page was last updated. By default, this is the timestamp of the most recent branch. pub updated: DateTime, } fn default_icon() -> String { String::from("page") } /// Branch attributes. #[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize)] pub struct Attributes { /// Unique identifier of the branch. /// /// Note that this must be unique to the _entire_ site, not just a single tree. /// This is because trees may be embedded within each other using [`Content::Link`]. #[serde(default)] pub id: String, /// Redirect old and deleted listed in the list to this branch. /// /// This can be used to keep links permanent even in case the structure of the treehouse changes. #[serde(default)] pub redirect_here: Vec, /// Controls how the block should be presented. #[serde(default)] pub content: Content, /// Do not persist the branch in localStorage. #[serde(default)] pub do_not_persist: bool, /// Strings of extra CSS class names to include in the generated HTML. #[serde(default)] pub classes: Classes, /// Enable `mini_template` templating in this branch. #[serde(default)] pub template: bool, /// Publishing stage; if `Draft`, the branch is invisible unless treehouse is compiled in /// debug mode. #[serde(default)] pub stage: Stage, /// List of extra spells to cast on the branch. #[serde(default)] pub cast: String, /// In feeds, specifies the list of tags to attach to an entry. /// This only has an effect on top-level branches. #[serde(default)] pub tags: Vec, } impl Attributes { /// Parses the timestamp out of the branch's ID. /// Returns `None` if the ID does not contain a timestamp. pub fn timestamp(&self) -> Option> { Ulid::from_string(&self.id) .ok() .as_ref() .map(Ulid::timestamp_ms) .and_then(|ms| DateTime::from_timestamp_millis(ms as i64)) } } /// Controls for block content presentation. #[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize)] #[serde(rename_all = "snake_case")] pub enum Content { /// Children are stored inline in the block. Nothing special happens. #[default] Inline, /// Link to another tree. /// /// When JavaScript is enabled, the tree's roots will be embedded inline into the branch and /// loaded lazily. /// /// Without JavaScript, the tree will be linked with an `` element. /// /// The string provided as an argument is relative to the `content` root and should not contain /// any file extensions. For example, to link to `content/my-article.tree`, /// use `content.link = "my-article"`. /// /// Note that `Link` branches must not contain any children. If a `Link` branch does contain /// children, an `attribute`-type error is raised. Link(VPathBuf), /// Valid link to another tree. /// This replaces `Content::Link` during semantic analysis. #[serde(skip)] ResolvedLink(FileId), } #[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize)] pub struct Classes { /// Classes to append to the branch itself (
  • ). #[serde(default)] pub branch: String, /// Classes to append to the branch's
      element containing its children. #[serde(default)] pub branch_children: String, } /// Publish stage of a branch. /// /// Draft branches are not included in release builds of treehouse. In debug builds, they are also /// marked with an extra "draft" before the content. #[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize)] pub enum Stage { #[default] Public, Draft, }