Embed a 'welcome' graph to fallback on
This commit is contained in:
parent
6180bb371f
commit
dfddbba4ef
5 changed files with 123 additions and 35 deletions
18
Cargo.lock
generated
18
Cargo.lock
generated
|
|
@ -259,7 +259,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "en"
|
name = "en"
|
||||||
version = "0.3.1-alpha"
|
version = "0.4.0-alpha"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
@ -1209,9 +1209,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ureq"
|
name = "ureq"
|
||||||
version = "3.2.0"
|
version = "3.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fdc97a28575b85cfedf2a7e7d3cc64b3e11bd8ac766666318003abbacc7a21fc"
|
checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"flate2",
|
"flate2",
|
||||||
|
|
@ -1220,15 +1220,15 @@ dependencies = [
|
||||||
"rustls",
|
"rustls",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"ureq-proto",
|
"ureq-proto",
|
||||||
"utf-8",
|
"utf8-zero",
|
||||||
"webpki-roots",
|
"webpki-roots",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ureq-proto"
|
name = "ureq-proto"
|
||||||
version = "0.5.3"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f"
|
checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"http",
|
"http",
|
||||||
|
|
@ -1237,10 +1237,10 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf-8"
|
name = "utf8-zero"
|
||||||
version = "0.7.6"
|
version = "0.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "en"
|
name = "en"
|
||||||
version = "0.3.1-alpha"
|
version = "0.4.0-alpha"
|
||||||
description = "A non-linear writing instrument."
|
description = "A non-linear writing instrument."
|
||||||
license = "AGPL-3.0-only"
|
license = "AGPL-3.0-only"
|
||||||
|
|
||||||
|
|
|
||||||
103
src/graph.rs
103
src/graph.rs
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{collections::HashMap, path::PathBuf};
|
use std::{collections::HashMap, io, path::PathBuf};
|
||||||
|
|
||||||
pub use edge::Edge;
|
pub use edge::Edge;
|
||||||
pub use meta::{Config, Meta};
|
pub use meta::{Config, Meta};
|
||||||
|
|
@ -43,6 +43,18 @@ pub struct Stats {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Graph {
|
impl Graph {
|
||||||
|
fn welcome() -> Graph {
|
||||||
|
let toml = include_str!("../static/welcome.toml");
|
||||||
|
let mut welcome_graph = match Graph::from_serial(toml, &Format::TOML) {
|
||||||
|
Ok(graph) => graph,
|
||||||
|
Err(error) => {
|
||||||
|
panic!("Welcome graph parsing must be infallible: {error:?}")
|
||||||
|
},
|
||||||
|
};
|
||||||
|
welcome_graph.modulate();
|
||||||
|
welcome_graph
|
||||||
|
}
|
||||||
|
|
||||||
pub fn with_message(message: &str) -> Graph {
|
pub fn with_message(message: &str) -> Graph {
|
||||||
let graph = Graph::default();
|
let graph = Graph::default();
|
||||||
let mut messages = graph.meta.messages;
|
let mut messages = graph.meta.messages;
|
||||||
|
|
@ -69,12 +81,23 @@ impl Graph {
|
||||||
/// Loads a Graph TOML file from CLI arguments or their defaults and
|
/// Loads a Graph TOML file from CLI arguments or their defaults and
|
||||||
/// returns a modulated Graph.
|
/// returns a modulated Graph.
|
||||||
///
|
///
|
||||||
|
/// Loads a default graph with basic usage instructions if no file is found.
|
||||||
|
///
|
||||||
/// Returns a graph with an error message if any errors are propagated.
|
/// Returns a graph with an error message if any errors are propagated.
|
||||||
pub fn load() -> Graph {
|
pub fn load() -> Graph {
|
||||||
let result = Graph::load_file(None);
|
let result = Graph::load_file(None);
|
||||||
match result {
|
match result {
|
||||||
Ok(graph) => graph,
|
Ok(graph) => graph,
|
||||||
Err(error) => Graph::malformed(Some(&error)),
|
Err(error) => {
|
||||||
|
if error.not_found {
|
||||||
|
return Graph::welcome()
|
||||||
|
}
|
||||||
|
if let Some(message) = error.message {
|
||||||
|
Graph::malformed(Some(&message))
|
||||||
|
} else {
|
||||||
|
Graph::malformed(None)
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,7 +107,7 @@ impl Graph {
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// Propagates errors from `Graph::read_file`.
|
/// Propagates errors from `Graph::read_file`.
|
||||||
pub fn load_file(path: Option<&str>) -> Result<Graph, String> {
|
pub fn load_file(path: Option<&str>) -> Result<Graph, LoadError> {
|
||||||
let mut graph = Graph::from_file(path)?;
|
let mut graph = Graph::from_file(path)?;
|
||||||
graph.modulate();
|
graph.modulate();
|
||||||
Ok(graph)
|
Ok(graph)
|
||||||
|
|
@ -95,21 +118,24 @@ impl Graph {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// Returns Err if it can't read the contents of `in_path`.
|
/// Returns Err if it can't read the contents of `in_path`.
|
||||||
/// Propagates errors from `Graph::from_serial`.
|
/// Propagates errors from `Graph::from_serial`.
|
||||||
pub fn from_file(in_path: Option<&str>) -> Result<Graph, String> {
|
pub fn from_file(in_path: Option<&str>) -> Result<Graph, LoadError> {
|
||||||
let cli_path = Arguments::default().parse().graph_path;
|
let cli_path = Arguments::default().parse().graph_path;
|
||||||
let path = in_path.map_or(cli_path, PathBuf::from);
|
let path = in_path.map_or(cli_path, PathBuf::from);
|
||||||
|
|
||||||
let toml_source = match std::fs::read_to_string(&path) {
|
let toml_source = match std::fs::read_to_string(&path) {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(e) => {
|
Err(error) => {
|
||||||
log!(
|
log!(
|
||||||
ERROR,
|
ERROR,
|
||||||
"Error reading path {}: {e}",
|
"Error reading path {}: {error}",
|
||||||
path.as_path().display(),
|
path.as_path().display(),
|
||||||
);
|
);
|
||||||
return Err(format!(
|
return Err(LoadError::from_io_with_message(
|
||||||
"Failed reading file at {}",
|
&format!(
|
||||||
path.as_path().display(),
|
"Failed reading file at {}",
|
||||||
|
path.as_path().display(),
|
||||||
|
),
|
||||||
|
error,
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -489,12 +515,60 @@ impl Graph {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum Format {
|
pub enum Format {
|
||||||
TOML,
|
TOML,
|
||||||
JSON,
|
JSON,
|
||||||
Unsupported,
|
Unsupported,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct LoadError {
|
||||||
|
pub message: Option<String>,
|
||||||
|
pub not_found: bool,
|
||||||
|
pub io_error: Option<io::Error>,
|
||||||
|
pub serial_error: Option<SerialError>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LoadError {
|
||||||
|
fn from_io_with_message(message: &str, io_error: io::Error) -> LoadError {
|
||||||
|
LoadError {
|
||||||
|
message: Some(String::from(message)),
|
||||||
|
not_found: io_error.kind() == io::ErrorKind::NotFound,
|
||||||
|
io_error: Some(io_error),
|
||||||
|
serial_error: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SerialError> for LoadError {
|
||||||
|
fn from(error: SerialError) -> LoadError {
|
||||||
|
LoadError {
|
||||||
|
message: Some(error.message.clone()),
|
||||||
|
not_found: false,
|
||||||
|
serial_error: Some(error),
|
||||||
|
io_error: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::Error> for LoadError {
|
||||||
|
fn from(error: io::Error) -> LoadError {
|
||||||
|
LoadError {
|
||||||
|
message: Some(error.to_string()),
|
||||||
|
not_found: error.kind() == io::ErrorKind::NotFound,
|
||||||
|
io_error: Some(error),
|
||||||
|
serial_error: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct SerialError {
|
||||||
|
pub cause: SerialErrorCause,
|
||||||
|
pub message: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
pub enum SerialErrorCause {
|
pub enum SerialErrorCause {
|
||||||
UnsupportedFormat,
|
UnsupportedFormat,
|
||||||
|
|
@ -511,12 +585,6 @@ impl std::fmt::Display for SerialErrorCause {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
||||||
pub struct SerialError {
|
|
||||||
pub cause: SerialErrorCause,
|
|
||||||
pub message: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SerialError> for String {
|
impl From<SerialError> for String {
|
||||||
fn from(error: SerialError) -> String {
|
fn from(error: SerialError) -> String {
|
||||||
format!("{}: {}", error.cause, error.message)
|
format!("{}: {}", error.cause, error.message)
|
||||||
|
|
@ -1314,12 +1382,11 @@ mod serial_tests {
|
||||||
use crate::dev::test::{Directories, Error};
|
use crate::dev::test::{Directories, Error};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bad_graph_path() -> Result<(), Error> {
|
fn no_graph_fallback() -> Result<(), Error> {
|
||||||
let _dirs = Directories::setup("bad_graph_path")?;
|
let _dirs = Directories::setup("bad_graph_path")?;
|
||||||
|
|
||||||
let graph = Graph::load();
|
let graph = Graph::load();
|
||||||
let message = graph.meta.messages.first().unwrap();
|
assert_eq!(graph.nodes["GettingStarted"].title, "Getting Started");
|
||||||
assert!(message.contains("Failed reading file at"));
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -137,15 +137,14 @@ fn load_templates() -> Result<tera::Tera, tera::Error> {
|
||||||
let root = PathBuf::from("templates");
|
let root = PathBuf::from("templates");
|
||||||
let default_names: Vec<&str> = DEFAULTS.iter().map(|(n, _)| *n).collect();
|
let default_names: Vec<&str> = DEFAULTS.iter().map(|(n, _)| *n).collect();
|
||||||
|
|
||||||
log!(
|
|
||||||
DEBUG,
|
|
||||||
"Reading templates from {}, canonical form {:?}",
|
|
||||||
root.display(),
|
|
||||||
root.canonicalize()
|
|
||||||
);
|
|
||||||
|
|
||||||
match fs::read_dir(&root) {
|
match fs::read_dir(&root) {
|
||||||
Ok(dir) => {
|
Ok(dir) => {
|
||||||
|
log!(
|
||||||
|
DEBUG,
|
||||||
|
"Reading templates from root directory '{}', canonically {:?}",
|
||||||
|
root.display(),
|
||||||
|
root.canonicalize()
|
||||||
|
);
|
||||||
for file_opt in dir {
|
for file_opt in dir {
|
||||||
let file = file_opt?;
|
let file = file_opt?;
|
||||||
let path = file.path();
|
let path = file.path();
|
||||||
|
|
@ -168,6 +167,11 @@ fn load_templates() -> Result<tera::Tera, tera::Error> {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
|
log!(
|
||||||
|
VERBOSE,
|
||||||
|
"A 'templates' directory was not found or is not accessible: \
|
||||||
|
only built-in templates will be available"
|
||||||
|
);
|
||||||
if error.kind() != ErrorKind::NotFound {
|
if error.kind() != ErrorKind::NotFound {
|
||||||
return Err(tera::Error::msg(error.to_string()))
|
return Err(tera::Error::msg(error.to_string()))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
17
static/welcome.toml
Normal file
17
static/welcome.toml
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
[nodes.GettingStarted]
|
||||||
|
title = "Getting Started"
|
||||||
|
text = """
|
||||||
|
## Welcome to en!
|
||||||
|
#
|
||||||
|
If you are seeing this, it's working!
|
||||||
|
|
||||||
|
Now that you know how to run it, tell en how to find your graph file by adding a `--graph` option:
|
||||||
|
|
||||||
|
`
|
||||||
|
en --graph my_graph.toml
|
||||||
|
`
|
||||||
|
|
||||||
|
Alternatively, you can also add a `static` directory next to the en binary with a `graph.toml` file in it.
|
||||||
|
|
||||||
|
To learn how to write your first graph and everything else about en, check out the |documentation|https://en.jutty.dev|.
|
||||||
|
"""
|
||||||
Loading…
Add table
Add a link
Reference in a new issue