diff --git a/src/config_spec.rs b/src/config_spec.rs index 43e7e7f..4f1ff99 100644 --- a/src/config_spec.rs +++ b/src/config_spec.rs @@ -1,3 +1,35 @@ +//! Config specification helpers. +//! +//! Per module that wants config fields, declare a +//! ```rust +//! pub const CONFIG: CfgGroup<'static> = CfgGroup { +//! name: "modname", // this will be the field name prefix in the KV store +//! description: "what this module is for", +//! fields: &[ +//! // full field name in the KV store will be `"modname-user"` +//! CfgField::Default { key: "user", default: "xyzzy", description: "User name for someservice." }, +//! CfgField::Password { key: "password", description: "Password for someservice." }, +//! // also available: Optional, Generated, Silent +//! ], +//! }; +//! ``` +//! +//! and then in the main module, glue them all together and use them to populate defaults etc. +//! +//! ```rust +//! const CONFIG_SPEC: CfgSpec<'static> = CfgSpec { +//! groups: &[ +//! modname::CONFIG, +//! // etc. +//! ], +//! }; +//! // always: +//! let config = KeyValueStore::new("config.sqlite"); +//! CONFIG_SPEC.populate_defaults(&config); +//! // only if you're explicitly wanting to check / edit the config: +//! CONFIG_SPEC.interactive_check(config); +//! ``` + use crate::is_dry_run; use crate::key_value::KeyValueStore as KV; use std::error::Error; @@ -27,7 +59,7 @@ pub enum CfgField<'a> { /// Empty by default, required, will prompt without echo. Password { key: &'a str, description: &'a str }, /// Empty by default, can be user-provided, or will be generated randomly. - RandomId { + Generated { key: &'a str, generator: fn(config: &KV, is_dry_run: bool) -> Result>, generator_description: &'a str, @@ -89,7 +121,7 @@ impl<'a> CfgField<'a> { CfgField::Default { key, .. } => key, CfgField::Optional { key, .. } => key, CfgField::Password { key, .. } => key, - CfgField::RandomId { key, .. } => key, + CfgField::Generated { key, .. } => key, } .to_string() } @@ -106,7 +138,7 @@ impl<'a> CfgField<'a> { CfgField::Default { description, .. } => description, CfgField::Optional { description, .. } => description, CfgField::Password { description, .. } => description, - CfgField::RandomId { description, .. } => description, + CfgField::Generated { description, .. } => description, } .to_string() } @@ -117,7 +149,7 @@ impl<'a> CfgField<'a> { match self { CfgField::Silent { default, .. } => Some(default.to_string()), CfgField::Default { default, .. } => Some(default.to_string()), - CfgField::RandomId { generator_description, .. } => { + CfgField::Generated { generator_description, .. } => { Some(format!("{}{}{}", '(', generator_description, ')')) }, _ => None, @@ -129,7 +161,7 @@ impl<'a> CfgField<'a> { match self { CfgField::Silent { default, .. } => Ok(Some(default.to_string())), CfgField::Default { default, .. } => Ok(Some(default.to_string())), - CfgField::RandomId { generator, .. } => generator(config, is_dry_run()).map(Some), + CfgField::Generated { generator, .. } => generator(config, is_dry_run()).map(Some), _ => Ok(None), } } diff --git a/src/hedgedoc.rs b/src/hedgedoc.rs index 32e4089..683c406 100644 --- a/src/hedgedoc.rs +++ b/src/hedgedoc.rs @@ -17,13 +17,13 @@ pub const CONFIG: CfgGroup<'static> = CfgGroup { default: "plenum-template", description: "Name of the pad containing the template to use.", }, - CfgField::RandomId { + CfgField::Generated { key: "last-id", generator: make_pad_id, generator_description: "Makes a new pad that's completely empty.", description: "ID of last plenum's pad.", }, - CfgField::RandomId { + CfgField::Generated { key: "next-id", generator: make_pad_id, generator_description: "Makes a new pad that's completely empty.", diff --git a/src/key_value.rs b/src/key_value.rs index 8aabce3..0a605bb 100644 --- a/src/key_value.rs +++ b/src/key_value.rs @@ -1,3 +1,21 @@ +//! SQLite-backed key/value store, primarily intended as a config file (few writes). +//! +//! ```rust +//! // Open a new or existing DB (no distinction made.) +//! let cfg = KeyValueStore::new("config.sqlite")?; +//! // Ensure defaults exist. (Do this early, this function panics on error.) +//! cfg.default( "foo", "bar" ); +//! cfg.default( "baz", "quux" ); +//! // Normal use after that. +//! let foo = cfg.get( "foo" ).unwrap(); +//! let baz = &cfg["baz"]; // shorthand that LEAKS the string and panics on error +//! let asdf = cfg.get( "asdf" ).unwrap_or_default( "abc" ); +//! cfg.set( "fnord", "23" )?; +//! cfg.delete( "foo" )?; +//! // If any writes failed, this flag will be set and the data got logged to stderr. +//! let all_ok = !cfg.has_errors(); +//! ``` + use rusqlite::{params, Connection, Result}; use std::cell::Cell; use std::collections::HashSet;