Concentrate configuration content parsing in syntax::serial
Fixes some pages not having parsed input by making it much harder to construct a config with unparsed text, which is something you basically never want. The parsing now happens much earlier and consumers don't need to remember to parse the configuration anymore. Fixes a possible stack overflow due to parsing and configuration depending on each other. This commit also has dependencies updates and minor justfile tweaks.
This commit is contained in:
parent
a24a877ad7
commit
7300a29b67
12 changed files with 142 additions and 132 deletions
12
.justfile
12
.justfile
|
|
@ -22,6 +22,14 @@ run-watch:
|
||||||
|
|
||||||
alias w := run-watch
|
alias w := run-watch
|
||||||
|
|
||||||
|
# Apply basic assessments, build and run on changes
|
||||||
|
[group: 'develop']
|
||||||
|
assess-run-watch extra='false':
|
||||||
|
{{ watch_cmd }} {{ just_cmd }} lint test \
|
||||||
|
{{ if extra == "cover" { "cover-assess" } else { "" } }} run
|
||||||
|
|
||||||
|
alias aw := assess-run-watch
|
||||||
|
|
||||||
# Format all files
|
# Format all files
|
||||||
[group: 'develop']
|
[group: 'develop']
|
||||||
format:
|
format:
|
||||||
|
|
@ -207,8 +215,8 @@ alias fb := full-build
|
||||||
|
|
||||||
## META
|
## META
|
||||||
|
|
||||||
[default]
|
[default, private]
|
||||||
_default:
|
default:
|
||||||
@just --list --unsorted --justfile {{justfile()}}
|
@just --list --unsorted --justfile {{justfile()}}
|
||||||
|
|
||||||
export RUSTFLAGS := "-Dwarnings"
|
export RUSTFLAGS := "-Dwarnings"
|
||||||
|
|
|
||||||
24
Cargo.lock
generated
24
Cargo.lock
generated
|
|
@ -73,9 +73,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum-core"
|
name = "axum-core"
|
||||||
version = "0.5.5"
|
version = "0.5.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22"
|
checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
|
@ -529,9 +529,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.16"
|
version = "1.0.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7ee5b5339afb4c41626dde77b7a611bd4f2c202b897852b4bcf5d03eddc61010"
|
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
|
|
@ -740,9 +740,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.103"
|
version = "1.0.104"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
@ -872,9 +872,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.21"
|
version = "1.0.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "62049b2877bf12821e8f9ad256ee38fdc31db7387ec2d3b3f403024de2034aea"
|
checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "same-file"
|
name = "same-file"
|
||||||
|
|
@ -917,9 +917,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.147"
|
version = "1.0.148"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6af14725505314343e673e9ecb7cd7e8a36aa9791eb936235a3567cc31447ae4"
|
checksum = "3084b546a1dd6289475996f182a22aba973866ea8e8b02c51d9f46b1336a22da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
|
@ -1571,6 +1571,6 @@ checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zmij"
|
name = "zmij"
|
||||||
version = "0.1.9"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d0095ecd462946aa3927d9297b63ef82fb9a5316d7a37d134eeb36e58228615a"
|
checksum = "e6d6085d62852e35540689d1f97ad663e3971fc19cf5eceab364d62c646ea167"
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@ keyword-idents = "warn"
|
||||||
[lints.clippy]
|
[lints.clippy]
|
||||||
# levels: allow, warn, deny, forbid
|
# levels: allow, warn, deny, forbid
|
||||||
|
|
||||||
|
manual_non_exhaustive = "allow"
|
||||||
|
|
||||||
# pedantic
|
# pedantic
|
||||||
assigning_clones = "warn"
|
assigning_clones = "warn"
|
||||||
borrow_as_ptr = "warn"
|
borrow_as_ptr = "warn"
|
||||||
|
|
@ -63,7 +65,6 @@ explicit_iter_loop = "warn"
|
||||||
filter_map_next = "warn"
|
filter_map_next = "warn"
|
||||||
flat_map_option = "warn"
|
flat_map_option = "warn"
|
||||||
float_cmp = "warn"
|
float_cmp = "warn"
|
||||||
fn_params_excessive_bools = "warn"
|
|
||||||
format_push_string = "warn"
|
format_push_string = "warn"
|
||||||
from_iter_instead_of_collect = "warn"
|
from_iter_instead_of_collect = "warn"
|
||||||
if_not_else = "warn"
|
if_not_else = "warn"
|
||||||
|
|
@ -126,7 +127,6 @@ semicolon_if_nothing_returned = "warn"
|
||||||
should_panic_without_expect = "warn"
|
should_panic_without_expect = "warn"
|
||||||
similar_names = "warn"
|
similar_names = "warn"
|
||||||
str_split_at_newline = "warn"
|
str_split_at_newline = "warn"
|
||||||
struct_excessive_bools = "warn"
|
|
||||||
struct_field_names = "warn"
|
struct_field_names = "warn"
|
||||||
trivially_copy_pass_by_ref = "warn"
|
trivially_copy_pass_by_ref = "warn"
|
||||||
unchecked_time_subtraction = "warn"
|
unchecked_time_subtraction = "warn"
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ mod tests {
|
||||||
let graph = Graph {
|
let graph = Graph {
|
||||||
meta: Meta {
|
meta: Meta {
|
||||||
config: config
|
config: config
|
||||||
.map(|c| c.to_owned())
|
.map(std::borrow::ToOwned::to_owned)
|
||||||
.unwrap_or(default_graph.meta.config),
|
.unwrap_or(default_graph.meta.config),
|
||||||
..default_graph.meta
|
..default_graph.meta
|
||||||
},
|
},
|
||||||
|
|
@ -121,10 +121,8 @@ mod tests {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn no_about_page() {
|
async fn no_about_page() {
|
||||||
let config = Config {
|
let mut config = Config::new();
|
||||||
about: false,
|
config.about = false;
|
||||||
..populate_graph().meta.config
|
|
||||||
};
|
|
||||||
|
|
||||||
let response = request("/about", Some(&config)).await;
|
let response = request("/about", Some(&config)).await;
|
||||||
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
||||||
|
|
@ -132,10 +130,8 @@ mod tests {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn no_tree_page() {
|
async fn no_tree_page() {
|
||||||
let config = Config {
|
let mut config = Config::new();
|
||||||
tree: false,
|
config.tree = false;
|
||||||
..populate_graph().meta.config
|
|
||||||
};
|
|
||||||
|
|
||||||
let response = request("/tree", Some(&config)).await;
|
let response = request("/tree", Some(&config)).await;
|
||||||
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
||||||
|
|
@ -143,10 +139,8 @@ mod tests {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn no_toml_raw_graph() {
|
async fn no_toml_raw_graph() {
|
||||||
let config = Config {
|
let mut config = Config::new();
|
||||||
raw_toml: false,
|
config.raw_toml = false;
|
||||||
..populate_graph().meta.config
|
|
||||||
};
|
|
||||||
|
|
||||||
let response = request("/graph/toml", Some(&config)).await;
|
let response = request("/graph/toml", Some(&config)).await;
|
||||||
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
||||||
|
|
@ -154,10 +148,8 @@ mod tests {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn no_json_raw_graph() {
|
async fn no_json_raw_graph() {
|
||||||
let config = Config {
|
let mut config = Config::new();
|
||||||
raw_json: false,
|
config.raw_json = false;
|
||||||
..populate_graph().meta.config
|
|
||||||
};
|
|
||||||
|
|
||||||
let response = request("/graph/json", Some(&config)).await;
|
let response = request("/graph/json", Some(&config)).await;
|
||||||
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
||||||
|
|
@ -165,10 +157,8 @@ mod tests {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn no_raw_graph() {
|
async fn no_raw_graph() {
|
||||||
let config = Config {
|
let mut config = Config::new();
|
||||||
raw: false,
|
config.raw = false;
|
||||||
..populate_graph().meta.config
|
|
||||||
};
|
|
||||||
|
|
||||||
let toml_response = request("/graph/toml", Some(&config)).await;
|
let toml_response = request("/graph/toml", Some(&config)).await;
|
||||||
assert_eq!(toml_response.status(), StatusCode::NOT_FOUND);
|
assert_eq!(toml_response.status(), StatusCode::NOT_FOUND);
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,9 @@ pub async fn node(Path(id): Path<String>) -> Response<Body> {
|
||||||
|
|
||||||
let mut context = tera::Context::new();
|
let mut context = tera::Context::new();
|
||||||
context.insert("node", &node);
|
context.insert("node", &node);
|
||||||
context.insert("text", &content::parse(&node.text));
|
context.insert("text", &content::parse(&node.text, &graph.meta.config));
|
||||||
context.insert("incoming", &graph.incoming.get(&id));
|
context.insert("incoming", &graph.incoming.get(&id));
|
||||||
context.insert("config", &graph.meta.config.parse_text());
|
context.insert("config", &graph.meta.config);
|
||||||
|
|
||||||
let not_found = node == empty_node;
|
let not_found = node == empty_node;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ pub async fn page(template: &str) -> Response<Body> {
|
||||||
|
|
||||||
context.insert("nodes", &nodes);
|
context.insert("nodes", &nodes);
|
||||||
context.insert("root_node", &root_node);
|
context.insert("root_node", &root_node);
|
||||||
context.insert("config", &graph.meta.config.parse_text());
|
context.insert("config", &graph.meta.config);
|
||||||
|
|
||||||
handlers::template::by_filename(template, &context, 500, None, false)
|
handlers::template::by_filename(template, &context, 500, None, false)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -145,9 +145,12 @@ mod tests {
|
||||||
let node = crate::types::Node::new(Some(payload.to_string()));
|
let node = crate::types::Node::new(Some(payload.to_string()));
|
||||||
let graph = crate::syntax::serial::populate_graph();
|
let graph = crate::syntax::serial::populate_graph();
|
||||||
context.insert("node", &node);
|
context.insert("node", &node);
|
||||||
context.insert("text", &crate::syntax::content::parse(&node.text));
|
context.insert(
|
||||||
|
"text",
|
||||||
|
&crate::syntax::content::parse(&node.text, &graph.meta.config),
|
||||||
|
);
|
||||||
context.insert("incoming", &graph.incoming.get(&node.id));
|
context.insert("incoming", &graph.incoming.get(&node.id));
|
||||||
context.insert("config", &graph.meta.config.parse_text());
|
context.insert("config", &graph.meta.config);
|
||||||
let (body, status) = render("node.html", &context, None);
|
let (body, status) = render("node.html", &context, None);
|
||||||
assert_eq!(status, 200);
|
assert_eq!(status, 200);
|
||||||
assert!(body.matches(payload).count() == 1);
|
assert!(body.matches(payload).count() == 1);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
use parser::{token::Token, lexeme::Lexeme};
|
use parser::{token::Token, lexeme::Lexeme};
|
||||||
|
|
||||||
|
use crate::types::Config;
|
||||||
|
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
|
||||||
pub trait Parseable {
|
pub trait Parseable {
|
||||||
|
|
@ -12,6 +14,9 @@ type Probe = fn(&Lexeme) -> bool;
|
||||||
type Lexer = fn(&Lexeme) -> Token;
|
type Lexer = fn(&Lexeme) -> Token;
|
||||||
type LexMap<'lm> = &'lm [(Probe, Lexer)];
|
type LexMap<'lm> = &'lm [(Probe, Lexer)];
|
||||||
|
|
||||||
pub fn parse(text: &str) -> String {
|
pub fn parse(text: &str, config: &Config) -> String {
|
||||||
parser::read(text)
|
if text.is_empty() {
|
||||||
|
return String::new();
|
||||||
|
}
|
||||||
|
parser::read(text, config)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::collections::{HashMap};
|
use std::collections::{HashMap};
|
||||||
|
|
||||||
use crate::{syntax::serial::populate_graph, types::Config};
|
use crate::types::Config;
|
||||||
use super::{Parseable as _, Token, LexMap};
|
use super::{Parseable as _, Token, LexMap};
|
||||||
use token::{
|
use token::{
|
||||||
anchor::Anchor, linebreak::LineBreak, paragraph::Paragraph, header::Header,
|
anchor::Anchor, linebreak::LineBreak, paragraph::Paragraph, header::Header,
|
||||||
|
|
@ -19,10 +19,9 @@ const LEXMAP: LexMap = &[
|
||||||
(Literal::probe, |word| Token::Literal(Literal::lex(word))),
|
(Literal::probe, |word| Token::Literal(Literal::lex(word))),
|
||||||
];
|
];
|
||||||
|
|
||||||
fn lex(text: &str, map: LexMap) -> Vec<Token> {
|
fn lex(text: &str, map: LexMap, config: &Config) -> Vec<Token> {
|
||||||
let mut tokens: Vec<Token> = Vec::new();
|
let mut tokens: Vec<Token> = Vec::new();
|
||||||
let mut state = State::new();
|
let mut state = State::new();
|
||||||
let config: Config = populate_graph().meta.config;
|
|
||||||
|
|
||||||
let segments = segment::segment(text);
|
let segments = segment::segment(text);
|
||||||
let lexemes = Lexeme::collect(&segments);
|
let lexemes = Lexeme::collect(&segments);
|
||||||
|
|
@ -38,7 +37,7 @@ fn lex(text: &str, map: LexMap) -> Vec<Token> {
|
||||||
} else if Header::probe(lexeme) {
|
} else if Header::probe(lexeme) {
|
||||||
let mut header = Header::lex(lexeme);
|
let mut header = Header::lex(lexeme);
|
||||||
header.dom_id = Some(Header::make_id(
|
header.dom_id = Some(Header::make_id(
|
||||||
&config,
|
config,
|
||||||
iterator.peek().map_or(&Lexeme::new("", ""), |l| l),
|
iterator.peek().map_or(&Lexeme::new("", ""), |l| l),
|
||||||
&mut state.dom_ids,
|
&mut state.dom_ids,
|
||||||
));
|
));
|
||||||
|
|
@ -247,19 +246,23 @@ fn parse(tokens: &[Token]) -> String {
|
||||||
tokens.iter().map(Token::render).collect::<String>()
|
tokens.iter().map(Token::render).collect::<String>()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn read(text: &str) -> String {
|
pub(super) fn read(text: &str, config: &Config) -> String {
|
||||||
parse(&lex(text, LEXMAP))
|
parse(&lex(text, LEXMAP, config))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::syntax::content::parser::token::header::Level;
|
use crate::{types::Graph, syntax::content::parser::token::header::Level};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
fn read_noconfig(input: &str) -> String {
|
||||||
|
read(input, &Graph::new(None).meta.config)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_render_is_empty() {
|
fn empty_render_is_empty() {
|
||||||
assert_eq!(read(""), "");
|
assert_eq!(read_noconfig(""), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -267,18 +270,21 @@ mod tests {
|
||||||
let en = "`this |test|` tries ## to |brea|k|: things";
|
let en = "`this |test|` tries ## to |brea|k|: things";
|
||||||
let html = r#"<p><code>this |test|</code> tries ## to <a href="/node/k">brea</a>: things</p>"#;
|
let html = r#"<p><code>this |test|</code> tries ## to <a href="/node/k">brea</a>: things</p>"#;
|
||||||
|
|
||||||
assert_eq!(read(en), html);
|
assert_eq!(read_noconfig(en), html);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn force_flanking() {
|
fn force_flanking() {
|
||||||
assert_eq!(read("|Node||"), r#"<p><a href="/node/Node">Node</a></p>"#);
|
assert_eq!(
|
||||||
|
read_noconfig("|Node||"),
|
||||||
|
r#"<p><a href="/node/Node">Node</a></p>"#
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn flanking_with_trailing_pipe() {
|
fn flanking_with_trailing_pipe() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
read("|Node|Destination|"),
|
read_noconfig("|Node|Destination|"),
|
||||||
r#"<p><a href="/node/Destination">Node</a></p>"#
|
r#"<p><a href="/node/Destination">Node</a></p>"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -286,7 +292,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn nonleading_second_pipe() {
|
fn nonleading_second_pipe() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
read("Go to Node|Destination|, here"),
|
read_noconfig("Go to Node|Destination|, here"),
|
||||||
r#"<p>Go to <a href="/node/Destination">Node</a>, here</p>"#,
|
r#"<p>Go to <a href="/node/Destination">Node</a>, here</p>"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -294,7 +300,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn clear_anchor_buffer() {
|
fn clear_anchor_buffer() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
read("|SomeAnchor|\n|SomeOtherAnchor|"),
|
read_noconfig("|SomeAnchor|\n|SomeOtherAnchor|"),
|
||||||
concat!(
|
concat!(
|
||||||
r#"<p><a href="/node/SomeAnchor">SomeAnchor</a></p>"#,
|
r#"<p><a href="/node/SomeAnchor">SomeAnchor</a></p>"#,
|
||||||
"\n",
|
"\n",
|
||||||
|
|
|
||||||
|
|
@ -182,10 +182,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ascii_ids_set() {
|
fn ascii_ids_set() {
|
||||||
let config = Config {
|
let mut config = Config::new();
|
||||||
ascii_dom_ids: true,
|
config.ascii_dom_ids = true;
|
||||||
..Config::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let id = Header::make_id(
|
let id = Header::make_id(
|
||||||
&config,
|
&config,
|
||||||
|
|
@ -197,10 +195,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ascii_ids_unset() {
|
fn ascii_ids_unset() {
|
||||||
let config = Config {
|
let mut config = Config::new();
|
||||||
ascii_dom_ids: false,
|
config.ascii_dom_ids = false;
|
||||||
..Config::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let id = Header::make_id(
|
let id = Header::make_id(
|
||||||
&config,
|
&config,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
syntax::command::Arguments,
|
syntax::command::Arguments,
|
||||||
types::{Edge, Graph, Node},
|
types::{Edge, Graph, Meta, Node},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn populate_graph() -> Graph {
|
pub fn populate_graph() -> Graph {
|
||||||
|
|
@ -11,29 +11,26 @@ pub fn populate_graph() -> Graph {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(e) => format!("Error: {e}"),
|
Err(e) => format!("Error: {e}"),
|
||||||
};
|
};
|
||||||
let graph = deserialize_graph(&Format::TOML, &toml_source);
|
|
||||||
|
|
||||||
|
let graph = deserialize_graph(&Format::TOML, &toml_source);
|
||||||
|
modulate_graph(&graph)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn modulate_graph(graph: &Graph) -> Graph {
|
||||||
let nodes = modulate_nodes(&graph.nodes);
|
let nodes = modulate_nodes(&graph.nodes);
|
||||||
|
|
||||||
Graph {
|
Graph {
|
||||||
nodes: nodes.clone(),
|
|
||||||
incoming: make_incoming(&nodes),
|
incoming: make_incoming(&nodes),
|
||||||
lowercase_keymap: map_lowercase_keys(&nodes),
|
lowercase_keymap: map_lowercase_keys(&nodes),
|
||||||
..graph
|
nodes,
|
||||||
|
meta: Meta {
|
||||||
|
config: graph.meta.config.clone().parse_text(),
|
||||||
|
..graph.meta.clone()
|
||||||
|
},
|
||||||
|
..graph.to_owned()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_lowercase_keys(
|
|
||||||
source_map: &HashMap<String, Node>,
|
|
||||||
) -> HashMap<String, String> {
|
|
||||||
let mut out_map: HashMap<String, String> = HashMap::new();
|
|
||||||
let keys = source_map.keys();
|
|
||||||
for key in keys {
|
|
||||||
out_map.insert(key.clone().to_lowercase(), key.clone());
|
|
||||||
}
|
|
||||||
out_map
|
|
||||||
}
|
|
||||||
|
|
||||||
fn modulate_nodes(old_nodes: &HashMap<String, Node>) -> HashMap<String, Node> {
|
fn modulate_nodes(old_nodes: &HashMap<String, Node>) -> HashMap<String, Node> {
|
||||||
let mut nodes: HashMap<String, Node> = HashMap::new();
|
let mut nodes: HashMap<String, Node> = HashMap::new();
|
||||||
|
|
||||||
|
|
@ -89,23 +86,6 @@ fn modulate_nodes(old_nodes: &HashMap<String, Node>) -> HashMap<String, Node> {
|
||||||
nodes
|
nodes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct a HashMap with incoming connections (reversed edges)
|
|
||||||
fn make_incoming(nodes: &HashMap<String, Node>) -> HashMap<String, Vec<Edge>> {
|
|
||||||
let mut incoming: HashMap<String, Vec<Edge>> = HashMap::new();
|
|
||||||
|
|
||||||
for node in nodes.clone().into_values() {
|
|
||||||
let empty_vec: Vec<Edge> = vec![];
|
|
||||||
for edge in &node.connections.clone().unwrap_or_default() {
|
|
||||||
let mut edges =
|
|
||||||
incoming.get(&edge.to.clone()).unwrap_or(&empty_vec).clone();
|
|
||||||
edges.extend_from_slice(std::slice::from_ref(edge));
|
|
||||||
incoming.insert(edge.to.clone(), edges.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
incoming
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Format {
|
pub enum Format {
|
||||||
TOML,
|
TOML,
|
||||||
JSON,
|
JSON,
|
||||||
|
|
@ -137,6 +117,34 @@ pub fn deserialize_graph(in_format: &Format, serial: &str) -> Graph {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Construct a HashMap with incoming connections (reversed edges)
|
||||||
|
fn make_incoming(nodes: &HashMap<String, Node>) -> HashMap<String, Vec<Edge>> {
|
||||||
|
let mut incoming: HashMap<String, Vec<Edge>> = HashMap::new();
|
||||||
|
|
||||||
|
for node in nodes.clone().into_values() {
|
||||||
|
let empty_vec: Vec<Edge> = vec![];
|
||||||
|
for edge in &node.connections.clone().unwrap_or_default() {
|
||||||
|
let mut edges =
|
||||||
|
incoming.get(&edge.to.clone()).unwrap_or(&empty_vec).clone();
|
||||||
|
edges.extend_from_slice(std::slice::from_ref(edge));
|
||||||
|
incoming.insert(edge.to.clone(), edges.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
incoming
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map_lowercase_keys(
|
||||||
|
source_map: &HashMap<String, Node>,
|
||||||
|
) -> HashMap<String, String> {
|
||||||
|
let mut out_map: HashMap<String, String> = HashMap::new();
|
||||||
|
let keys = source_map.keys();
|
||||||
|
for key in keys {
|
||||||
|
out_map.insert(key.clone().to_lowercase(), key.clone());
|
||||||
|
}
|
||||||
|
out_map
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
||||||
66
src/types.rs
66
src/types.rs
|
|
@ -57,9 +57,10 @@ fn mkversion() -> (u8, u8, u8) {
|
||||||
(0, 0, 0)
|
(0, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(clippy::struct_excessive_bools)]
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Default, PartialEq, Eq, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Default, PartialEq, Eq, Debug)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
#[serde(default)]
|
||||||
|
_private: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub site_title: String,
|
pub site_title: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|
@ -119,27 +120,7 @@ impl Graph {
|
||||||
incoming: HashMap::new(),
|
incoming: HashMap::new(),
|
||||||
lowercase_keymap: HashMap::new(),
|
lowercase_keymap: HashMap::new(),
|
||||||
meta: Meta {
|
meta: Meta {
|
||||||
config: Config {
|
config: Config::new(),
|
||||||
site_title: String::new(),
|
|
||||||
site_description: String::new(),
|
|
||||||
footer: true,
|
|
||||||
footer_credits: true,
|
|
||||||
footer_date: true,
|
|
||||||
footer_text: String::new(),
|
|
||||||
about: true,
|
|
||||||
about_text: String::new(),
|
|
||||||
tree: true,
|
|
||||||
raw: true,
|
|
||||||
raw_toml: true,
|
|
||||||
raw_json: true,
|
|
||||||
index_search: true,
|
|
||||||
index_node_list: true,
|
|
||||||
index_node_count: 8,
|
|
||||||
index_root_node: true,
|
|
||||||
tree_node_text: false,
|
|
||||||
ascii_dom_ids: false,
|
|
||||||
content_language: String::new(),
|
|
||||||
},
|
|
||||||
version: (0, 1, 0),
|
version: (0, 1, 0),
|
||||||
messages: message.map_or(vec![], |m| vec![m.to_string()]),
|
messages: message.map_or(vec![], |m| vec![m.to_string()]),
|
||||||
},
|
},
|
||||||
|
|
@ -177,23 +158,36 @@ impl Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
|
pub fn new() -> Config {
|
||||||
|
Config {
|
||||||
|
_private: true,
|
||||||
|
site_title: String::new(),
|
||||||
|
site_description: String::new(),
|
||||||
|
footer: true,
|
||||||
|
footer_credits: true,
|
||||||
|
footer_date: true,
|
||||||
|
footer_text: String::new(),
|
||||||
|
about: true,
|
||||||
|
about_text: String::new(),
|
||||||
|
tree: true,
|
||||||
|
raw: true,
|
||||||
|
raw_toml: true,
|
||||||
|
raw_json: true,
|
||||||
|
index_search: true,
|
||||||
|
index_node_list: true,
|
||||||
|
index_node_count: 8,
|
||||||
|
index_root_node: true,
|
||||||
|
tree_node_text: false,
|
||||||
|
ascii_dom_ids: false,
|
||||||
|
content_language: String::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn parse_text(self) -> Config {
|
pub fn parse_text(self) -> Config {
|
||||||
let footer_text = if self.footer_text.is_empty() {
|
|
||||||
self.footer_text
|
|
||||||
} else {
|
|
||||||
content::parse(&self.footer_text)
|
|
||||||
};
|
|
||||||
|
|
||||||
let about_text = if self.about_text.is_empty() {
|
|
||||||
self.about_text
|
|
||||||
} else {
|
|
||||||
content::parse(&self.about_text)
|
|
||||||
};
|
|
||||||
|
|
||||||
Config {
|
Config {
|
||||||
footer_text,
|
footer_text: content::parse(&self.footer_text, &self),
|
||||||
about_text,
|
about_text: content::parse(&self.about_text, &self),
|
||||||
..self
|
..self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue