Compare commits

..

26 Commits

Author SHA1 Message Date
fluxcdbot
b3929f22f3 CI: autofix rust code.
Some checks failed
clippy Build clippy has failed
format Build format has succeeded
rust-test Build rust-test has failed
build Build build has succeeded
2025-08-31 22:24:16 +00:00
Tom Alexander
bad12160ac Let chains have been stabalized.
Some checks are pending
clippy Build clippy has started
format Build format has succeeded
rust-test Build rust-test has succeeded
build Build build has succeeded
2025-08-31 18:22:06 -04:00
Tom Alexander
c43679fda9 Switch to local-path-provisioner.
Some checks failed
build Build build has started
clippy Build clippy has failed
rust-test Build rust-test has failed
format Build format has failed
2025-08-31 17:53:10 -04:00
Tom Alexander
9cc28f6f0d Merge branch 'unlisted_posts'
All checks were successful
format Build format has succeeded
clippy Build clippy has succeeded
rust-test Build rust-test has succeeded
build Build build has succeeded
2025-02-23 12:12:43 -05:00
Tom Alexander
d2256b8333 Add publish filter to blog posts and pages.
All checks were successful
format Build format has succeeded
clippy Build clippy has succeeded
rust-test Build rust-test has succeeded
2025-02-23 12:08:43 -05:00
Tom Alexander
fa8753077a Add support for unlisted posts.
All checks were successful
format Build format has succeeded
clippy Build clippy has succeeded
rust-test Build rust-test has succeeded
2025-02-23 12:02:14 -05:00
Tom Alexander
0420f58d02 Add a hover effect to the home link in the page header.
All checks were successful
format Build format has succeeded
clippy Build clippy has succeeded
rust-test Build rust-test has succeeded
build Build build has succeeded
2025-02-22 23:16:18 -05:00
Tom Alexander
0250aa106e Merge branch 'about_me'
All checks were successful
format Build format has succeeded
clippy Build clippy has succeeded
rust-test Build rust-test has succeeded
build Build build has succeeded
2025-02-22 22:54:18 -05:00
Tom Alexander
ca1c456571 Pass the nav links in the PageHeader render context.
All checks were successful
format Build format has succeeded
clippy Build clippy has succeeded
rust-test Build rust-test has succeeded
2025-02-22 22:49:59 -05:00
Tom Alexander
4403980e2e Add a me link to the nav bar. 2025-02-22 22:36:42 -05:00
Tom Alexander
dbfbce955d Merge branch 'tracing'
Some checks are pending
clippy Build clippy has started
format Build format has succeeded
rust-test Build rust-test has succeeded
build Build build has succeeded
2025-02-22 21:12:44 -05:00
Tom Alexander
2e08d2e59a Set up tracing. 2025-02-22 21:12:33 -05:00
Tom Alexander
9f14534c10 Merge branch 'source_code_highlighting'
All checks were successful
format Build format has succeeded
clippy Build clippy has succeeded
rust-test Build rust-test has succeeded
build Build build has succeeded
2025-02-22 19:56:47 -05:00
Tom Alexander
4e34ebc29e Reformat css.
All checks were successful
format Build format has succeeded
clippy Build clippy has succeeded
rust-test Build rust-test has succeeded
2025-02-22 19:45:04 -05:00
Tom Alexander
8d85d5ef79 Fix clippy.
All checks were successful
format Build format has succeeded
clippy Build clippy has succeeded
rust-test Build rust-test has succeeded
2025-02-22 19:42:24 -05:00
Tom Alexander
7d73a3c948 Clean up. 2025-02-22 19:24:05 -05:00
Tom Alexander
c501f7cedc Add syntax highlighting for bash.
Some checks failed
format Build format has succeeded
clippy Build clippy has failed
rust-test Build rust-test has succeeded
2025-02-22 19:07:56 -05:00
Tom Alexander
41927764fc Continue highlights across code block lines. 2025-02-22 18:45:20 -05:00
Tom Alexander
75a763569b Disable ligatures in all code areas. 2025-02-22 18:27:45 -05:00
Tom Alexander
c67eb32774 Add more colors to python. 2025-02-22 18:26:33 -05:00
Tom Alexander
04952895cf Add support for highlighting python based on the nix highlighter. 2025-02-22 17:56:56 -05:00
Tom Alexander
749f6d7a55 Dynamically register which CSS files are needed. 2025-02-22 17:28:24 -05:00
Tom Alexander
c4cf814f8d Also add highlighting for paths. 2025-02-22 16:55:53 -05:00
Tom Alexander
3245e830d2 Assign more colors. 2025-02-22 16:46:47 -05:00
Tom Alexander
57eb1b81ec Start assigning colors. 2025-02-22 16:35:05 -05:00
Tom Alexander
c601c8697a Start a language-specific css file for highlight colors. 2025-02-22 16:25:36 -05:00
62 changed files with 1715 additions and 215 deletions

View File

@@ -203,7 +203,7 @@ spec:
- name: git-source - name: git-source
volumeClaimTemplate: volumeClaimTemplate:
spec: spec:
storageClassName: "nfs-client" storageClassName: "local-path"
accessModes: accessModes:
- ReadWriteOnce - ReadWriteOnce
resources: resources:

View File

@@ -345,7 +345,7 @@ spec:
- name: git-source - name: git-source
volumeClaimTemplate: volumeClaimTemplate:
spec: spec:
storageClassName: "nfs-client" storageClassName: "local-path"
accessModes: accessModes:
- ReadWriteOnce - ReadWriteOnce
resources: resources:

View File

@@ -289,7 +289,7 @@ spec:
- name: git-source - name: git-source
volumeClaimTemplate: volumeClaimTemplate:
spec: spec:
storageClassName: "nfs-client" storageClassName: "local-path"
accessModes: accessModes:
- ReadWriteOnce - ReadWriteOnce
resources: resources:

View File

@@ -279,7 +279,7 @@ spec:
- name: git-source - name: git-source
volumeClaimTemplate: volumeClaimTemplate:
spec: spec:
storageClassName: "nfs-client" storageClassName: "local-path"
accessModes: accessModes:
- ReadWriteOnce - ReadWriteOnce
resources: resources:

892
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
[package] [package]
name = "natter" name = "natter"
version = "0.0.1" version = "0.0.1"
edition = "2021" edition = "2024"
authors = ["Tom Alexander <tom@fizz.buzz>"] authors = ["Tom Alexander <tom@fizz.buzz>"]
description = "A static site generator using org source files." description = "A static site generator using org source files."
license = "0BSD" license = "0BSD"
@@ -31,9 +31,21 @@ serde = { version = "1.0.189", default-features = false, features = ["std", "der
serde_json = "1.0.107" serde_json = "1.0.107"
tokio = { version = "1.30.0", default-features = false, features = ["rt", "rt-multi-thread", "fs", "io-util"] } tokio = { version = "1.30.0", default-features = false, features = ["rt", "rt-multi-thread", "fs", "io-util"] }
toml = "0.8.2" toml = "0.8.2"
tree-sitter-bash = "0.23.3"
tree-sitter-highlight = "0.25.2" tree-sitter-highlight = "0.25.2"
tree-sitter-nix = "0.0.2" tree-sitter-nix = "0.0.2"
tree-sitter-python = "0.23.6"
url = "2.5.0" url = "2.5.0"
tracing = { version = "0.1.37", optional = true }
tracing-opentelemetry = { version = "0.20.0", optional = true }
tracing-subscriber = { version = "0.3.17", optional = true, features = ["env-filter"] }
opentelemetry = { version = "0.20.0", optional = true, default-features = false, features = ["trace", "rt-tokio"] }
opentelemetry-otlp = { version = "0.13.0", optional = true }
opentelemetry-semantic-conventions = { version = "0.12.0", optional = true }
[features]
default = ["tracing"]
tracing = ["dep:opentelemetry", "dep:opentelemetry-otlp", "dep:opentelemetry-semantic-conventions", "dep:tracing", "dep:tracing-opentelemetry", "dep:tracing-subscriber"]
# Optimized build for any sort of release. # Optimized build for any sort of release.
[profile.release-lto] [profile.release-lto]

21
TODO.org Normal file
View File

@@ -0,0 +1,21 @@
* Things to do [6/17]
** DONE If the paragraph only contains an image, text-align center
** DONE Syntax highlighting for code blocks
** TODO Render gnuplot
** TODO Pretty-print the timestamps
** TODO Support Table of Contents
** TODO Support line numbers in code blocks
** TODO Support references to code block lines
** TODO Only include text up to first heading on homepage and include a "read more" link
** DONE Make loading language-specific CSS files conditional on the presence of src blocks using those languages
** DONE Set up tracing so I can use warning and such
** TODO Make copying of language-specific CSS files conditional on the presence of src blocks using those languages
** TODO Switch to an entirely lazily-evaluated output tree
** TODO Add highlighting for languages [1/2]
*** DONE bash
*** TODO gnuplot
https://github.com/dpezto/tree-sitter-gnuplot is not on crates.io so I'd have to add a git dependency to use it. This would prevent publishing this crate to crates.io.
** DONE Bug: carry over highlight starts when breaking lines
** TODO Add dates to posts
** DONE Add support for unlisted posts (posts that do not show up on the homepage).
** TODO Add support for showing file name where we currently show language

View File

@@ -0,0 +1,39 @@
:root {
--srclg-bash-srchl-comment-color: #048a81;
--srclg-bash-srchl-function-color: #e95a62;
--srclg-bash-srchl-keyword-color: #1a936f;
--srclg-bash-srchl-property-color: inherit;
--srclg-bash-srchl-string-color: #ecc30b;
}
@media (prefers-color-scheme: light) {
:root {
--srclg-bash-srchl-comment-color: #fb757e;
--srclg-bash-srchl-function-color: #16a59d;
--srclg-bash-srchl-keyword-color: #e56c90;
--srclg-bash-srchl-property-color: inherit;
--srclg-bash-srchl-string-color: #133cf4;
}
}
.main_content {
.src_block {
&.srclg_bash {
.srchl_comment {
color: var(--srclg-bash-srchl-comment-color);
}
.srchl_function {
color: var(--srclg-bash-srchl-function-color);
}
.srchl_keyword {
color: var(--srclg-bash-srchl-keyword-color);
}
.srchl_property {
color: var(--srclg-bash-srchl-property-color);
}
.srchl_string {
color: var(--srclg-bash-srchl-string-color);
}
}
}
}

View File

@@ -0,0 +1,39 @@
:root {
--srclg-nix-srchl-keyword-color: #1a936f;
--srclg-nix-srchl-comment-color: #048a81;
--srclg-nix-srchl-property-color: #bfbccb;
--srclg-nix-srchl-string-color: #ecc30b;
--srclg-nix-srchl-string-special-path-color: #067bc2;
}
@media (prefers-color-scheme: light) {
:root {
--srclg-nix-srchl-keyword-color: #e56c90;
--srclg-nix-srchl-comment-color: #fb757e;
--srclg-nix-srchl-property-color: #404334;
--srclg-nix-srchl-string-color: #133cf4;
--srclg-nix-srchl-string-special-path-color: #f9843d;
}
}
.main_content {
.src_block {
&.srclg_nix {
.srchl_keyword {
color: var(--srclg-nix-srchl-keyword-color);
}
.srchl_comment {
color: var(--srclg-nix-srchl-comment-color);
}
.srchl_property {
color: var(--srclg-nix-srchl-property-color);
}
.srchl_string {
color: var(--srclg-nix-srchl-string-color);
}
.srchl_string_special_path {
color: var(--srclg-nix-srchl-string-special-path-color);
}
}
}
}

View File

@@ -0,0 +1,51 @@
/* ea912c */
/* e95a62 */
:root {
--srclg-python-srchl-comment-color: #048a81;
--srclg-python-srchl-function-builtin-color: #e95a62;
--srclg-python-srchl-keyword-color: #1a936f;
--srclg-python-srchl-property-color: inherit;
--srclg-python-srchl-string-color: #ecc30b;
--srclg-python-srchl-type-color: #067bc2;
--srclg-python-srchl-variable-color: #ea912c;
}
@media (prefers-color-scheme: light) {
:root {
--srclg-python-srchl-comment-color: #fb757e;
--srclg-python-srchl-function-builtin-color: #16a59d;
--srclg-python-srchl-keyword-color: #e56c90;
--srclg-python-srchl-property-color: inherit;
--srclg-python-srchl-string-color: #133cf4;
--srclg-python-srchl-type-color: #f9843d;
--srclg-python-srchl-variable-color: #156ed3;
}
}
.main_content {
.src_block {
&.srclg_python {
.srchl_comment {
color: var(--srclg-python-srchl-comment-color);
}
.srchl_function_builtin {
color: var(--srclg-python-srchl-function-builtin-color);
}
.srchl_keyword {
color: var(--srclg-python-srchl-keyword-color);
}
.srchl_property {
color: var(--srclg-python-srchl-property-color);
}
.srchl_string {
color: var(--srclg-python-srchl-string-color);
}
.srchl_type {
color: var(--srclg-python-srchl-type-color);
}
.srchl_variable {
color: var(--srclg-python-srchl-variable-color);
}
}
}
}

View File

@@ -9,9 +9,8 @@
--blog-post-background-color: #0a0a0a; --blog-post-background-color: #0a0a0a;
--src-font-family: --src-font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo,
ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, Consolas, "DejaVu Sans Mono", monospace;
"DejaVu Sans Mono", monospace;
--src-block-background-color: #141414; --src-block-background-color: #141414;
--src-block-border-color: #84828f; --src-block-border-color: #84828f;
@@ -23,6 +22,13 @@
--table-border-color: #6a687a; --table-border-color: #6a687a;
--table-odd-background-color: #0a0a0a; --table-odd-background-color: #0a0a0a;
--table-even-background-color: #141414; --table-even-background-color: #141414;
--header-nav-regular-font-color: var(--site-text-color);
--header-nav-regular-background-color: var(--site-background-color);
--header-nav-hover-font-color: var(--site-background-color);
--header-nav-hover-background-color: var(--site-text-color);
--header-home-regular-font-color: var(--site-text-color);
--header-home-hover-font-color: #6ccff6;
} }
@media (prefers-color-scheme: light) { @media (prefers-color-scheme: light) {
@@ -46,6 +52,13 @@
--table-border-color: #959785; --table-border-color: #959785;
--table-odd-background-color: #f5f5f5; --table-odd-background-color: #f5f5f5;
--table-even-background-color: #ebebeb; --table-even-background-color: #ebebeb;
--header-nav-regular-font-color: var(--site-text-color);
--header-nav-regular-background-color: var(--site-background-color);
--header-nav-hover-font-color: var(--site-background-color);
--header-nav-hover-background-color: var(--site-text-color);
--header-home-regular-font-color: var(--site-text-color);
--header-home-hover-font-color: #933009;
} }
} }
@@ -56,9 +69,8 @@
body { body {
color: var(--site-text-color); color: var(--site-text-color);
background-color: var(--site-background-color); background-color: var(--site-background-color);
font-family: font-family: source-sans-pro, Seravek, "Gill Sans Nova", Ubuntu, Calibri,
source-sans-pro, Seravek, "Gill Sans Nova", Ubuntu, Calibri, "DejaVu Sans", "DejaVu Sans", sans-serif;
sans-serif;
a:link, a:link,
a:visited { a:visited {
@@ -74,18 +86,57 @@ body {
} }
.page_header { .page_header {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: stretch;
width: 100%; width: 100%;
max-width: var(--main-max-width); max-width: var(--main-max-width);
border-bottom: 0.1rem solid var(--header-divider-color); border-bottom: 0.1rem solid var(--header-divider-color);
.home_link { .home_link {
display: block;
font-size: 2rem; font-size: 2rem;
font-weight: 600; font-weight: 600;
text-decoration: none; text-decoration: none;
color: var(--header-home-regular-font-color);
transition-property: color;
transition-duration: 0.1s;
transition-timing-function: ease-out;
&:hover {
color: var(--header-home-hover-font-color) !important;
}
&:link, &:link,
&:visited { &:visited {
color: var(--site-text-color); color: inherit;
}
}
.header_nav_bar {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: end;
align-items: stretch;
column-gap: 1rem;
.nav_link {
display: flex;
flex-direction: column;
justify-content: space-around;
color: var(--header-nav-regular-font-color);
background: var(--header-nav-regular-background-color);
padding: 0 0.5rem;
transition-property: background, color;
transition-duration: 0.1s;
transition-timing-function: ease-out;
&:hover {
color: var(--header-nav-hover-font-color);
background: var(--header-nav-hover-background-color);
}
} }
} }
} }
@@ -161,6 +212,7 @@ body {
font-size: 1rem; font-size: 1rem;
font-family: var(--src-font-family); font-family: var(--src-font-family);
margin: 1rem 0; margin: 1rem 0;
font-variant-ligatures: none;
.src_language { .src_language {
display: inline-block; display: inline-block;
@@ -185,11 +237,13 @@ body {
.inline_source_block { .inline_source_block {
font-family: var(--src-font-family); font-family: var(--src-font-family);
font-size: 1.2rem; font-size: 1.2rem;
font-variant-ligatures: none;
} }
.code, .code,
.verbatim { .verbatim {
font-family: var(--src-font-family); font-family: var(--src-font-family);
font-variant-ligatures: none;
} }
.quote_block { .quote_block {

View File

@@ -1,4 +1,9 @@
<header class="page_header"> <header class="page_header">
<a class="home_link" href="{.home_link}">{.website_title}</a> <a class="home_link" href="{.home_link}">{.website_title}</a>
{! TODO: Additional links? Probably using the nav semantic element. !} {! TODO: Additional links? Probably using the nav semantic element. !}
<nav class="header_nav_bar">
{#.nav_links}
<a class="nav_link" href="{.url}"><div>{.text}</div></a>
{/.nav_links}
</nav>
</header> </header>

View File

@@ -1,4 +1,4 @@
<div class="src_block"> <div class="src_block{?.language} srclg_{.language}{/.language}">
{?.language}<div class="src_language">{.language}</div>{/.language} {?.language}<div class="src_language">{.language}</div>{/.language}
<table class="src_body"> <table class="src_body">
<tbody> <tbody>

View File

@@ -1,8 +1,8 @@
use std::ffi::OsStr; use std::ffi::OsStr;
use std::path::PathBuf; use std::path::PathBuf;
use include_dir::include_dir;
use include_dir::Dir; use include_dir::Dir;
use include_dir::include_dir;
use tokio::fs::DirEntry; use tokio::fs::DirEntry;
use tokio::task::JoinHandle; use tokio::task::JoinHandle;
@@ -15,14 +15,15 @@ use crate::context::RenderBlogStreamInput;
use crate::context::RenderContext; use crate::context::RenderContext;
use crate::context::RenderPage; use crate::context::RenderPage;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::get_web_path;
use crate::intermediate::BlogPost; use crate::intermediate::BlogPost;
use crate::intermediate::IPage; use crate::intermediate::IPage;
use crate::intermediate::PublishStatus;
use crate::intermediate::get_web_path;
use crate::render::DusterRenderer; use crate::render::DusterRenderer;
use crate::render::RendererIntegration; use crate::render::RendererIntegration;
use crate::walk_fs::walk_fs;
use crate::walk_fs::WalkAction; use crate::walk_fs::WalkAction;
use crate::walk_fs::WalkFsFilterResult; use crate::walk_fs::WalkFsFilterResult;
use crate::walk_fs::walk_fs;
use super::stylesheet::Stylesheet; use super::stylesheet::Stylesheet;
@@ -84,7 +85,11 @@ impl SiteRenderer {
pub(crate) async fn render_pages(&self, config: &Config) -> Result<(), CustomError> { pub(crate) async fn render_pages(&self, config: &Config) -> Result<(), CustomError> {
let renderer_integration = self.init_renderer_integration()?; let renderer_integration = self.init_renderer_integration()?;
for page in &self.pages { for page in self.pages.iter().filter(|page| match page.natter_publish {
PublishStatus::Full => true,
PublishStatus::Unlisted => true,
PublishStatus::Unpublished => false,
}) {
let output_path = self.output_directory.join(page.get_output_path()); let output_path = self.output_directory.join(page.get_output_path());
let dependency_manager = let dependency_manager =
std::sync::Arc::new(std::sync::Mutex::new(DependencyManager::new())); std::sync::Arc::new(std::sync::Mutex::new(DependencyManager::new()));
@@ -115,7 +120,17 @@ impl SiteRenderer {
pub(crate) async fn render_blog_posts(&self, config: &Config) -> Result<(), CustomError> { pub(crate) async fn render_blog_posts(&self, config: &Config) -> Result<(), CustomError> {
let renderer_integration = self.init_renderer_integration()?; let renderer_integration = self.init_renderer_integration()?;
for blog_post in &self.blog_posts { for blog_post in self.blog_posts.iter().filter(|blog_post| {
match blog_post
.get_index_page()
.expect("Blog posts should have an index page.")
.natter_publish
{
PublishStatus::Full => true,
PublishStatus::Unlisted => true,
PublishStatus::Unpublished => false,
}
}) {
for blog_post_page in &blog_post.pages { for blog_post_page in &blog_post.pages {
let output_path = self let output_path = self
.output_directory .output_directory
@@ -155,7 +170,21 @@ impl SiteRenderer {
// Sort blog posts by date, newest first. // Sort blog posts by date, newest first.
let sorted_blog_posts = { let sorted_blog_posts = {
let mut sorted_blog_posts: Vec<_> = self.blog_posts.iter().collect(); let mut sorted_blog_posts: Vec<_> = self
.blog_posts
.iter()
.filter(|blog_post| {
match blog_post
.get_index_page()
.expect("Blog posts should have an index page.")
.natter_publish
{
PublishStatus::Full => true,
PublishStatus::Unlisted => false,
PublishStatus::Unpublished => false,
}
})
.collect();
sorted_blog_posts sorted_blog_posts
.sort_by_key(|blog_post| (blog_post.get_date(), blog_post.id.as_str())); .sort_by_key(|blog_post| (blog_post.get_date(), blog_post.id.as_str()));
sorted_blog_posts.reverse(); sorted_blog_posts.reverse();

View File

@@ -8,17 +8,17 @@ use crate::cli::parameters::BuildArgs;
use crate::command::build::render::SiteRenderer; use crate::command::build::render::SiteRenderer;
use crate::config::Config; use crate::config::Config;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::get_org_files;
use crate::intermediate::BlogPost; use crate::intermediate::BlogPost;
use crate::intermediate::IPage; use crate::intermediate::IPage;
use crate::intermediate::IntermediateContext; use crate::intermediate::IntermediateContext;
use crate::intermediate::PageInput; use crate::intermediate::PageInput;
use crate::intermediate::Registry; use crate::intermediate::Registry;
use crate::walk_fs::walk_fs; use crate::intermediate::get_org_files;
use crate::walk_fs::WalkAction; use crate::walk_fs::WalkAction;
use crate::walk_fs::WalkFsFilterResult; use crate::walk_fs::WalkFsFilterResult;
use include_dir::include_dir; use crate::walk_fs::walk_fs;
use include_dir::Dir; use include_dir::Dir;
use include_dir::include_dir;
use tokio::fs::DirEntry; use tokio::fs::DirEntry;
static DEFAULT_STYLESHEETS: Dir = static DEFAULT_STYLESHEETS: Dir =

View File

@@ -3,6 +3,8 @@ use serde::Serialize;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IAstNode; use crate::intermediate::IAstNode;
use super::RenderHeading;
use super::RenderSection;
use super::angle_link::RenderAngleLink; use super::angle_link::RenderAngleLink;
use super::babel_call::RenderBabelCall; use super::babel_call::RenderBabelCall;
use super::bold::RenderBold; use super::bold::RenderBold;
@@ -55,8 +57,6 @@ use super::timestamp::RenderTimestamp;
use super::underline::RenderUnderline; use super::underline::RenderUnderline;
use super::verbatim::RenderVerbatim; use super::verbatim::RenderVerbatim;
use super::verse_block::RenderVerseBlock; use super::verse_block::RenderVerseBlock;
use super::RenderHeading;
use super::RenderSection;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(untagged)] #[serde(untagged)]

View File

@@ -1,17 +1,19 @@
use std::collections::HashSet;
use serde::Serialize; use serde::Serialize;
use super::render_context::RenderContext; use super::render_context::RenderContext;
use crate::context::macros::push_file; use crate::context::macros::push_file;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::get_web_path;
use crate::intermediate::BlogPost; use crate::intermediate::BlogPost;
use crate::intermediate::BlogPostPage; use crate::intermediate::BlogPostPage;
use crate::intermediate::get_web_path;
use super::footnote_definition::RenderRealFootnoteDefinition;
use super::macros::render;
use super::GlobalSettings; use super::GlobalSettings;
use super::PageHeader; use super::PageHeader;
use super::RenderDocumentElement; use super::RenderDocumentElement;
use super::footnote_definition::RenderRealFootnoteDefinition;
use super::macros::render;
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct RenderBlogPostPageInput<'a> { pub(crate) struct RenderBlogPostPageInput<'a> {
@@ -51,28 +53,6 @@ render!(
render_context, render_context,
{ {
push_file!(render_context, &original.page.src, { push_file!(render_context, &original.page.src, {
let css_files = vec![
get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"stylesheet/reset.css",
)?,
get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"stylesheet/main.css",
)?,
];
let js_files = vec![get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"blog_post.js",
)?];
let global_settings =
GlobalSettings::new(original.page.title.clone(), css_files, js_files);
let page_header = PageHeader::new( let page_header = PageHeader::new(
render_context.config.get_site_title().map(str::to_string), render_context.config.get_site_title().map(str::to_string),
Some(get_web_path( Some(get_web_path(
@@ -81,6 +61,12 @@ render!(
render_context.output_file, render_context.output_file,
"", "",
)?), )?),
Some(get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"about_me",
)?),
); );
let link_to_blog_post = get_web_path( let link_to_blog_post = get_web_path(
render_context.config, render_context.config,
@@ -114,6 +100,46 @@ render!(
ret ret
}; };
let mut css_files = vec![
get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"stylesheet/reset.css",
)?,
get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"stylesheet/main.css",
)?,
];
let additional_css_files = render_context
.dependency_manager
.lock()
.unwrap()
.list_css()?
.map(|css_name| {
get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
format!("stylesheet/{}", css_name),
)
})
.collect::<Result<HashSet<_>, _>>()?;
css_files.extend(additional_css_files.into_iter());
let js_files = vec![get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"blog_post.js",
)?];
let global_settings =
GlobalSettings::new(original.page.title.clone(), css_files, js_files);
let ret = RenderBlogPostPage { let ret = RenderBlogPostPage {
global_settings, global_settings,
page_header: Some(page_header), page_header: Some(page_header),

View File

@@ -1,13 +1,15 @@
use std::collections::HashSet;
use serde::Serialize; use serde::Serialize;
use super::macros::render; use super::macros::render;
use super::render_context::RenderContext; use super::render_context::RenderContext;
use crate::context::macros::push_file;
use crate::context::RenderDocumentElement; use crate::context::RenderDocumentElement;
use crate::context::RenderRealFootnoteDefinition; use crate::context::RenderRealFootnoteDefinition;
use crate::context::macros::push_file;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::get_web_path;
use crate::intermediate::BlogPost; use crate::intermediate::BlogPost;
use crate::intermediate::get_web_path;
use super::GlobalSettings; use super::GlobalSettings;
use super::PageHeader; use super::PageHeader;
@@ -49,31 +51,6 @@ render!(
original, original,
render_context, render_context,
{ {
let css_files = vec![
get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"stylesheet/reset.css",
)?,
get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"stylesheet/main.css",
)?,
];
let js_files = vec![get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"blog_post.js",
)?];
let global_settings = GlobalSettings::new(
render_context.config.get_site_title().map(str::to_string),
css_files,
js_files,
);
let page_header = PageHeader::new( let page_header = PageHeader::new(
render_context.config.get_site_title().map(str::to_string), render_context.config.get_site_title().map(str::to_string),
Some(get_web_path( Some(get_web_path(
@@ -82,6 +59,12 @@ render!(
render_context.output_file, render_context.output_file,
"", "",
)?), )?),
Some(get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"about_me",
)?),
); );
let children = original let children = original
@@ -105,6 +88,49 @@ render!(
None None
}; };
let mut css_files = vec![
get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"stylesheet/reset.css",
)?,
get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"stylesheet/main.css",
)?,
];
let additional_css_files = render_context
.dependency_manager
.lock()
.unwrap()
.list_css()?
.map(|css_name| {
get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
format!("stylesheet/{}", css_name),
)
})
.collect::<Result<HashSet<_>, _>>()?;
css_files.extend(additional_css_files.into_iter());
let js_files = vec![get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"blog_post.js",
)?];
let global_settings = GlobalSettings::new(
render_context.config.get_site_title().map(str::to_string),
css_files,
js_files,
);
Ok(RenderBlogStream { Ok(RenderBlogStream {
global_settings, global_settings,
page_header: Some(page_header), page_header: Some(page_header),

View File

@@ -3,9 +3,9 @@ use serde::Serialize;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IBold; use crate::intermediate::IBold;
use super::RenderObject;
use super::macros::render; use super::macros::render;
use super::render_context::RenderContext; use super::render_context::RenderContext;
use super::RenderObject;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]

View File

@@ -7,6 +7,7 @@ use super::RenderContext;
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum Dependency { pub(crate) enum Dependency {
StaticFile { absolute_path: PathBuf }, StaticFile { absolute_path: PathBuf },
CssFile { name: String },
} }
impl Dependency { impl Dependency {
@@ -40,6 +41,11 @@ impl Dependency {
} }
Ok(()) Ok(())
} }
Dependency::CssFile { name: _ } => {
// We don't do anything because the CSS files are already copied into the output from natter's default environment.
// TODO: When we add support for CSS outside the default environment, we should add support for dynamically picking which ones to copy here.
Ok(())
}
} }
} }
} }

View File

@@ -65,4 +65,20 @@ impl DependencyManager {
std::mem::swap(&mut self.dependencies, &mut dependencies); std::mem::swap(&mut self.dependencies, &mut dependencies);
dependencies dependencies
} }
pub(crate) fn include_css<N>(&mut self, name: N) -> Result<(), CustomError>
where
std::string::String: From<N>,
{
self.dependencies
.push(Dependency::CssFile { name: name.into() });
Ok(())
}
pub(crate) fn list_css(&self) -> Result<impl Iterator<Item = &String>, CustomError> {
Ok(self.dependencies.iter().filter_map(|dep| match dep {
Dependency::CssFile { name } => Some(name),
_ => None,
}))
}
} }

View File

@@ -4,9 +4,9 @@ use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IDocumentElement; use crate::intermediate::IDocumentElement;
use super::macros::render;
use super::RenderHeading; use super::RenderHeading;
use super::RenderSection; use super::RenderSection;
use super::macros::render;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(untagged)] #[serde(untagged)]

View File

@@ -4,9 +4,9 @@ use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IHeading; use crate::intermediate::IHeading;
use super::macros::render;
use super::RenderDocumentElement; use super::RenderDocumentElement;
use super::RenderObject; use super::RenderObject;
use super::macros::render;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]

View File

@@ -4,8 +4,8 @@ use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IItalic; use crate::intermediate::IItalic;
use super::macros::render;
use super::RenderObject; use super::RenderObject;
use super::macros::render;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]

View File

@@ -1,13 +1,15 @@
use super::footnote_definition::RenderRealFootnoteDefinition; use std::collections::HashSet;
use super::macros::render;
use super::render_context::RenderContext;
use super::GlobalSettings; use super::GlobalSettings;
use super::PageHeader; use super::PageHeader;
use super::RenderDocumentElement; use super::RenderDocumentElement;
use super::footnote_definition::RenderRealFootnoteDefinition;
use super::macros::render;
use super::render_context::RenderContext;
use crate::context::macros::push_file; use crate::context::macros::push_file;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::get_web_path;
use crate::intermediate::IPage; use crate::intermediate::IPage;
use crate::intermediate::get_web_path;
use serde::Serialize; use serde::Serialize;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
@@ -30,27 +32,6 @@ pub(crate) struct RenderPage {
render!(RenderPage, IPage, original, render_context, { render!(RenderPage, IPage, original, render_context, {
push_file!(render_context, &original.src, { push_file!(render_context, &original.src, {
let css_files = vec![
get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"stylesheet/reset.css",
)?,
get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"stylesheet/main.css",
)?,
];
let js_files = vec![get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"blog_post.js",
)?];
let global_settings = GlobalSettings::new(original.title.clone(), css_files, js_files);
let page_header = PageHeader::new( let page_header = PageHeader::new(
render_context.config.get_site_title().map(str::to_string), render_context.config.get_site_title().map(str::to_string),
Some(get_web_path( Some(get_web_path(
@@ -59,6 +40,12 @@ render!(RenderPage, IPage, original, render_context, {
render_context.output_file, render_context.output_file,
"", "",
)?), )?),
Some(get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"about_me",
)?),
); );
let link_to_blog_post = get_web_path( let link_to_blog_post = get_web_path(
render_context.config, render_context.config,
@@ -92,6 +79,45 @@ render!(RenderPage, IPage, original, render_context, {
ret ret
}; };
let mut css_files = vec![
get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"stylesheet/reset.css",
)?,
get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"stylesheet/main.css",
)?,
];
let additional_css_files = render_context
.dependency_manager
.lock()
.unwrap()
.list_css()?
.map(|css_name| {
get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
format!("stylesheet/{}", css_name),
)
})
.collect::<Result<HashSet<_>, _>>()?;
css_files.extend(additional_css_files.into_iter());
let js_files = vec![get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"blog_post.js",
)?];
let global_settings = GlobalSettings::new(original.title.clone(), css_files, js_files);
let ret = RenderPage { let ret = RenderPage {
global_settings, global_settings,
page_header: Some(page_header), page_header: Some(page_header),

View File

@@ -7,13 +7,35 @@ use serde::Serialize;
pub(crate) struct PageHeader { pub(crate) struct PageHeader {
website_title: Option<String>, website_title: Option<String>,
home_link: Option<String>, home_link: Option<String>,
nav_links: Vec<NavLink>,
}
/// A link in the top-right of the page.
#[derive(Debug, Serialize)]
#[serde(tag = "type")]
#[serde(rename = "nav_link")]
pub(crate) struct NavLink {
text: Option<String>,
url: Option<String>,
} }
impl PageHeader { impl PageHeader {
pub(crate) fn new(website_title: Option<String>, home_link: Option<String>) -> PageHeader { pub(crate) fn new(
website_title: Option<String>,
home_link: Option<String>,
about_me_link: Option<String>,
) -> PageHeader {
PageHeader { PageHeader {
website_title, website_title,
home_link, home_link,
nav_links: about_me_link
.map(|url| {
vec![NavLink {
text: Some("About Me".to_owned()),
url: Some(url),
}]
})
.unwrap_or_default(),
} }
} }
} }

View File

@@ -4,8 +4,8 @@ use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IParagraph; use crate::intermediate::IParagraph;
use super::macros::render;
use super::RenderObject; use super::RenderObject;
use super::macros::render;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]

View File

@@ -4,9 +4,9 @@ use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IPlainListItem; use crate::intermediate::IPlainListItem;
use super::macros::render;
use super::RenderElement; use super::RenderElement;
use super::RenderObject; use super::RenderObject;
use super::macros::render;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]

View File

@@ -4,8 +4,8 @@ use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IPlainListSimpleItem; use crate::intermediate::IPlainListSimpleItem;
use super::macros::render;
use super::RenderObject; use super::RenderObject;
use super::macros::render;
/// Special case for list items with only paragraphs and sublists as their children. In those cases, the paragraph tags are omitted. This is equivalent to a Paragraph but in a different struct to have a different type tag. /// Special case for list items with only paragraphs and sublists as their children. In those cases, the paragraph tags are omitted. This is equivalent to a Paragraph but in a different struct to have a different type tag.
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]

View File

@@ -4,8 +4,8 @@ use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IQuoteBlock; use crate::intermediate::IQuoteBlock;
use super::macros::render;
use super::RenderElement; use super::RenderElement;
use super::macros::render;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]

View File

@@ -5,8 +5,8 @@ use crate::error::CustomError;
use crate::intermediate::IRegularLink; use crate::intermediate::IRegularLink;
use crate::intermediate::LinkTarget; use crate::intermediate::LinkTarget;
use super::macros::render;
use super::RenderObject; use super::RenderObject;
use super::macros::render;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]

View File

@@ -4,8 +4,8 @@ use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::ISection; use crate::intermediate::ISection;
use super::macros::render;
use super::RenderElement; use super::RenderElement;
use super::macros::render;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]

View File

@@ -34,7 +34,7 @@ pub(crate) enum RenderSrcSegment {
HighlightEnd, HighlightEnd,
} }
render!(RenderSrcBlock, ISrcBlock, original, _render_context, { render!(RenderSrcBlock, ISrcBlock, original, render_context, {
let lines = original let lines = original
.lines .lines
.iter() .iter()
@@ -47,7 +47,7 @@ render!(RenderSrcBlock, ISrcBlock, original, _render_context, {
content: body.to_owned(), content: body.to_owned(),
}, },
ISrcSegment::HighlightStart { name } => RenderSrcSegment::HighlightStart { ISrcSegment::HighlightStart { name } => RenderSrcSegment::HighlightStart {
name: name.to_owned(), name: css_safe_name(name),
}, },
ISrcSegment::HighlightEnd => RenderSrcSegment::HighlightEnd, ISrcSegment::HighlightEnd => RenderSrcSegment::HighlightEnd,
}) })
@@ -55,9 +55,41 @@ render!(RenderSrcBlock, ISrcBlock, original, _render_context, {
RenderSrcLine { children } RenderSrcLine { children }
}) })
.collect(); .collect();
match original.language.as_deref() {
Some("bash") => {
render_context
.dependency_manager
.lock()
.unwrap()
.include_css("language_bash.css")?;
}
Some("nix") => {
render_context
.dependency_manager
.lock()
.unwrap()
.include_css("language_nix.css")?;
}
Some("python") => {
render_context
.dependency_manager
.lock()
.unwrap()
.include_css("language_python.css")?;
}
_ => {}
};
Ok(RenderSrcBlock { Ok(RenderSrcBlock {
lines, lines,
language: original.language.clone(), language: original.language.clone(),
post_blank: original.post_blank, post_blank: original.post_blank,
}) })
}); });
fn css_safe_name<S>(inp: S) -> String
where
std::string::String: From<S>,
{
let inp: String = inp.into();
inp.replace(".", "_")
}

View File

@@ -4,8 +4,8 @@ use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IStrikeThrough; use crate::intermediate::IStrikeThrough;
use super::macros::render;
use super::RenderObject; use super::RenderObject;
use super::macros::render;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]

View File

@@ -4,8 +4,8 @@ use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::ITableCell; use crate::intermediate::ITableCell;
use super::macros::render;
use super::RenderObject; use super::RenderObject;
use super::macros::render;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]

View File

@@ -4,8 +4,8 @@ use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IUnderline; use crate::intermediate::IUnderline;
use super::macros::render;
use super::RenderObject; use super::RenderObject;
use super::macros::render;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]

88
src/init_tracing.rs Normal file
View File

@@ -0,0 +1,88 @@
#[cfg(feature = "tracing")]
use opentelemetry_otlp::WithExportConfig;
#[cfg(feature = "tracing")]
use tracing::warn;
#[cfg(feature = "tracing")]
use tracing_subscriber::fmt;
#[cfg(feature = "tracing")]
use tracing_subscriber::prelude::__tracing_subscriber_SubscriberExt;
#[cfg(feature = "tracing")]
use tracing_subscriber::util::SubscriberInitExt;
const SERVICE_NAME: &str = "natter";
// Despite the obvious verbosity that fully-qualifying everything causes, in these functions I am fully-qualifying everything relating to tracing. This is because the tracing feature involves multiple libraries working together and so I think it is beneficial to see which libraries contribute which bits.
#[cfg(feature = "tracing")]
pub(crate) fn init_telemetry() -> Result<(), Box<dyn std::error::Error>> {
let log_to_console = fmt::layer();
let subscriber = tracing_subscriber::Registry::default();
let level_filter_layer = tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or(tracing_subscriber::EnvFilter::new("WARN"));
// by default it will hit http://localhost:4317 with a gRPC payload
// TODO: I think the endpoint can be controlled by the OTEL_EXPORTER_OTLP_TRACES_ENDPOINT env variable instead of hard-coded into this code base. Regardless, I am the only developer right now so I am not too concerned.
let exporter = opentelemetry_otlp::new_exporter()
.tonic()
// Using "localhost" is broken inside the docker container when tracing
.with_endpoint("http://127.0.0.1:4317/v1/traces");
let tracer = opentelemetry_otlp::new_pipeline()
.tracing()
.with_exporter(exporter)
.with_trace_config(opentelemetry::sdk::trace::config().with_resource(
opentelemetry::sdk::Resource::new(vec![opentelemetry::KeyValue::new(
opentelemetry_semantic_conventions::resource::SERVICE_NAME,
SERVICE_NAME.to_string(),
)]),
))
// If I do install_batch then 1K+ spans will get orphaned off into their own trace and I get the error message "OpenTelemetry trace error occurred. cannot send message to batch processor as the channel is closed"
//
// If I do install_simple then it only creates 1 trace (which is good!) but my console gets spammed with this concerning log message that makes me think it might be dropping the extra spans on the floor: "OpenTelemetry trace error occurred. Exporter otlp encountered the following error(s): the grpc server returns error (Unknown error): , detailed error message: Service was not ready: transport error"
//
// I suspect it is related to this bug: https://github.com/open-telemetry/opentelemetry-rust/issues/888
//
// .install_simple()
.install_batch(opentelemetry::runtime::Tokio);
let tracing_layer = tracer.map(|tracer| tracing_opentelemetry::layer().with_tracer(tracer));
opentelemetry::global::set_text_map_propagator(
opentelemetry::sdk::propagation::TraceContextPropagator::new(),
);
match tracing_layer {
Ok(tracing_layer) => {
subscriber
.with(level_filter_layer)
.with(tracing_layer)
.with(log_to_console)
.try_init()?;
}
Err(e) => {
subscriber
.with(level_filter_layer)
.with(fmt::layer())
.try_init()?;
warn!("Failed initialize OpenTelemetry tracing: {}", e);
}
};
Ok(())
}
#[cfg(feature = "tracing")]
pub(crate) fn shutdown_telemetry() -> Result<(), Box<dyn std::error::Error>> {
opentelemetry::global::shutdown_tracer_provider();
Ok(())
}
#[cfg(not(feature = "tracing"))]
pub(crate) fn init_telemetry() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
#[cfg(not(feature = "tracing"))]
pub(crate) fn shutdown_telemetry() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}

View File

@@ -1,28 +1,3 @@
use super::angle_link::IAngleLink;
use super::bold::IBold;
use super::citation::ICitation;
use super::citation_reference::ICitationReference;
use super::code::ICode;
use super::comment::IComment;
use super::entity::IEntity;
use super::export_snippet::IExportSnippet;
use super::footnote_reference::IFootnoteReference;
use super::inline_babel_call::IInlineBabelCall;
use super::inline_source_block::IInlineSourceBlock;
use super::italic::IItalic;
use super::keyword::IKeyword;
use super::latex_fragment::ILatexFragment;
use super::line_break::ILineBreak;
use super::org_macro::IOrgMacro;
use super::plain_link::IPlainLink;
use super::plain_text::IPlainText;
use super::radio_link::IRadioLink;
use super::radio_target::IRadioTarget;
use super::regular_link::IRegularLink;
use super::statistics_cookie::IStatisticsCookie;
use super::strike_through::IStrikeThrough;
use super::subscript::ISubscript;
use super::superscript::ISuperscript;
use super::IBabelCall; use super::IBabelCall;
use super::ICenterBlock; use super::ICenterBlock;
use super::IClock; use super::IClock;
@@ -52,6 +27,31 @@ use super::IUnderline;
use super::IVerbatim; use super::IVerbatim;
use super::IVerseBlock; use super::IVerseBlock;
use super::IntermediateContext; use super::IntermediateContext;
use super::angle_link::IAngleLink;
use super::bold::IBold;
use super::citation::ICitation;
use super::citation_reference::ICitationReference;
use super::code::ICode;
use super::comment::IComment;
use super::entity::IEntity;
use super::export_snippet::IExportSnippet;
use super::footnote_reference::IFootnoteReference;
use super::inline_babel_call::IInlineBabelCall;
use super::inline_source_block::IInlineSourceBlock;
use super::italic::IItalic;
use super::keyword::IKeyword;
use super::latex_fragment::ILatexFragment;
use super::line_break::ILineBreak;
use super::org_macro::IOrgMacro;
use super::plain_link::IPlainLink;
use super::plain_text::IPlainText;
use super::radio_link::IRadioLink;
use super::radio_target::IRadioTarget;
use super::regular_link::IRegularLink;
use super::statistics_cookie::IStatisticsCookie;
use super::strike_through::IStrikeThrough;
use super::subscript::ISubscript;
use super::superscript::ISuperscript;
use crate::error::CustomError; use crate::error::CustomError;
use futures::future::{BoxFuture, FutureExt}; use futures::future::{BoxFuture, FutureExt};

View File

@@ -7,12 +7,12 @@ use tokio::fs::DirEntry;
use tokio::task::JoinHandle; use tokio::task::JoinHandle;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IntermediateContext;
use crate::intermediate::blog_post_page::BlogPostPageInput; use crate::intermediate::blog_post_page::BlogPostPageInput;
use crate::intermediate::registry::Registry; use crate::intermediate::registry::Registry;
use crate::intermediate::IntermediateContext;
use crate::walk_fs::walk_fs;
use crate::walk_fs::WalkAction; use crate::walk_fs::WalkAction;
use crate::walk_fs::WalkFsFilterResult; use crate::walk_fs::WalkFsFilterResult;
use crate::walk_fs::walk_fs;
use super::BlogPostPage; use super::BlogPostPage;

View File

@@ -4,10 +4,10 @@ use crate::error::CustomError;
use super::footnote_definition::IRealFootnoteDefinition; use super::footnote_definition::IRealFootnoteDefinition;
use super::macros::intermediate;
use super::IDocumentElement; use super::IDocumentElement;
use super::IHeading; use super::IHeading;
use super::ISection; use super::ISection;
use super::macros::intermediate;
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct BlogPostPageInput<'b, 'parse> { pub(crate) struct BlogPostPageInput<'b, 'parse> {
@@ -48,6 +48,16 @@ pub(crate) struct BlogPostPage {
pub(crate) children: Vec<IDocumentElement>, pub(crate) children: Vec<IDocumentElement>,
pub(crate) footnotes: Vec<IRealFootnoteDefinition>, pub(crate) footnotes: Vec<IRealFootnoteDefinition>,
pub(crate) natter_publish: PublishStatus,
}
#[derive(Debug, Default)]
pub(crate) enum PublishStatus {
#[default]
Full,
Unlisted,
Unpublished,
} }
intermediate!( intermediate!(
@@ -93,6 +103,7 @@ intermediate!(
date: get_date(original.document), date: get_date(original.document),
children, children,
footnotes, footnotes,
natter_publish: get_publish_status(original.document).unwrap_or_default(),
}) })
} }
); );
@@ -129,3 +140,25 @@ pub(crate) fn get_date(document: &organic::types::Document<'_>) -> Option<String
.last() .last()
.map(|kw| kw.value.to_owned()) .map(|kw| kw.value.to_owned())
} }
pub(crate) fn get_publish_status(document: &organic::types::Document<'_>) -> Option<PublishStatus> {
let publish_string = organic::types::AstNode::from(document)
.iter_all_ast_nodes()
.filter_map(|node| match node {
organic::types::AstNode::Keyword(kw)
if kw.key.eq_ignore_ascii_case("natter_publish") =>
{
Some(kw)
}
_ => None,
})
.last()
.map(|kw| kw.value);
match publish_string {
Some("full") => Some(PublishStatus::Full),
Some("unlisted") => Some(PublishStatus::Unlisted),
Some("unpublished") => Some(PublishStatus::Unpublished),
Some(status) => panic!("Unrecognized publish status: {}", status),
None => None,
}
}

View File

@@ -1,7 +1,7 @@
use organic::types::StandardProperties; use organic::types::StandardProperties;
use super::macros::intermediate;
use super::IObject; use super::IObject;
use super::macros::intermediate;
use crate::error::CustomError; use crate::error::CustomError;

View File

@@ -1,7 +1,7 @@
use super::IPlainListSimpleItem;
use super::comment::IComment; use super::comment::IComment;
use super::keyword::IKeyword; use super::keyword::IKeyword;
use super::macros::iselector; use super::macros::iselector;
use super::IPlainListSimpleItem;
use super::IBabelCall; use super::IBabelCall;
use super::ICenterBlock; use super::ICenterBlock;

View File

@@ -1,7 +1,7 @@
use super::macros::intermediate;
use super::registry::register_footnote_definition;
use super::IAstNode; use super::IAstNode;
use super::IntermediateContext; use super::IntermediateContext;
use super::macros::intermediate;
use super::registry::register_footnote_definition;
use crate::error::CustomError; use crate::error::CustomError;
use organic::types::StandardProperties; use organic::types::StandardProperties;
@@ -32,6 +32,7 @@ pub(crate) struct IRealFootnoteDefinition {
} }
impl IRealFootnoteDefinition { impl IRealFootnoteDefinition {
#[allow(clippy::needless_lifetimes)]
pub(crate) async fn new<'orig, 'parse>( pub(crate) async fn new<'orig, 'parse>(
_intermediate_context: IntermediateContext<'orig, 'parse>, _intermediate_context: IntermediateContext<'orig, 'parse>,
footnote_id: usize, footnote_id: usize,

View File

@@ -1,6 +1,6 @@
use super::macros::intermediate;
use super::IDocumentElement; use super::IDocumentElement;
use super::IObject; use super::IObject;
use super::macros::intermediate;
use crate::error::CustomError; use crate::error::CustomError;
use organic::types::StandardProperties; use organic::types::StandardProperties;

View File

@@ -1,7 +1,7 @@
use organic::types::StandardProperties; use organic::types::StandardProperties;
use super::macros::intermediate;
use super::IObject; use super::IObject;
use super::macros::intermediate;
use crate::error::CustomError; use crate::error::CustomError;

View File

@@ -9,6 +9,7 @@ macro_rules! inoop {
} }
impl $istruct { impl $istruct {
#[allow(clippy::extra_unused_lifetimes)]
pub(crate) async fn new<'reg, 'orig, 'parse>( pub(crate) async fn new<'reg, 'orig, 'parse>(
_intermediate_context: crate::intermediate::IntermediateContext<'orig, 'parse>, _intermediate_context: crate::intermediate::IntermediateContext<'orig, 'parse>,
original: &'orig organic::types::$pstruct<'parse>, original: &'orig organic::types::$pstruct<'parse>,

View File

@@ -71,9 +71,10 @@ mod verse_block;
pub(crate) use angle_link::IAngleLink; pub(crate) use angle_link::IAngleLink;
pub(crate) use ast_node::IAstNode; pub(crate) use ast_node::IAstNode;
pub(crate) use babel_call::IBabelCall; pub(crate) use babel_call::IBabelCall;
pub(crate) use blog_post::get_org_files;
pub(crate) use blog_post::BlogPost; pub(crate) use blog_post::BlogPost;
pub(crate) use blog_post::get_org_files;
pub(crate) use blog_post_page::BlogPostPage; pub(crate) use blog_post_page::BlogPostPage;
pub(crate) use blog_post_page::PublishStatus;
pub(crate) use bold::IBold; pub(crate) use bold::IBold;
pub(crate) use center_block::ICenterBlock; pub(crate) use center_block::ICenterBlock;
pub(crate) use citation::ICitation; pub(crate) use citation::ICitation;

View File

@@ -21,6 +21,7 @@ use super::plain_text::IPlainText;
use super::radio_link::IRadioLink; use super::radio_link::IRadioLink;
use super::radio_target::IRadioTarget; use super::radio_target::IRadioTarget;
use super::ITarget;
use super::regular_link::IRegularLink; use super::regular_link::IRegularLink;
use super::statistics_cookie::IStatisticsCookie; use super::statistics_cookie::IStatisticsCookie;
use super::strike_through::IStrikeThrough; use super::strike_through::IStrikeThrough;
@@ -29,7 +30,6 @@ use super::superscript::ISuperscript;
use super::timestamp::ITimestamp; use super::timestamp::ITimestamp;
use super::underline::IUnderline; use super::underline::IUnderline;
use super::verbatim::IVerbatim; use super::verbatim::IVerbatim;
use super::ITarget;
use futures::future::{BoxFuture, FutureExt}; use futures::future::{BoxFuture, FutureExt};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View File

@@ -1,10 +1,12 @@
use super::blog_post_page::get_date;
use super::blog_post_page::get_title;
use super::footnote_definition::IRealFootnoteDefinition;
use super::macros::intermediate;
use super::IDocumentElement; use super::IDocumentElement;
use super::IHeading; use super::IHeading;
use super::ISection; use super::ISection;
use super::PublishStatus;
use super::blog_post_page::get_date;
use super::blog_post_page::get_publish_status;
use super::blog_post_page::get_title;
use super::footnote_definition::IRealFootnoteDefinition;
use super::macros::intermediate;
use crate::error::CustomError; use crate::error::CustomError;
use std::path::PathBuf; use std::path::PathBuf;
@@ -24,6 +26,8 @@ pub(crate) struct IPage {
pub(crate) children: Vec<IDocumentElement>, pub(crate) children: Vec<IDocumentElement>,
pub(crate) footnotes: Vec<IRealFootnoteDefinition>, pub(crate) footnotes: Vec<IRealFootnoteDefinition>,
pub(crate) natter_publish: PublishStatus,
} }
intermediate!( intermediate!(
@@ -69,6 +73,7 @@ intermediate!(
date: get_date(original.document), date: get_date(original.document),
children, children,
footnotes, footnotes,
natter_publish: get_publish_status(original.document).unwrap_or_default(),
}) })
} }
); );

View File

@@ -1,7 +1,5 @@
use std::any::Any;
use super::macros::intermediate;
use super::IObject; use super::IObject;
use super::macros::intermediate;
use crate::error::CustomError; use crate::error::CustomError;
use organic::types::StandardProperties; use organic::types::StandardProperties;
@@ -33,6 +31,7 @@ intermediate!(
); );
impl IParagraph { impl IParagraph {
#[allow(clippy::needless_lifetimes)]
pub(crate) async fn artificial<'orig, 'parse>( pub(crate) async fn artificial<'orig, 'parse>(
_intermediate_context: crate::intermediate::IntermediateContext<'orig, 'parse>, _intermediate_context: crate::intermediate::IntermediateContext<'orig, 'parse>,
children: Vec<IObject>, children: Vec<IObject>,
@@ -52,19 +51,19 @@ impl IParagraph {
.children .children
.iter() .iter()
.filter(|c| match c { .filter(|c| match c {
IObject::RegularLink(iregular_link) => match &iregular_link.target { IObject::RegularLink(iregular_link) => matches!(
super::LinkTarget::Image { src, alt } => true, &iregular_link.target,
_ => false, super::LinkTarget::Image { src: _, alt: _ }
}, ),
_ => false, _ => false,
}) })
.count(); .count();
num_images == 1 num_images == 1
&& self.children.iter().all(|c| match c { && self.children.iter().all(|c| match c {
IObject::RegularLink(iregular_link) => match &iregular_link.target { IObject::RegularLink(iregular_link) => matches!(
super::LinkTarget::Image { src, alt } => true, &iregular_link.target,
_ => false, super::LinkTarget::Image { src: _, alt: _ }
}, ),
IObject::PlainText(iplain_text) => { IObject::PlainText(iplain_text) => {
iplain_text.source.chars().all(|c| c.is_ascii_whitespace()) iplain_text.source.chars().all(|c| c.is_ascii_whitespace())
} }

View File

@@ -1,5 +1,5 @@
use super::macros::intermediate;
use super::IPlainListItem; use super::IPlainListItem;
use super::macros::intermediate;
use crate::error::CustomError; use crate::error::CustomError;
use organic::types::StandardProperties; use organic::types::StandardProperties;

View File

@@ -1,5 +1,5 @@
use super::macros::intermediate;
use super::IPlainListSimpleItem; use super::IPlainListSimpleItem;
use super::macros::intermediate;
use super::IElement; use super::IElement;
use super::IObject; use super::IObject;

View File

@@ -1,5 +1,5 @@
use super::macros::intermediate;
use super::IObject; use super::IObject;
use super::macros::intermediate;
use crate::error::CustomError; use crate::error::CustomError;
use organic::types::StandardProperties; use organic::types::StandardProperties;

View File

@@ -1,5 +1,5 @@
use super::macros::intermediate;
use super::IElement; use super::IElement;
use super::macros::intermediate;
use crate::error::CustomError; use crate::error::CustomError;
use organic::types::StandardProperties; use organic::types::StandardProperties;

View File

@@ -3,11 +3,11 @@ use organic::types::Element;
use organic::types::Object; use organic::types::Object;
use std::collections::HashMap; use std::collections::HashMap;
use super::ast_node::IAstNode;
use super::ast_node::IntoIAstNode;
use super::IObject; use super::IObject;
use super::IParagraph; use super::IParagraph;
use super::IntermediateContext; use super::IntermediateContext;
use super::ast_node::IAstNode;
use super::ast_node::IntoIAstNode;
type IdCounter = u16; type IdCounter = u16;
@@ -180,6 +180,7 @@ async fn convert_definition_contents<'orig, 'parse>(
} }
/// Take a footnote definition that has not yet received a reference and move it into the active footnotes. /// Take a footnote definition that has not yet received a reference and move it into the active footnotes.
#[allow(clippy::needless_lifetimes)]
pub(crate) async fn promote_footnote_definition<'orig, 'parse>( pub(crate) async fn promote_footnote_definition<'orig, 'parse>(
intermediate_context: IntermediateContext<'orig, 'parse>, intermediate_context: IntermediateContext<'orig, 'parse>,
label: &'parse str, label: &'parse str,

View File

@@ -5,9 +5,9 @@ use organic::types::LinkType;
use organic::types::StandardProperties; use organic::types::StandardProperties;
use url::Url; use url::Url;
use super::IntermediateContext;
use super::get_web_path; use super::get_web_path;
use super::macros::intermediate; use super::macros::intermediate;
use super::IntermediateContext;
use super::IObject; use super::IObject;
use crate::context::RenderContext; use crate::context::RenderContext;
@@ -186,7 +186,7 @@ impl LinkTarget {
if path.is_absolute() { if path.is_absolute() {
return Ok(format!("file://{}", input)); return Ok(format!("file://{}", input));
} }
return Ok(input.into_owned()); Ok(input.into_owned())
} }
/// Get file name from the last segment of an image path. /// Get file name from the last segment of an image path.
@@ -199,7 +199,7 @@ impl LinkTarget {
let path = Path::new(input.as_ref()); let path = Path::new(input.as_ref());
match path match path
.components() .components()
.last() .next_back()
.ok_or("Images should have at least one component in their path.")? .ok_or("Images should have at least one component in their path.")?
{ {
std::path::Component::Prefix(_) => { std::path::Component::Prefix(_) => {
@@ -209,9 +209,7 @@ impl LinkTarget {
std::path::Component::RootDir std::path::Component::RootDir
| std::path::Component::CurDir | std::path::Component::CurDir
| std::path::Component::ParentDir => { | std::path::Component::ParentDir => {
return Err( Err("Final component of an image path should be a normal component.".into())
"Final component of an image path should be a normal component.".into(),
);
} }
std::path::Component::Normal(file_name) => Ok(file_name std::path::Component::Normal(file_name) => Ok(file_name
.to_str() .to_str()
@@ -236,6 +234,7 @@ mod tests {
let registry = Registry::new(); let registry = Registry::new();
let registry = Arc::new(Mutex::new(registry)); let registry = Arc::new(Mutex::new(registry));
let intermediate_context = IntermediateContext::new(registry)?; let intermediate_context = IntermediateContext::new(registry)?;
#[allow(clippy::single_element_loop)]
for (inp, typ) in [( for (inp, typ) in [(
"https://test.example/foo", "https://test.example/foo",
LinkType::Protocol(Cow::from("https")), LinkType::Protocol(Cow::from("https")),

View File

@@ -1,5 +1,5 @@
use super::macros::intermediate;
use super::IElement; use super::IElement;
use super::macros::intermediate;
use crate::error::CustomError; use crate::error::CustomError;
use organic::types::StandardProperties; use organic::types::StandardProperties;

View File

@@ -1,10 +1,8 @@
use std::borrow::Borrow; use std::borrow::Borrow;
use std::borrow::Cow;
use super::macros::intermediate; use super::macros::intermediate;
use crate::error::CustomError; use crate::error::CustomError;
use organic::types::StandardProperties; use organic::types::StandardProperties;
use tree_sitter_highlight::Highlight;
use tree_sitter_highlight::HighlightConfiguration; use tree_sitter_highlight::HighlightConfiguration;
use tree_sitter_highlight::HighlightEvent; use tree_sitter_highlight::HighlightEvent;
use tree_sitter_highlight::Highlighter; use tree_sitter_highlight::Highlighter;
@@ -79,8 +77,20 @@ intermediate!(
.collect(); .collect();
let language = original.language.map(str::to_owned); let language = original.language.map(str::to_owned);
match language.as_ref().map(String::as_str) { match language.as_deref() {
Some("nix") => { Some(lang @ "bash") => {
let highlighted = highlight_bash(&lines);
if let Ok(highlighted) = highlighted {
return Ok(ISrcBlock {
lines: highlighted,
language,
post_blank: original.get_post_blank(),
});
} else {
println!("Warning: Failed to highlight {} source.", lang);
}
}
Some(lang @ "nix") => {
let highlighted = highlight_nix(&lines); let highlighted = highlight_nix(&lines);
if let Ok(highlighted) = highlighted { if let Ok(highlighted) = highlighted {
return Ok(ISrcBlock { return Ok(ISrcBlock {
@@ -88,8 +98,25 @@ intermediate!(
language, language,
post_blank: original.get_post_blank(), post_blank: original.get_post_blank(),
}); });
} else {
println!("Warning: Failed to highlight {} source.", lang);
} }
} }
Some(lang @ "python") => {
let highlighted = highlight_python(&lines);
if let Ok(highlighted) = highlighted {
return Ok(ISrcBlock {
lines: highlighted,
language,
post_blank: original.get_post_blank(),
});
} else {
println!("Warning: Failed to highlight {} source.", lang);
}
}
Some(lang) => {
println!("Warning: No highlighting for language: {}", lang);
}
_ => {} _ => {}
}; };
let highlighted = highlight_plain(&lines)?; let highlighted = highlight_plain(&lines)?;
@@ -124,7 +151,7 @@ where
std::string::String: for<'a> From<&'a L>, std::string::String: for<'a> From<&'a L>,
{ {
Ok(lines Ok(lines
.into_iter() .iter()
.map(|l| { .map(|l| {
let mut line = ISrcLine::new(); let mut line = ISrcLine::new();
line.children.push(ISrcSegment::RawText(l.into())); line.children.push(ISrcSegment::RawText(l.into()));
@@ -133,20 +160,18 @@ where
.collect()) .collect())
} }
fn highlight_nix<L>(lines: &[L]) -> Result<Vec<ISrcLine>, CustomError> fn highlight_tree_sitter<L>(
config: HighlightConfiguration,
highlight_names: &[&str],
lines: &[L],
) -> Result<Vec<ISrcLine>, CustomError>
where where
L: Borrow<str>, L: Borrow<str>,
{ {
let highlight_names = ["comment", "keyword"]; let combined_text = lines.join("");
// Need 1 highlighter per thread // Need 1 highlighter per thread
let mut highlighter = Highlighter::new(); let mut highlighter = Highlighter::new();
let language = tree_sitter_nix::LANGUAGE.into();
let mut config =
HighlightConfiguration::new(language, "nix", tree_sitter_nix::HIGHLIGHTS_QUERY, "", "")
.unwrap();
config.configure(&highlight_names);
let combined_text = lines.join("");
let highlights = highlighter let highlights = highlighter
.highlight(&config, combined_text.as_bytes(), None, |_| None) .highlight(&config, combined_text.as_bytes(), None, |_| None)
@@ -154,6 +179,7 @@ where
let mut highlighted_text: Vec<ISrcLine> = Vec::with_capacity(lines.len()); let mut highlighted_text: Vec<ISrcLine> = Vec::with_capacity(lines.len());
let mut current_line = ISrcLine::new(); let mut current_line = ISrcLine::new();
let mut highlight_stack: Vec<&str> = Vec::new();
for event in highlights { for event in highlights {
match event.unwrap() { match event.unwrap() {
HighlightEvent::Source { start, end } => { HighlightEvent::Source { start, end } => {
@@ -163,8 +189,22 @@ where
current_line current_line
.children .children
.push(ISrcSegment::RawText(first_line.to_owned())); .push(ISrcSegment::RawText(first_line.to_owned()));
current_line.children.extend(
highlight_stack
.iter()
.map(|_name| ISrcSegment::HighlightEnd),
);
highlighted_text.push(current_line); highlighted_text.push(current_line);
current_line = ISrcLine::new(); current_line = ISrcLine::new();
current_line
.children
.extend(
highlight_stack
.iter()
.map(|name| ISrcSegment::HighlightStart {
name: (*name).into(),
}),
);
span = &span[(line_break_index + 1)..]; span = &span[(line_break_index + 1)..];
} }
if !span.is_empty() { if !span.is_empty() {
@@ -174,19 +214,82 @@ where
} }
} }
HighlightEvent::HighlightStart(s) => { HighlightEvent::HighlightStart(s) => {
highlight_stack.push(highlight_names[s.0]);
current_line.children.push(ISrcSegment::HighlightStart { current_line.children.push(ISrcSegment::HighlightStart {
name: highlight_names[s.0].to_owned(), name: highlight_names[s.0].into(),
}); });
} }
HighlightEvent::HighlightEnd => { HighlightEvent::HighlightEnd => {
highlight_stack.pop();
current_line.children.push(ISrcSegment::HighlightEnd); current_line.children.push(ISrcSegment::HighlightEnd);
} }
} }
} }
debug_assert!(highlight_stack.is_empty());
Ok(highlighted_text) Ok(highlighted_text)
} }
fn highlight_bash<L>(lines: &[L]) -> Result<Vec<ISrcLine>, CustomError>
where
L: Borrow<str>,
{
let highlight_names = ["comment", "function", "keyword", "property", "string"];
let language = tree_sitter_bash::LANGUAGE.into();
let mut config =
HighlightConfiguration::new(language, "bash", tree_sitter_bash::HIGHLIGHT_QUERY, "", "")
.unwrap();
config.configure(&highlight_names);
highlight_tree_sitter(config, &highlight_names, lines)
}
fn highlight_nix<L>(lines: &[L]) -> Result<Vec<ISrcLine>, CustomError>
where
L: Borrow<str>,
{
let highlight_names = [
"comment",
"keyword",
"property",
"string",
"string.special.path",
// "string.special.uri",
];
let language = tree_sitter_nix::LANGUAGE.into();
let mut config =
HighlightConfiguration::new(language, "nix", tree_sitter_nix::HIGHLIGHTS_QUERY, "", "")
.unwrap();
config.configure(&highlight_names);
highlight_tree_sitter(config, &highlight_names, lines)
}
fn highlight_python<L>(lines: &[L]) -> Result<Vec<ISrcLine>, CustomError>
where
L: Borrow<str>,
{
let highlight_names = [
"comment",
"function.builtin",
"keyword",
"property",
"string",
"type",
"variable",
];
let language = tree_sitter_python::LANGUAGE.into();
let mut config = HighlightConfiguration::new(
language,
"python",
tree_sitter_python::HIGHLIGHTS_QUERY,
"",
"",
)
.unwrap();
config.configure(&highlight_names);
highlight_tree_sitter(config, &highlight_names, lines)
}
// use tree_sitter::Parser; // use tree_sitter::Parser;
// fn dump_nix<B>(body: B) -> Result<(), CustomError> // fn dump_nix<B>(body: B) -> Result<(), CustomError>
// where // where

View File

@@ -1,7 +1,7 @@
use organic::types::StandardProperties; use organic::types::StandardProperties;
use super::macros::intermediate;
use super::IObject; use super::IObject;
use super::macros::intermediate;
use crate::error::CustomError; use crate::error::CustomError;

View File

@@ -1,4 +1,3 @@
use super::macros::inoop;
use super::macros::intermediate; use super::macros::intermediate;
use super::util::coalesce_whitespace; use super::util::coalesce_whitespace;
use crate::error::CustomError; use crate::error::CustomError;

View File

@@ -1,7 +1,7 @@
use organic::types::StandardProperties; use organic::types::StandardProperties;
use super::macros::intermediate;
use super::IObject; use super::IObject;
use super::macros::intermediate;
use crate::error::CustomError; use crate::error::CustomError;

View File

@@ -1,4 +1,3 @@
#![feature(let_chains)]
use std::process::ExitCode; use std::process::ExitCode;
use clap::Parser; use clap::Parser;
@@ -8,11 +7,14 @@ use self::cli::parameters::Commands;
use self::command::build::build_site; use self::command::build::build_site;
use self::command::init::init_natter_folder; use self::command::init::init_natter_folder;
use self::error::CustomError; use self::error::CustomError;
use self::init_tracing::init_telemetry;
use self::init_tracing::shutdown_telemetry;
mod cli; mod cli;
mod command; mod command;
mod config; mod config;
mod context; mod context;
mod error; mod error;
mod init_tracing;
mod intermediate; mod intermediate;
mod render; mod render;
mod walk_fs; mod walk_fs;
@@ -23,6 +25,7 @@ fn main() -> Result<ExitCode, CustomError> {
} }
async fn main_body() -> Result<ExitCode, CustomError> { async fn main_body() -> Result<ExitCode, CustomError> {
init_telemetry().expect("Telemetry should initialize successfully.");
let args = Cli::parse(); let args = Cli::parse();
match args.command { match args.command {
Commands::Init(args) => { Commands::Init(args) => {
@@ -32,5 +35,6 @@ async fn main_body() -> Result<ExitCode, CustomError> {
build_site(args).await?; build_site(args).await?;
} }
}; };
shutdown_telemetry().expect("Telemetry should shutdown successfully.");
Ok(ExitCode::SUCCESS) Ok(ExitCode::SUCCESS)
} }