Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Difference From sidoc-0.1.0 To sidoc-0.1.1
|
2024-11-07
| ||
| 09:13 | change log. check-in: ef4f5138fd user: jan tags: trunk | |
| 08:53 | Update crate metadata. Happy pedantic clippy. check-in: c84ce1ad85 user: jan tags: trunk, sidoc-0.1.1 | |
|
2024-11-06
| ||
| 18:42 | Happy pedantic clippy. check-in: c1d63406c5 user: jan tags: trunk | |
|
2022-09-29
| ||
| 18:48 | Move repo. Clippy. check-in: 98b35525c4 user: jan tags: trunk | |
|
2020-09-12
| ||
| 13:56 | Ignore gitignore. check-in: 4bf4749324 user: jan tags: trunk, sidoc-0.1.0 | |
| 13:54 | Move from temporary repository. check-in: b274896be6 user: jan tags: trunk | |
Changes to .efiles.
1 | Cargo.toml | > > > | > > > > | 1 2 3 4 5 6 7 8 9 10 | Cargo.toml README.md www/index.md www/changelog.md src/err.rs src/lib.rs src/builder.rs src/doc.rs src/render.rs tests/*.rs |
Changes to .fossil-settings/ignore-glob.
1 | .*.swp | < | 1 2 3 | .*.swp Cargo.lock target |
Changes to Cargo.toml.
1 2 | [package] name = "sidoc" | | < | > > | | > > > > > > > | > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
[package]
name = "sidoc"
version = "0.1.1"
edition = "2021"
license = "0BSD"
# https://crates.io/category_slugs
categories = [ "template-engine" ]
keywords = [ "text", "document", "html" ]
repository = "https://repos.qrnch.tech/pub/sidoc"
description = "Generate structured/scoped indented documents."
exclude = [
".fossil-settings",
".efiles",
".fslckout",
"www",
"bacon.toml",
"rustfmt.toml"
]
# https://doc.rust-lang.org/cargo/reference/manifest.html#the-badges-section
[badges]
maintenance = { status = "passively-maintained" }
[dependencies]
[lints.clippy]
all = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -1 }
nursery = { level = "warn", priority = -1 }
cargo = { level = "warn", priority = -1 }
|
Added README.md.
> > > > | 1 2 3 4 | # sidoc Template engine designed to generate indented documents. |
Added bacon.toml.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# This is a configuration file for the bacon tool
#
# Complete help on configuration: https://dystroy.org/bacon/config/
#
# You may check the current default at
# https://github.com/Canop/bacon/blob/main/defaults/default-bacon.toml
default_job = "clippy-all"
[jobs.check]
command = ["cargo", "check", "--color", "always"]
need_stdout = false
[jobs.check-all]
command = ["cargo", "check", "--all-targets", "--color", "always"]
need_stdout = false
# Run clippy on the default target
[jobs.clippy]
command = [
"cargo", "clippy",
"--color", "always",
]
need_stdout = false
# Run clippy on all targets
# To disable some lints, you may change the job this way:
# [jobs.clippy-all]
# command = [
# "cargo", "clippy",
# "--all-targets",
# "--color", "always",
# "--",
# "-A", "clippy::bool_to_int_with_if",
# "-A", "clippy::collapsible_if",
# "-A", "clippy::derive_partial_eq_without_eq",
# ]
# need_stdout = false
[jobs.clippy-all]
command = [
"cargo", "clippy",
"--all-targets",
"--color", "always",
]
need_stdout = false
# This job lets you run
# - all tests: bacon test
# - a specific test: bacon test -- config::test_default_files
# - the tests of a package: bacon test -- -- -p config
[jobs.test]
command = [
"cargo", "test", "--color", "always",
"--", "--color", "always", # see https://github.com/Canop/bacon/issues/124
]
need_stdout = true
[jobs.nextest]
command = [
"cargo", "nextest", "run",
"--color", "always",
"--hide-progress-bar", "--failure-output", "final"
]
need_stdout = true
analyzer = "nextest"
[jobs.doc]
command = ["cargo", "doc", "--color", "always", "--no-deps"]
need_stdout = false
# If the doc compiles, then it opens in your browser and bacon switches
# to the previous job
[jobs.doc-open]
command = ["cargo", "doc", "--color", "always", "--no-deps", "--open"]
need_stdout = false
on_success = "back" # so that we don't open the browser at each change
# You can run your application and have the result displayed in bacon,
# if it makes sense for this crate.
# Don't forget the `--color always` part or the errors won't be
# properly parsed.
[jobs.run]
command = [
"cargo", "run",
"--color", "always",
# put launch parameters for your program behind a `--` separator
]
need_stdout = true
allow_warnings = true
background = true
# Run your long-running application (eg server) and have the result displayed in bacon.
# For programs that never stop (eg a server), `background` is set to false
# to have the cargo run output immediately displayed instead of waiting for
# program's end.
# 'on_change_strategy' is set to `kill_then_restart` to have your program restart
# on every change (an alternative would be to use the 'F5' key manually in bacon).
# If you often use this job, it makes sense to override the 'r' key by adding
# a binding `r = job:run-long` at the end of this file .
[jobs.run-long]
command = [
"cargo", "run",
"--color", "always",
# put launch parameters for your program behind a `--` separator
]
need_stdout = true
allow_warnings = true
background = false
on_change_strategy = "kill_then_restart"
# This parameterized job runs the example of your choice, as soon
# as the code compiles.
# Call it as
# bacon ex -- my-example
[jobs.ex]
command = ["cargo", "run", "--color", "always", "--example"]
need_stdout = true
allow_warnings = true
# You may define here keybindings that would be specific to
# a project, for example a shortcut to launch a specific job.
# Shortcuts to internal functions (scrolling, toggling, etc.)
# should go in your personal global prefs.toml file instead.
[keybindings]
# alt-m = "job:my-job"
c = "job:clippy-all" # comment this to have 'c' run clippy on only the default target
|
Changes to src/builder.rs.
1 2 3 4 5 6 7 8 9 10 11 |
use crate::{Doc, Error, Node};
/// Constructor for `Doc` objects.
pub struct Builder {
nodes: Vec<Node>,
scope_stack: Vec<Option<String>>
}
impl Builder {
/// Create a new `Doc` builder context.
pub fn new() -> Self {
| > > | < < < > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
use crate::{Doc, Error, Node};
/// Constructor for `Doc` objects.
#[derive(Default)]
pub struct Builder {
nodes: Vec<Node>,
scope_stack: Vec<Option<String>>
}
impl Builder {
/// Create a new `Doc` builder context.
#[must_use]
pub fn new() -> Self {
Self::default()
}
/// Begin a scope, pushing an optional scope terminator to the internal scope
/// stack.
///
/// If the scope generated using a terminator line, that line will appended
/// to the document when the scope is closed using the `exit()` method.
#[allow(clippy::needless_pass_by_value)]
pub fn scope<L: ToString, K: ToString>(
&mut self,
begin_line: L,
term_line: Option<K>
) -> &mut Self {
self.nodes.push(Node::BeginScope(begin_line.to_string()));
if let Some(ln) = term_line {
self.scope_stack.push(Some(ln.to_string()));
} else {
self.scope_stack.push(None);
}
self
}
/// Leave a previously entered scope.
///
/// If the `scope()` call that created the current scope
///
/// # Panics
/// The scope stack must not be empty.
pub fn exit(&mut self) -> &mut Self {
if let Some(s) = self.scope_stack.pop().unwrap() {
self.nodes.push(Node::EndScope(Some(s)));
} else {
self.nodes.push(Node::EndScope(None));
}
self
}
/// Leave previously entered scope, adding a line passed by the caller rather
/// than the scope stack.
///
/// # Panics
/// The scope stack must not be empty.
#[allow(clippy::needless_pass_by_value)]
pub fn exit_line<L: ToString>(&mut self, line: L) -> &mut Self {
let _ = self.scope_stack.pop().unwrap();
self.nodes.push(Node::EndScope(Some(line.to_string())));
self
}
/// Add a new line at current scope.
#[allow(clippy::needless_pass_by_value)]
pub fn line<L: ToString>(&mut self, line: L) -> &mut Self {
self.nodes.push(Node::Line(line.to_string()));
self
}
/// Add a named optional reference.
///
/// References are placeholders for other documents. An optional reference
/// means that this reference does not need to be resolved by the renderer.
#[allow(clippy::needless_pass_by_value)]
pub fn optref<N: ToString>(&mut self, name: N) -> &mut Self {
self.nodes.push(Node::OptRef(name.to_string()));
self
}
/// Add a named required reference.
///
/// References are placeholders for other documents. A required reference
/// must be resolved by the renderer or it will return an error to its
/// caller.
#[allow(clippy::needless_pass_by_value)]
pub fn reqref<N: ToString>(&mut self, name: N) -> &mut Self {
self.nodes.push(Node::ReqRef(name.to_string()));
self
}
/// Generate a `Doc` object from this document.
///
/// The document must be properly nested before calling this function,
/// meaning all scopes it opened must be closed.
///
/// # Errors
/// [`Error::BadNesting`] means one or more scopes are still open.
pub fn build(self) -> Result<Doc, Error> {
if self.scope_stack.is_empty() {
Ok(Doc { nodes: self.nodes })
} else {
Err(Error::BadNesting(format!(
"{} scope(s) remaining",
self.scope_stack.len()
)))
}
}
}
// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :
|
Changes to src/doc.rs.
1 2 3 4 5 6 7 8 9 |
use crate::Node;
/// A "Doc" represents a set of lines and references to other Doc's.
pub struct Doc {
pub(crate) nodes: Vec<Node>
}
impl Doc {
pub fn new() -> Self {
| > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
use crate::Node;
/// A "Doc" represents a set of lines and references to other Doc's.
#[derive(Default)]
pub struct Doc {
pub(crate) nodes: Vec<Node>
}
impl Doc {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :
|
Changes to src/err.rs.
1 2 3 4 5 6 7 8 9 10 11 12 |
use std::fmt;
#[derive(Debug)]
pub enum Error {
BadRef(String),
BadNesting(String)
}
impl std::error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
| | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
use std::fmt;
#[derive(Debug)]
pub enum Error {
BadRef(String),
BadNesting(String)
}
impl std::error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::BadRef(s) => write!(f, "Bad reference error; {s}"),
Self::BadNesting(s) => write!(f, "Bad nesting error; {s}")
}
}
}
// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :
|
Changes to src/render.rs.
|
| | < > < | < | | | > > > > > > > > > > > > > | < | < | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
use std::{collections::HashMap, sync::Arc};
use crate::{Doc, Error, Node};
#[allow(clippy::module_name_repetitions)]
pub struct RenderContext {
ichar: char,
iwidth: usize,
dict: HashMap<String, Arc<Doc>>
}
impl Default for RenderContext {
fn default() -> Self {
Self {
ichar: ' ',
iwidth: 2,
dict: HashMap::new()
}
}
}
impl RenderContext {
/// Create a new render context object.
#[must_use]
pub fn new() -> Self {
Self::default()
}
/// Add a shared `Doc` document to the render context.
#[allow(clippy::needless_pass_by_value)]
pub fn doc<N: ToString>(&mut self, id: N, doc: Arc<Doc>) {
self.dict.insert(id.to_string(), Arc::clone(&doc));
}
/// Render a root `Doc`, resolving all references.
///
/// # Errors
/// [`Error::BadRef`] means a referenced document does not exist.
pub fn render(&self, name: &str) -> Result<String, Error> {
struct IterNode<'a> {
lst: &'a Vec<Node>,
idx: usize
}
let mut iterstack = Vec::new();
let mut out = String::new();
let mut indent: usize = 0;
// Generate single indent string
let istr = self.ichar.to_string().repeat(self.iwidth);
if let Some(dict) = self.dict.get(name) {
iterstack.push(IterNode {
lst: &dict.nodes,
idx: 0
});
} else {
return Err(Error::BadRef(format!("Missing root document '{name}'")));
}
'outer: while let Some(mut it) = iterstack.pop() {
while it.idx < it.lst.len() {
match &it.lst[it.idx] {
Node::BeginScope(s) => {
let is = istr.repeat(indent);
out.push_str(&is);
out.push_str(s);
out.push('\n');
indent += 1;
}
Node::EndScope(s) => {
indent -= 1;
if let Some(s) = s {
let is = istr.repeat(indent);
out.push_str(&is);
out.push_str(s);
out.push('\n');
}
}
Node::Line(s) => {
let is = istr.repeat(indent);
out.push_str(&is);
out.push_str(s);
out.push('\n');
}
Node::OptRef(name) => {
if let Some(dict) = self.dict.get(name) {
iterstack.push(IterNode {
lst: it.lst,
idx: it.idx + 1
|
| ︙ | ︙ | |||
96 97 98 99 100 101 102 |
});
iterstack.push(IterNode {
lst: &dict.nodes,
idx: 0
});
continue 'outer;
| < > | | < | < | 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
});
iterstack.push(IterNode {
lst: &dict.nodes,
idx: 0
});
continue 'outer;
}
return Err(Error::BadRef(format!(
"Missing required document '{name}'"
)));
}
}
it.idx += 1;
}
}
Ok(out)
}
}
// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :
|
Added www/changelog.md.
> > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # Change Log ⚠️ indicates a breaking change. ## [Unreleased] [Details](/vdiff?from=sidoc-0.1.0&to=trunk) ### Added - Derive `Default` on `Doc` and `RenderContext` ### Changed ### Removed --- ## [0.1.0] - 2020-09-12 Initial release |
Added www/index.md.
> > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # sidoc Template engine designed to generate indented documents. ## Change log The details of changes can always be found in the timeline, but for a high-level view of changes between released versions there's a manually maintained [Change Log](./changelog.md). ## Project Status sidoc is passively maintained. It will receive updates and fixes and needed, but is currently considered to be feature complete. |