diff --git a/Cargo.toml b/Cargo.toml index cff3d7a..5cabe31 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,4 @@ clap = "4.5.11" rpassword = "7.3.1" serde = {version = "1.0.204", features = ["derive"]} serde_json = "1.0.122" +colored = "2.1.0" diff --git a/src/config_spec.rs b/src/config_spec.rs index 4f1ff99..58f64e4 100644 --- a/src/config_spec.rs +++ b/src/config_spec.rs @@ -17,15 +17,32 @@ //! and then in the main module, glue them all together and use them to populate defaults etc. //! //! ```rust +//! # mod modname { +//! # 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 +//! # ], +//! # }; +//! # } //! const CONFIG_SPEC: CfgSpec<'static> = CfgSpec { //! groups: &[ //! modname::CONFIG, //! // etc. //! ], //! }; +//! // open a config file somewhere (we use a dummy to not cause side-effects) +//! let config = KeyValueStore::new_dummy()?; +//! assert_eq!( config.get("modname-user").unwrap(), "xyzzy" ); //! // always: -//! let config = KeyValueStore::new("config.sqlite"); //! CONFIG_SPEC.populate_defaults(&config); +//! ``` +//! +//! ```ignore //! // only if you're explicitly wanting to check / edit the config: //! CONFIG_SPEC.interactive_check(config); //! ``` diff --git a/src/email.rs b/src/email.rs index 13f783e..fdd735a 100644 --- a/src/email.rs +++ b/src/email.rs @@ -1,9 +1,9 @@ use std::error::Error; use colored::Colorize; -use lettre::{Message, SmtpTransport, Transport}; use lettre::message::{header, SinglePart}; use lettre::transport::smtp::authentication::Credentials; +use lettre::{Message, SmtpTransport, Transport}; use uuid::Uuid; use crate::config_spec::{CfgField, CfgGroup}; diff --git a/src/key_value.rs b/src/key_value.rs index 14984d1..07c2171 100644 --- a/src/key_value.rs +++ b/src/key_value.rs @@ -1,8 +1,12 @@ //! SQLite-backed key/value store, primarily intended as a config file (few writes). //! -//! ```rust +//! ```ignore //! // Open a new or existing DB (no distinction made.) //! let cfg = KeyValueStore::new("config.sqlite")?; +//! ``` +//! +//! ``` +//! # let cfg = KeyValueStore::new_dummy()?; //! // Ensure defaults exist. (Do this early, this function panics on error.) //! cfg.default( "foo", "bar" ); //! cfg.default( "baz", "quux" ); @@ -16,13 +20,12 @@ //! let all_ok = !cfg.has_errors(); //! ``` +use colored::Colorize; +use rusqlite::{params, Connection, Result}; use std::cell::Cell; use std::collections::HashSet; use std::ops::Index; -use colored::Colorize; -use rusqlite::{Connection, params, Result}; - /// Simple SQLite-backed key/value store. /// /// All failing writes will log a message, any errors will set `has_errors`. @@ -40,6 +43,17 @@ impl KeyValueStore { /// Open or create a DB with a `kv_store` table storing all tuples. pub fn new(db_path: &str) -> Result { let conn = Connection::open(db_path)?; + KeyValueStore::setup(conn) + } + + /// Mostly for tests, make a temporary DB in memory. + pub fn new_dummy() -> Result { + let conn = Connection::open_in_memory()?; + KeyValueStore::setup(conn) + } + + /// Actual constructor. + fn setup(conn: Connection) -> Result { conn.execute( "CREATE TABLE IF NOT EXISTS kv_store ( key TEXT PRIMARY KEY, diff --git a/src/main.rs b/src/main.rs index af06a4d..a29f986 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,18 +1,3 @@ -use std::borrow::Cow; -use std::env; -use std::error::Error; - -use chrono::{Datelike, Local, NaiveDate, Weekday}; -use clap::{Arg, Command}; -use colored::Colorize; -use regex::Regex; -use reqwest::blocking::Client; - -use config_spec::{CfgField, CfgGroup, CfgSpec}; -use email::{Email, SimpleEmail}; -use hedgedoc::HedgeDoc; -use key_value::KeyValueStore as KV; - // Dies ist der Plenumsbot vom Chaos Computer Club Berlin. /* Plenumsbot @@ -44,18 +29,28 @@ MediaWiki future improvements: - search ADJ_TIMEYWIMEY to find places that need adjusting if the bot might run late (that's an incomplete list, but tag things as you notice them…) +*/ +use chrono::{Datelike, Local, NaiveDate, Weekday}; +use clap::{Arg, Command}; +use colored::Colorize; +use regex::Regex; +use reqwest::blocking::Client; +use std::borrow::Cow; +use std::env; +use std::error::Error; - */ -// Import other .rs files as modules mod key_value; +use key_value::KeyValueStore as KV; mod config_spec; +use config_spec::{CfgField, CfgGroup, CfgSpec}; mod template; - pub mod variables_and_settings; mod email; +use email::{Email, SimpleEmail}; mod hedgedoc; +use hedgedoc::HedgeDoc; mod mediawiki; const FALLBACK_TEMPLATE: &str = variables_and_settings::FALLBACK_TEMPLATE; @@ -146,7 +141,7 @@ fn main() -> Result<(), Box> { "#; println!("{}", ansi_art.red()); - + let args = parse_args(); let config_file = args.config_file.as_str(); let config = KV::new(config_file).unwrap(); @@ -234,7 +229,7 @@ fn main() -> Result<(), Box> { let auth_result = mediawiki::get_login_token(&Client::new(), &config.get("wiki-http-user").expect("HTTP User not found DB!"), &config.get("wiki-http-password").expect("HTTP Password not found DB!"))?; println!("---AUTH RESULT:---\n{}\n-----------", auth_result); // TEMPORÄR ENDE - + let pad_content = hedgedoc.download(¤t_pad_id).expect("Fehler beim Download des Pads!"); let pad_content_without_top_instructions = try_to_remove_top_instructions(pad_content); println!("Pad-content geladen!"); diff --git a/src/template.rs b/src/template.rs index d41de22..f6dfb1a 100644 --- a/src/template.rs +++ b/src/template.rs @@ -1,11 +1,16 @@ use regex::{Captures, Regex}; -pub fn replace( text: &str, replacer: F ) -> String where F: Fn(&Captures) -> String { - let regex = Regex::new( r"\{\{([\w_-]+)\}\}" ).unwrap(); +pub fn replace(text: &str, replacer: F) -> String +where + F: Fn(&Captures) -> String, +{ + let regex = Regex::new(r"\{\{([\w_-]+)\}\}").unwrap(); regex.replace_all(text, replacer).into_owned() } -pub fn config_replacer<'a>( config: &'a crate::KV, blacklist_substrings : &'a [&'a str] ) -> impl Fn(&Captures) -> String + 'a { +pub fn config_replacer<'a>( + config: &'a crate::KV, blacklist_substrings: &'a [&'a str], +) -> impl Fn(&Captures) -> String + 'a { move |caps: &Captures| { let key = &caps[1]; if blacklist_substrings.iter().any(|&substr| key.contains(substr)) {