rename: CfgField: RandomId->Generated, +module doc
This commit is contained in:
parent
b2b4f15747
commit
15fcb18422
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.",
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue