rename: CfgField: RandomId->Generated, +module doc

This commit is contained in:
nobody 2024-08-05 16:10:25 +02:00 committed by murmeldin
parent b2b4f15747
commit 15fcb18422
3 changed files with 57 additions and 7 deletions

View file

@ -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::is_dry_run;
use crate::key_value::KeyValueStore as KV; use crate::key_value::KeyValueStore as KV;
use std::error::Error; use std::error::Error;
@ -27,7 +59,7 @@ pub enum CfgField<'a> {
/// Empty by default, required, will prompt without echo. /// Empty by default, required, will prompt without echo.
Password { key: &'a str, description: &'a str }, Password { key: &'a str, description: &'a str },
/// Empty by default, can be user-provided, or will be generated randomly. /// Empty by default, can be user-provided, or will be generated randomly.
RandomId { Generated {
key: &'a str, key: &'a str,
generator: fn(config: &KV, is_dry_run: bool) -> Result<String, Box<dyn Error>>, generator: fn(config: &KV, is_dry_run: bool) -> Result<String, Box<dyn Error>>,
generator_description: &'a str, generator_description: &'a str,
@ -89,7 +121,7 @@ impl<'a> CfgField<'a> {
CfgField::Default { key, .. } => key, CfgField::Default { key, .. } => key,
CfgField::Optional { key, .. } => key, CfgField::Optional { key, .. } => key,
CfgField::Password { key, .. } => key, CfgField::Password { key, .. } => key,
CfgField::RandomId { key, .. } => key, CfgField::Generated { key, .. } => key,
} }
.to_string() .to_string()
} }
@ -106,7 +138,7 @@ impl<'a> CfgField<'a> {
CfgField::Default { description, .. } => description, CfgField::Default { description, .. } => description,
CfgField::Optional { description, .. } => description, CfgField::Optional { description, .. } => description,
CfgField::Password { description, .. } => description, CfgField::Password { description, .. } => description,
CfgField::RandomId { description, .. } => description, CfgField::Generated { description, .. } => description,
} }
.to_string() .to_string()
} }
@ -117,7 +149,7 @@ impl<'a> CfgField<'a> {
match self { match self {
CfgField::Silent { default, .. } => Some(default.to_string()), CfgField::Silent { default, .. } => Some(default.to_string()),
CfgField::Default { default, .. } => Some(default.to_string()), CfgField::Default { default, .. } => Some(default.to_string()),
CfgField::RandomId { generator_description, .. } => { CfgField::Generated { generator_description, .. } => {
Some(format!("{}{}{}", '(', generator_description, ')')) Some(format!("{}{}{}", '(', generator_description, ')'))
}, },
_ => None, _ => None,
@ -129,7 +161,7 @@ impl<'a> CfgField<'a> {
match self { match self {
CfgField::Silent { default, .. } => Ok(Some(default.to_string())), CfgField::Silent { default, .. } => Ok(Some(default.to_string())),
CfgField::Default { 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), _ => Ok(None),
} }
} }

View file

@ -17,13 +17,13 @@ pub const CONFIG: CfgGroup<'static> = CfgGroup {
default: "plenum-template", default: "plenum-template",
description: "Name of the pad containing the template to use.", description: "Name of the pad containing the template to use.",
}, },
CfgField::RandomId { CfgField::Generated {
key: "last-id", key: "last-id",
generator: make_pad_id, generator: make_pad_id,
generator_description: "Makes a new pad that's completely empty.", generator_description: "Makes a new pad that's completely empty.",
description: "ID of last plenum's pad.", description: "ID of last plenum's pad.",
}, },
CfgField::RandomId { CfgField::Generated {
key: "next-id", key: "next-id",
generator: make_pad_id, generator: make_pad_id,
generator_description: "Makes a new pad that's completely empty.", generator_description: "Makes a new pad that's completely empty.",

View file

@ -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 rusqlite::{params, Connection, Result};
use std::cell::Cell; use std::cell::Cell;
use std::collections::HashSet; use std::collections::HashSet;