add a tagging system to the website
This commit is contained in:
parent
701da6bc4b
commit
e1b6578b2a
97 changed files with 1025 additions and 979 deletions
138
src/doc.rs
Normal file
138
src/doc.rs
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
use chrono::{DateTime, Utc};
|
||||
use codespan_reporting::diagnostic::{Diagnostic, Label};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
state::{FileId, TomlError, Treehouse, toml_error_to_diagnostic},
|
||||
tree::attributes::{Picture, timestamp_from_id},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Doc {
|
||||
pub attributes: Attributes,
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize)]
|
||||
pub struct Attributes {
|
||||
/// Template to use for generating the page.
|
||||
/// Defaults to `_tree.hbs`.
|
||||
#[serde(default)]
|
||||
pub template: Option<String>,
|
||||
|
||||
/// The unique ID of the doc.
|
||||
/// Required to appear in feeds.
|
||||
///
|
||||
/// - New format: `doc?{date}-{name}`, where `{date}` is a `YYYY-MM-DD` date, and `{name}` is
|
||||
/// the filename of the document (or otherwise a unique name which doesn't conflict with docs
|
||||
/// made that day.)
|
||||
/// - Old format: `b?{ulid}`, where `{ulid}` is a ULID.
|
||||
/// This follows the format of branches.
|
||||
#[serde(default)]
|
||||
pub id: String,
|
||||
|
||||
/// Title of the page.
|
||||
/// The only necessary field.
|
||||
/// Unlike tree pages, doc pages always have titles.
|
||||
pub title: String,
|
||||
|
||||
/// Tags assigned to the document.
|
||||
/// Generally, you want to assign public documents to #all for them to show up on the front page.
|
||||
#[serde(default)]
|
||||
pub tags: Vec<String>,
|
||||
|
||||
/// Timestamp when the document was last updated.
|
||||
/// Required for inclusion in feeds.
|
||||
/// For pages with old style IDs, this is inferred from the ID.
|
||||
#[serde(default)]
|
||||
pub updated: Option<DateTime<Utc>>,
|
||||
|
||||
/// ID of picture attached to the page, to be used as a thumbnail.
|
||||
#[serde(default)]
|
||||
pub thumbnail: Option<Picture>,
|
||||
|
||||
/// Additional scripts to load into to the page.
|
||||
/// These are relative to the /static/js directory.
|
||||
#[serde(default)]
|
||||
pub scripts: Vec<String>,
|
||||
|
||||
/// Additional styles to load into to the page.
|
||||
/// These are relative to the /static/css directory.
|
||||
#[serde(default)]
|
||||
pub styles: Vec<String>,
|
||||
|
||||
/// If not `None`, the page will get an additional 'feed' field in template data, containing
|
||||
/// a feed of pages with the specified tag.
|
||||
#[serde(default)]
|
||||
pub include_feed: Option<IncludeFeed>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct IncludeFeed {
|
||||
/// The tag to look for.
|
||||
pub tag: String,
|
||||
|
||||
/// The title of the feed shown on the page.
|
||||
pub title: String,
|
||||
}
|
||||
|
||||
impl Doc {
|
||||
pub fn parse(treehouse: &mut Treehouse, file_id: FileId) -> (Doc, Vec<Diagnostic<FileId>>) {
|
||||
let mut diagnostics = vec![];
|
||||
|
||||
let source = treehouse.source(file_id).input();
|
||||
|
||||
let (front_matter, text) = source.split_once("+++").unwrap_or(("", source));
|
||||
let attributes_span = 0..front_matter.len();
|
||||
let mut attributes: Attributes =
|
||||
toml_edit::de::from_str(front_matter).unwrap_or_else(|error| {
|
||||
diagnostics.push(toml_error_to_diagnostic(TomlError {
|
||||
message: error.message().to_owned(),
|
||||
span: error.span(),
|
||||
file_id,
|
||||
input_range: attributes_span.clone(),
|
||||
}));
|
||||
Attributes::default()
|
||||
});
|
||||
|
||||
// Infer attributes
|
||||
|
||||
if let Some(branch_id) = attributes.id.strip_prefix("b?")
|
||||
&& let Some(timestamp) = timestamp_from_id(branch_id)
|
||||
{
|
||||
attributes.updated = Some(timestamp);
|
||||
}
|
||||
|
||||
// Emit warnings
|
||||
|
||||
if !attributes.tags.is_empty() {
|
||||
if attributes.id.is_empty() {
|
||||
diagnostics.push(
|
||||
Diagnostic::warning()
|
||||
.with_code("attr")
|
||||
.with_message("doc is tagged but missing id attribute")
|
||||
.with_labels(vec![Label::primary(file_id, attributes_span.clone())])
|
||||
.with_notes(vec!["id is required for showing up in feeds".into()]),
|
||||
);
|
||||
} else if attributes.updated.is_none() {
|
||||
diagnostics.push(
|
||||
Diagnostic::warning()
|
||||
.with_code("attr")
|
||||
.with_message("doc is tagged but missing updated attribute")
|
||||
.with_labels(vec![Label::primary(file_id, attributes_span.clone())])
|
||||
.with_notes(vec![
|
||||
"updated attribute is required for showing up in feeds".into(),
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
Doc {
|
||||
attributes,
|
||||
text: text.to_owned(),
|
||||
},
|
||||
diagnostics,
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue