Compare commits
52 Commits
79c36476bd
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3929f22f3 | ||
|
|
bad12160ac | ||
|
|
c43679fda9 | ||
|
|
9cc28f6f0d | ||
|
|
d2256b8333 | ||
|
|
fa8753077a | ||
|
|
0420f58d02 | ||
|
|
0250aa106e | ||
|
|
ca1c456571 | ||
|
|
4403980e2e | ||
|
|
dbfbce955d | ||
|
|
2e08d2e59a | ||
|
|
9f14534c10 | ||
|
|
4e34ebc29e | ||
|
|
8d85d5ef79 | ||
|
|
7d73a3c948 | ||
|
|
c501f7cedc | ||
|
|
41927764fc | ||
|
|
75a763569b | ||
|
|
c67eb32774 | ||
|
|
04952895cf | ||
|
|
749f6d7a55 | ||
|
|
c4cf814f8d | ||
|
|
3245e830d2 | ||
|
|
57eb1b81ec | ||
|
|
c601c8697a | ||
|
|
4ea1a46705 | ||
|
|
4cc04bda46 | ||
|
|
7e934cd360 | ||
|
|
e34e2ef75f | ||
|
|
c067ca9cc8 | ||
|
|
b06424cb17 | ||
|
|
ae6f18d19c | ||
|
|
c371b999d5 | ||
|
|
073ac0ac25 | ||
|
|
1c356737c1 | ||
|
|
339bd433f6 | ||
|
|
bb5fa6a487 | ||
|
|
71b6db14d5 | ||
|
|
69fb91db37 | ||
|
|
5fd93fc648 | ||
|
|
033a17e355 | ||
|
|
824f34bd4c | ||
|
|
d93e91c625 | ||
|
|
db21bd8a55 | ||
|
|
69729bd329 | ||
|
|
ddea8fdceb | ||
|
|
71f639e503 | ||
|
|
8714d3b650 | ||
|
|
88064409a6 | ||
|
|
7c17087920 | ||
|
|
c1837addd0 |
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
1016
Cargo.lock
generated
1016
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
16
Cargo.toml
16
Cargo.toml
@@ -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,7 +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-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
21
TODO.org
Normal 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
|
||||||
39
default_environment/stylesheet/language_bash.css
Normal file
39
default_environment/stylesheet/language_bash.css
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
default_environment/stylesheet/language_nix.css
Normal file
39
default_environment/stylesheet/language_nix.css
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
51
default_environment/stylesheet/language_python.css
Normal file
51
default_environment/stylesheet/language_python.css
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,9 @@
|
|||||||
--header-divider-color: #6a687a;
|
--header-divider-color: #6a687a;
|
||||||
|
|
||||||
--stream-divider-color: #6ccff6;
|
--stream-divider-color: #6ccff6;
|
||||||
--stream-post-background-color: #1f1f1f;
|
--stream-post-background-color: #0a0a0a;
|
||||||
|
|
||||||
|
--blog-post-background-color: #0a0a0a;
|
||||||
|
|
||||||
--src-font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo,
|
--src-font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo,
|
||||||
Consolas, "DejaVu Sans Mono", monospace;
|
Consolas, "DejaVu Sans Mono", monospace;
|
||||||
@@ -20,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) {
|
||||||
@@ -29,7 +38,9 @@
|
|||||||
--header-divider-color: #959785;
|
--header-divider-color: #959785;
|
||||||
|
|
||||||
--stream-divider-color: #933009;
|
--stream-divider-color: #933009;
|
||||||
--stream-post-background-color: #e0e0e0;
|
--stream-post-background-color: #f5f5f5;
|
||||||
|
|
||||||
|
--blog-post-background-color: #f5f5f5;
|
||||||
|
|
||||||
--src-block-background-color: #ebebeb;
|
--src-block-background-color: #ebebeb;
|
||||||
--src-block-border-color: #7b7d70;
|
--src-block-border-color: #7b7d70;
|
||||||
@@ -41,9 +52,20 @@
|
|||||||
--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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
color: var(--site-text-color);
|
color: var(--site-text-color);
|
||||||
background-color: var(--site-background-color);
|
background-color: var(--site-background-color);
|
||||||
@@ -64,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 {
|
||||||
font-size: 1.2rem;
|
display: block;
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,17 +145,19 @@ body {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: var(--main-max-width);
|
max-width: var(--main-max-width);
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
line-height: 1.2;
|
line-height: 1.4;
|
||||||
padding-bottom: 8rem;
|
padding-bottom: 8rem;
|
||||||
|
|
||||||
/* A stand-alone blog post (not in a blog stream). */
|
/* A stand-alone blog post (not in a blog stream). */
|
||||||
.blog_post {
|
.blog_post {
|
||||||
padding: 1rem 0 3rem 0;
|
padding: 1rem 0.2rem 0 0.2rem;
|
||||||
|
background: var(--blog-post-background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.blog_stream {
|
.blog_stream {
|
||||||
.stream_divider {
|
.stream_divider {
|
||||||
color: var(--stream-divider-color);
|
color: var(--stream-divider-color);
|
||||||
|
margin: 40px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stream_nav {
|
.stream_nav {
|
||||||
@@ -117,17 +180,29 @@ body {
|
|||||||
/* A blog post in a blog stream (for example, the homepage). */
|
/* A blog post in a blog stream (for example, the homepage). */
|
||||||
.blog_stream_post {
|
.blog_stream_post {
|
||||||
background: var(--stream-post-background-color);
|
background: var(--stream-post-background-color);
|
||||||
padding: 1rem 0.2rem;
|
padding: 0 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog_stream_post {
|
||||||
|
background: var(--stream-post-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog_stream_post:nth-child(1) {
|
||||||
|
padding-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.blog_post_title {
|
.blog_post_title {
|
||||||
font-size: 2.5rem;
|
font-size: 2.9rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
padding-bottom: 1rem;
|
padding-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin: 1rem 0;
|
margin: 1rem 0;
|
||||||
|
|
||||||
|
&.image {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.src_block {
|
.src_block {
|
||||||
@@ -137,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;
|
||||||
@@ -153,19 +229,21 @@ body {
|
|||||||
|
|
||||||
.src_line {
|
.src_line {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.inline_source_block {
|
.inline_source_block {
|
||||||
font-family: var(--src-font-family);
|
font-family: var(--src-font-family);
|
||||||
font-size: 1rem;
|
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-size: 1rem;
|
font-variant-ligatures: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quote_block {
|
.quote_block {
|
||||||
@@ -266,4 +344,15 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Never have media larger than its container */
|
||||||
|
img,
|
||||||
|
picture,
|
||||||
|
video {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||||
{#global_settings.css_files}<link rel="stylesheet" href="{.}">{/global_settings.css_files}
|
{#global_settings.css_files}<link rel="stylesheet" href="{.}">{/global_settings.css_files}
|
||||||
{#global_settings.js_files}<script type="text/javascript" src="{.}"></script>{/global_settings.js_files}
|
{#global_settings.js_files}<script type="text/javascript" src="{.}"></script>{/global_settings.js_files}
|
||||||
{?global_settings.page_title}<title>{global_settings.page_title}</title>{/global_settings.page_title}
|
{?global_settings.page_title}<title>{global_settings.page_title}</title>{/global_settings.page_title}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<p>{#.children}
|
<p class="{?.is_single_image}image{/.is_single_image}">{#.children}
|
||||||
{>object/}
|
{>object/}
|
||||||
{/.children}</p>
|
{/.children}</p>
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
<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>
|
||||||
{#.lines}
|
{#.lines}
|
||||||
<tr>
|
<tr>
|
||||||
<td><code class="src_line">{.}</code></td>
|
<td>{#.children}{@select key=.type}
|
||||||
|
{@eq value="raw_text"}<code class="src_line">{.content}</code>{/eq}
|
||||||
|
{@eq value="highlight_start"}<span class="srchl_{.name}">{/eq}
|
||||||
|
{@eq value="highlight_end"}</span>{/eq}
|
||||||
|
{@none}{!TODO: make this panic!}ERROR: Unrecognized type {.type}.{/none}
|
||||||
|
{/select}{/.children}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/.lines}
|
{/.lines}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
!!!!!!!! timestamp
|
<span class="timestamp">{.source}</span>
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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 =
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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")]
|
||||||
|
|||||||
@@ -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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
@@ -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")]
|
||||||
|
|||||||
@@ -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")]
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,15 @@ 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")]
|
||||||
#[serde(rename = "paragraph")]
|
#[serde(rename = "paragraph")]
|
||||||
pub(crate) struct RenderParagraph {
|
pub(crate) struct RenderParagraph {
|
||||||
children: Vec<RenderObject>,
|
children: Vec<RenderObject>,
|
||||||
|
is_single_image: bool,
|
||||||
post_blank: organic::types::PostBlank,
|
post_blank: organic::types::PostBlank,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,6 +27,7 @@ render!(RenderParagraph, IParagraph, original, render_context, {
|
|||||||
|
|
||||||
Ok(RenderParagraph {
|
Ok(RenderParagraph {
|
||||||
children,
|
children,
|
||||||
|
is_single_image: original.is_single_image(),
|
||||||
post_blank: original.post_blank,
|
post_blank: original.post_blank,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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")]
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
@@ -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")]
|
||||||
|
|||||||
@@ -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")]
|
||||||
|
|||||||
@@ -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")]
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use serde::Serialize;
|
|||||||
use super::render_context::RenderContext;
|
use super::render_context::RenderContext;
|
||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::intermediate::ISrcBlock;
|
use crate::intermediate::ISrcBlock;
|
||||||
|
use crate::intermediate::ISrcSegment;
|
||||||
|
|
||||||
use super::macros::render;
|
use super::macros::render;
|
||||||
|
|
||||||
@@ -10,15 +11,85 @@ use super::macros::render;
|
|||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "src_block")]
|
#[serde(rename = "src_block")]
|
||||||
pub(crate) struct RenderSrcBlock {
|
pub(crate) struct RenderSrcBlock {
|
||||||
lines: Vec<String>,
|
lines: Vec<RenderSrcLine>,
|
||||||
language: Option<String>,
|
language: Option<String>,
|
||||||
post_blank: organic::types::PostBlank,
|
post_blank: organic::types::PostBlank,
|
||||||
}
|
}
|
||||||
|
|
||||||
render!(RenderSrcBlock, ISrcBlock, original, _render_context, {
|
#[derive(Debug, Serialize)]
|
||||||
|
pub(crate) struct RenderSrcLine {
|
||||||
|
children: Vec<RenderSrcSegment>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub(crate) enum RenderSrcSegment {
|
||||||
|
#[serde(rename = "raw_text")]
|
||||||
|
RawText { content: String },
|
||||||
|
|
||||||
|
#[serde(rename = "highlight_start")]
|
||||||
|
HighlightStart { name: String },
|
||||||
|
|
||||||
|
#[serde(rename = "highlight_end")]
|
||||||
|
HighlightEnd,
|
||||||
|
}
|
||||||
|
|
||||||
|
render!(RenderSrcBlock, ISrcBlock, original, render_context, {
|
||||||
|
let lines = original
|
||||||
|
.lines
|
||||||
|
.iter()
|
||||||
|
.map(|original_line| {
|
||||||
|
let children = original_line
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|original_segment| match original_segment {
|
||||||
|
ISrcSegment::RawText(body) => RenderSrcSegment::RawText {
|
||||||
|
content: body.to_owned(),
|
||||||
|
},
|
||||||
|
ISrcSegment::HighlightStart { name } => RenderSrcSegment::HighlightStart {
|
||||||
|
name: css_safe_name(name),
|
||||||
|
},
|
||||||
|
ISrcSegment::HighlightEnd => RenderSrcSegment::HighlightEnd,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
RenderSrcLine { children }
|
||||||
|
})
|
||||||
|
.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: original.lines.clone(),
|
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(".", "_")
|
||||||
|
}
|
||||||
|
|||||||
@@ -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")]
|
||||||
|
|||||||
@@ -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")]
|
||||||
|
|||||||
@@ -1,16 +1,21 @@
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::macros::render;
|
||||||
use super::render_context::RenderContext;
|
use super::render_context::RenderContext;
|
||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::intermediate::ITimestamp;
|
use crate::intermediate::ITimestamp;
|
||||||
|
|
||||||
use super::macros::rnoop;
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "timestamp")]
|
#[serde(rename = "timestamp")]
|
||||||
pub(crate) struct RenderTimestamp {
|
pub(crate) struct RenderTimestamp {
|
||||||
|
source: String,
|
||||||
post_blank: organic::types::PostBlank,
|
post_blank: organic::types::PostBlank,
|
||||||
}
|
}
|
||||||
|
|
||||||
rnoop!(RenderTimestamp, ITimestamp);
|
render!(RenderTimestamp, ITimestamp, original, _render_context, {
|
||||||
|
Ok(RenderTimestamp {
|
||||||
|
source: original.source.clone(),
|
||||||
|
post_blank: original.post_blank,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|||||||
@@ -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
88
src/init_tracing.rs
Normal 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(())
|
||||||
|
}
|
||||||
@@ -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};
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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>,
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -127,6 +128,7 @@ pub(crate) use regular_link::LinkTarget;
|
|||||||
pub(crate) use section::ISection;
|
pub(crate) use section::ISection;
|
||||||
pub(crate) use special_block::ISpecialBlock;
|
pub(crate) use special_block::ISpecialBlock;
|
||||||
pub(crate) use src_block::ISrcBlock;
|
pub(crate) use src_block::ISrcBlock;
|
||||||
|
pub(crate) use src_block::ISrcSegment;
|
||||||
pub(crate) use statistics_cookie::IStatisticsCookie;
|
pub(crate) use statistics_cookie::IStatisticsCookie;
|
||||||
pub(crate) use strike_through::IStrikeThrough;
|
pub(crate) use strike_through::IStrikeThrough;
|
||||||
pub(crate) use subscript::ISubscript;
|
pub(crate) use subscript::ISubscript;
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
@@ -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(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
@@ -31,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>,
|
||||||
@@ -41,4 +42,32 @@ impl IParagraph {
|
|||||||
post_blank,
|
post_blank,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if the paragraph contains nothing but a single image.
|
||||||
|
///
|
||||||
|
/// When this happens, we want to center the image.
|
||||||
|
pub(crate) fn is_single_image(&self) -> bool {
|
||||||
|
let num_images = self
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.filter(|c| match c {
|
||||||
|
IObject::RegularLink(iregular_link) => matches!(
|
||||||
|
&iregular_link.target,
|
||||||
|
super::LinkTarget::Image { src: _, alt: _ }
|
||||||
|
),
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
.count();
|
||||||
|
num_images == 1
|
||||||
|
&& self.children.iter().all(|c| match c {
|
||||||
|
IObject::RegularLink(iregular_link) => matches!(
|
||||||
|
&iregular_link.target,
|
||||||
|
super::LinkTarget::Image { src: _, alt: _ }
|
||||||
|
),
|
||||||
|
IObject::PlainText(iplain_text) => {
|
||||||
|
iplain_text.source.chars().all(|c| c.is_ascii_whitespace())
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -73,7 +73,8 @@ impl LinkTarget {
|
|||||||
) -> Result<LinkTarget, CustomError> {
|
) -> Result<LinkTarget, CustomError> {
|
||||||
// If link type is file and the path ends in .svg then make it an image target
|
// If link type is file and the path ends in .svg then make it an image target
|
||||||
if let LinkType::File = link_type
|
if let LinkType::File = link_type
|
||||||
&& input.to_ascii_lowercase().ends_with(".svg")
|
&& (input.to_ascii_lowercase().ends_with(".svg")
|
||||||
|
|| input.to_ascii_lowercase().ends_with(".png"))
|
||||||
{
|
{
|
||||||
let src = Self::get_image_src(&input)?;
|
let src = Self::get_image_src(&input)?;
|
||||||
let alt = Self::get_image_alt(&input)?;
|
let alt = Self::get_image_alt(&input)?;
|
||||||
@@ -159,7 +160,7 @@ impl LinkTarget {
|
|||||||
render_context.output_file,
|
render_context.output_file,
|
||||||
relative_path_to_file,
|
relative_path_to_file,
|
||||||
)?;
|
)?;
|
||||||
let path_to_file = render_context
|
render_context
|
||||||
.dependency_manager
|
.dependency_manager
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -185,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.
|
||||||
@@ -198,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(_) => {
|
||||||
@@ -208,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()
|
||||||
@@ -235,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")),
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,31 @@
|
|||||||
|
use std::borrow::Borrow;
|
||||||
|
|
||||||
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::HighlightConfiguration;
|
||||||
|
use tree_sitter_highlight::HighlightEvent;
|
||||||
|
use tree_sitter_highlight::Highlighter;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct ISrcBlock {
|
pub(crate) struct ISrcBlock {
|
||||||
pub(crate) lines: Vec<String>,
|
pub(crate) lines: Vec<ISrcLine>,
|
||||||
pub(crate) language: Option<String>,
|
pub(crate) language: Option<String>,
|
||||||
pub(crate) post_blank: organic::types::PostBlank,
|
pub(crate) post_blank: organic::types::PostBlank,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) struct ISrcLine {
|
||||||
|
pub(crate) children: Vec<ISrcSegment>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) enum ISrcSegment {
|
||||||
|
RawText(String),
|
||||||
|
HighlightStart { name: String },
|
||||||
|
HighlightEnd,
|
||||||
|
}
|
||||||
|
|
||||||
intermediate!(
|
intermediate!(
|
||||||
ISrcBlock,
|
ISrcBlock,
|
||||||
&'orig organic::types::SrcBlock<'parse>,
|
&'orig organic::types::SrcBlock<'parse>,
|
||||||
@@ -59,14 +76,66 @@ intermediate!(
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let language = original.language.map(str::to_owned);
|
let language = original.language.map(str::to_owned);
|
||||||
|
|
||||||
|
match language.as_deref() {
|
||||||
|
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);
|
||||||
|
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 @ "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)?;
|
||||||
Ok(ISrcBlock {
|
Ok(ISrcBlock {
|
||||||
lines,
|
lines: highlighted,
|
||||||
language,
|
language,
|
||||||
post_blank: original.get_post_blank(),
|
post_blank: original.get_post_blank(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
impl ISrcLine {
|
||||||
|
pub(crate) fn new() -> ISrcLine {
|
||||||
|
ISrcLine {
|
||||||
|
children: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn ascii_whitespace_value(c: char) -> usize {
|
fn ascii_whitespace_value(c: char) -> usize {
|
||||||
match c {
|
match c {
|
||||||
' ' => 1,
|
' ' => 1,
|
||||||
@@ -76,3 +145,161 @@ fn ascii_whitespace_value(c: char) -> usize {
|
|||||||
_ => unreachable!("Only ascii whitespace can reach this code."),
|
_ => unreachable!("Only ascii whitespace can reach this code."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn highlight_plain<L>(lines: &[L]) -> Result<Vec<ISrcLine>, CustomError>
|
||||||
|
where
|
||||||
|
std::string::String: for<'a> From<&'a L>,
|
||||||
|
{
|
||||||
|
Ok(lines
|
||||||
|
.iter()
|
||||||
|
.map(|l| {
|
||||||
|
let mut line = ISrcLine::new();
|
||||||
|
line.children.push(ISrcSegment::RawText(l.into()));
|
||||||
|
line
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn highlight_tree_sitter<L>(
|
||||||
|
config: HighlightConfiguration,
|
||||||
|
highlight_names: &[&str],
|
||||||
|
lines: &[L],
|
||||||
|
) -> Result<Vec<ISrcLine>, CustomError>
|
||||||
|
where
|
||||||
|
L: Borrow<str>,
|
||||||
|
{
|
||||||
|
let combined_text = lines.join("");
|
||||||
|
|
||||||
|
// Need 1 highlighter per thread
|
||||||
|
let mut highlighter = Highlighter::new();
|
||||||
|
|
||||||
|
let highlights = highlighter
|
||||||
|
.highlight(&config, combined_text.as_bytes(), None, |_| None)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut highlighted_text: Vec<ISrcLine> = Vec::with_capacity(lines.len());
|
||||||
|
let mut current_line = ISrcLine::new();
|
||||||
|
let mut highlight_stack: Vec<&str> = Vec::new();
|
||||||
|
for event in highlights {
|
||||||
|
match event.unwrap() {
|
||||||
|
HighlightEvent::Source { start, end } => {
|
||||||
|
let mut span = &combined_text[start..end];
|
||||||
|
while let Some(line_break_index) = span.find('\n') {
|
||||||
|
let first_line = &span[..(line_break_index + 1)];
|
||||||
|
current_line
|
||||||
|
.children
|
||||||
|
.push(ISrcSegment::RawText(first_line.to_owned()));
|
||||||
|
current_line.children.extend(
|
||||||
|
highlight_stack
|
||||||
|
.iter()
|
||||||
|
.map(|_name| ISrcSegment::HighlightEnd),
|
||||||
|
);
|
||||||
|
highlighted_text.push(current_line);
|
||||||
|
current_line = ISrcLine::new();
|
||||||
|
current_line
|
||||||
|
.children
|
||||||
|
.extend(
|
||||||
|
highlight_stack
|
||||||
|
.iter()
|
||||||
|
.map(|name| ISrcSegment::HighlightStart {
|
||||||
|
name: (*name).into(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
span = &span[(line_break_index + 1)..];
|
||||||
|
}
|
||||||
|
if !span.is_empty() {
|
||||||
|
current_line
|
||||||
|
.children
|
||||||
|
.push(ISrcSegment::RawText(span.to_owned()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HighlightEvent::HighlightStart(s) => {
|
||||||
|
highlight_stack.push(highlight_names[s.0]);
|
||||||
|
current_line.children.push(ISrcSegment::HighlightStart {
|
||||||
|
name: highlight_names[s.0].into(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
HighlightEvent::HighlightEnd => {
|
||||||
|
highlight_stack.pop();
|
||||||
|
current_line.children.push(ISrcSegment::HighlightEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_assert!(highlight_stack.is_empty());
|
||||||
|
|
||||||
|
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;
|
||||||
|
// fn dump_nix<B>(body: B) -> Result<(), CustomError>
|
||||||
|
// where
|
||||||
|
// B: AsRef<str>,
|
||||||
|
// {
|
||||||
|
// let mut parser = Parser::new();
|
||||||
|
// parser
|
||||||
|
// .set_language(&tree_sitter_nix::LANGUAGE.into())
|
||||||
|
// .expect("Error loading Nix grammar");
|
||||||
|
// let mut tree = parser.parse(body.as_ref(), None).unwrap();
|
||||||
|
// println!("{}", tree.root_node());
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,23 @@
|
|||||||
use super::macros::inoop;
|
use super::macros::intermediate;
|
||||||
|
use super::util::coalesce_whitespace;
|
||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use organic::types::StandardProperties;
|
use organic::types::StandardProperties;
|
||||||
|
|
||||||
inoop!(ITimestamp, Timestamp);
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) struct ITimestamp {
|
||||||
|
pub(crate) source: String,
|
||||||
|
pub(crate) post_blank: organic::types::PostBlank,
|
||||||
|
}
|
||||||
|
|
||||||
|
intermediate!(
|
||||||
|
ITimestamp,
|
||||||
|
&'orig organic::types::Timestamp<'parse>,
|
||||||
|
original,
|
||||||
|
_intermediate_context,
|
||||||
|
{
|
||||||
|
Ok(ITimestamp {
|
||||||
|
source: coalesce_whitespace(original.source).into_owned(),
|
||||||
|
post_blank: original.get_post_blank(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user