Index: .efiles ================================================================== --- .efiles +++ .efiles @@ -1,3 +1,10 @@ Cargo.toml -src/*.rs +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 Index: .fossil-settings/ignore-glob ================================================================== --- .fossil-settings/ignore-glob +++ .fossil-settings/ignore-glob @@ -1,4 +1,3 @@ .*.swp -.gitignore Cargo.lock target Index: Cargo.toml ================================================================== --- Cargo.toml +++ Cargo.toml @@ -1,11 +1,31 @@ [package] name = "sidoc" -version = "0.1.0" -authors = ["Jan Danielsson "] -edition = "2018" +version = "0.1.1" +edition = "2021" license = "0BSD" +# https://crates.io/category_slugs +categories = [ "template-engine" ] keywords = [ "text", "document", "html" ] -repository = "https://github.com/openqrnch/sidoc" -description = "Library for generating structured/scoped indented documents." +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 Index: README.md ================================================================== --- /dev/null +++ README.md @@ -0,0 +1,4 @@ +# sidoc + +Template engine designed to generate indented documents. + ADDED bacon.toml Index: bacon.toml ================================================================== --- /dev/null +++ bacon.toml @@ -0,0 +1,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 Index: src/builder.rs ================================================================== --- src/builder.rs +++ src/builder.rs @@ -1,27 +1,27 @@ use crate::{Doc, Error, Node}; /// Constructor for `Doc` objects. +#[derive(Default)] pub struct Builder { nodes: Vec, scope_stack: Vec> } impl Builder { /// Create a new `Doc` builder context. + #[must_use] pub fn new() -> Self { - Builder { - nodes: Vec::new(), - scope_stack: Vec::new() - } + 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( &mut self, begin_line: L, term_line: Option ) -> &mut Self { @@ -35,10 +35,13 @@ } /// 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)); @@ -46,26 +49,32 @@ 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(&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(&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(&mut self, name: N) -> &mut Self { self.nodes.push(Node::OptRef(name.to_string())); self } @@ -72,19 +81,23 @@ /// 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(&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 { if self.scope_stack.is_empty() { Ok(Doc { nodes: self.nodes }) } else { Err(Error::BadNesting(format!( Index: src/doc.rs ================================================================== --- src/doc.rs +++ src/doc.rs @@ -1,14 +1,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 } impl Doc { + #[must_use] pub fn new() -> Self { - Doc { nodes: Vec::new() } + Self::default() } } // vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 : Index: src/err.rs ================================================================== --- src/err.rs +++ src/err.rs @@ -8,13 +8,13 @@ impl std::error::Error for Error {} impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match &*self { - Error::BadRef(s) => write!(f, "Bad reference error; {}", s), - Error::BadNesting(s) => write!(f, "Bad nesting error; {}", s) + 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 : Index: src/render.rs ================================================================== --- src/render.rs +++ src/render.rs @@ -1,33 +1,44 @@ -use std::collections::HashMap; -use std::sync::Arc; +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> } - -impl RenderContext { - /// Create a new render context object. - pub fn new() -> Self { - RenderContext { +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(&mut self, id: N, doc: Arc) { 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 { struct IterNode<'a> { lst: &'a Vec, idx: usize } @@ -42,38 +53,36 @@ iterstack.push(IterNode { lst: &dict.nodes, idx: 0 }); } else { - return Err(Error::BadRef(format!("Missing root document '{}'", name))); + return Err(Error::BadRef(format!("Missing root document '{name}'"))); } - 'outer: while !iterstack.is_empty() { - let mut it = iterstack.pop().unwrap(); - + '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_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_str(s); out.push('\n'); } } Node::Line(s) => { let is = istr.repeat(indent); out.push_str(&is); - out.push_str(&s); + out.push_str(s); out.push('\n'); } Node::OptRef(name) => { if let Some(dict) = self.dict.get(name) { iterstack.push(IterNode { @@ -98,16 +107,14 @@ iterstack.push(IterNode { lst: &dict.nodes, idx: 0 }); continue 'outer; - } else { - return Err(Error::BadRef(format!( - "Missing required document '{}'", - name - ))); } + return Err(Error::BadRef(format!( + "Missing required document '{name}'" + ))); } } it.idx += 1; } } ADDED www/changelog.md Index: www/changelog.md ================================================================== --- /dev/null +++ www/changelog.md @@ -0,0 +1,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 Index: www/index.md ================================================================== --- /dev/null +++ www/index.md @@ -0,0 +1,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. +