From c8031dfc011b8b34bba5e620ecbf17c31d2f6424 Mon Sep 17 00:00:00 2001 From: murmeldin Date: Fri, 2 Aug 2024 22:29:22 +0200 Subject: [PATCH] mit cargo fmt alles formatiert --- src/config_check.rs | 156 ++++++++++++++++++---------- src/hedgedoc.rs | 48 +++++---- src/main.rs | 185 +++++++++++++++++++++------------- src/mediawiki.rs | 19 ++-- src/variables_and_settings.rs | 2 +- 5 files changed, 256 insertions(+), 154 deletions(-) diff --git a/src/config_check.rs b/src/config_check.rs index b1491c4..ecd688b 100644 --- a/src/config_check.rs +++ b/src/config_check.rs @@ -3,14 +3,14 @@ use std::error::Error; use std::io::{self, Write}; /// Text shown for an empty field. -const EMPTY_VALUE : &str = "(empty)"; +const EMPTY_VALUE: &str = "(empty)"; /// Text shown in place of a password. -const HIDDEN_PASSWORD : &str = "*****"; +const HIDDEN_PASSWORD: &str = "*****"; // highlight some of the info const ANSI_GROUP: &str = "\x1b[1;31m"; const ANSI_FIELD: &str = "\x1b[1;33m"; -const ANSI_NOTICE: &str= "\x1b[33m"; +const ANSI_NOTICE: &str = "\x1b[33m"; const ANSI_RESET: &str = "\x1b[0m"; const CONFIG_SPEC: CfgSpec<'static> = CfgSpec { @@ -70,7 +70,7 @@ pub fn populate_defaults(config: &KV) { CfgField::Default { default, .. } => { config.default(&field.full_key(group.name), default) }, - _ => {} + _ => {}, } } } @@ -80,33 +80,47 @@ pub fn populate_defaults(config: &KV) { /// /// Will report if the config is fine or has missing values. pub fn interactive_check(config: KV) -> Result<(), Box> { - let mut all_valid : bool = true; + let mut all_valid: bool = true; for group in CONFIG_SPEC.groups { - group_header( group.name, group.description ); + group_header(group.name, group.description); let todo = needs_info(&config, group); // TODO: add distinction between edit all / edit necessary only let choices = if !todo.is_empty() { - println!( "{}The following fields need adjustment: {}{}", ANSI_NOTICE, todo.join(", "), ANSI_RESET ); + println!( + "{}The following fields need adjustment: {}{}", + ANSI_NOTICE, + todo.join(", "), + ANSI_RESET + ); &["[E]dit (default)", "[L]ist all", "[S]kip group"] } else { - println!( "{}This group looks fine. OK to [S]kip, unless you want to adjust values here.{}", ANSI_NOTICE, ANSI_RESET ); - &["[S]kip group (default)", "[E]dit", "[L]ist all", ] + println!( + "{}This group looks fine. OK to [S]kip, unless you want to adjust values here.{}", + ANSI_NOTICE, ANSI_RESET + ); + &["[S]kip group (default)", "[E]dit", "[L]ist all"] }; loop { - let choice = prompt_action( choices ); + let choice = prompt_action(choices); match choice { - 'L' => { show_group(&config, group); }, + 'L' => { + show_group(&config, group); + }, 'E' => { for field in group.fields { - check_field( &config, group.name, field, &mut all_valid )?; + check_field(&config, group.name, field, &mut all_valid)?; } break; }, 'S' => { - if !todo.is_empty() { all_valid = false; } + if !todo.is_empty() { + all_valid = false; + } break; }, - _ => { unreachable!(); }, // (prompt already checks) + _ => { + unreachable!(); + }, // (prompt already checks) } } } @@ -117,30 +131,49 @@ pub fn interactive_check(config: KV) -> Result<(), Box> { } } -fn check_field( config: &KV, grpname: &str, field: &CfgField, ok: &mut bool ) -> Result<(), Box> { - if field.is_silent() { return Ok(()) } +fn check_field( + config: &KV, grpname: &str, field: &CfgField, ok: &mut bool, +) -> Result<(), Box> { + if field.is_silent() { + return Ok(()); + } show_field(config, grpname, field); let key = field.full_key(grpname); let value = config.get(&key).ok(); let mut actions: Vec<&'static str> = Vec::new(); // TODO: adjust order: empty password should offer input first // TODO: RandomId should offer generating as [R]egenerate - if value.is_some() | field.is_password() | field.is_optional() { actions.push("[K]eep as-is"); } - if field.default_description().is_some() { actions.push("[R]eset to default"); } - if field.is_password() { actions.push( "[R]emove" ); } - actions.push( "[I]nput new value" ); - if !field.is_password() { actions.push( "[L]ong (multiline) input" ) }; + if value.is_some() | field.is_password() | field.is_optional() { + actions.push("[K]eep as-is"); + } + if field.default_description().is_some() { + actions.push("[R]eset to default"); + } + if field.is_password() { + actions.push("[R]emove"); + } + actions.push("[I]nput new value"); + if !field.is_password() { + actions.push("[L]ong (multiline) input") + }; match prompt_action(&actions) { 'K' => { // we allow leaving a password empty, but that means config is incomplete - if value.is_none() & !field.is_optional() { *ok &= false; } + if value.is_none() & !field.is_optional() { + *ok &= false; + } }, 'R' => { match field.default_value()? { - Some(value) => { config.set(&key, &value)?; }, + Some(value) => { + config.set(&key, &value)?; + }, // password again - None => { config.delete(&key)?; *ok &= false; }, + None => { + config.delete(&key)?; + *ok &= false; + }, } }, 'I' => { @@ -151,43 +184,49 @@ fn check_field( config: &KV, grpname: &str, field: &CfgField, ok: &mut bool ) -> let value = prompt_multiline(); config.set(&key, &value)?; }, - _ => { return Err("Wat.".into()); } + _ => { + return Err("Wat.".into()); + }, } Ok(()) } /* ***** displaying various kinds of info ***** */ -fn group_header( name: &str, description: &str ) { +fn group_header(name: &str, description: &str) { println!("=============================="); println!("{}{}{} - {}", ANSI_GROUP, name, ANSI_RESET, description); println!(""); } -fn show_group( config: &KV, group: &CfgGroup ) { +fn show_group(config: &KV, group: &CfgGroup) { for field in group.fields { - show_field( &config, group.name, field ); + show_field(&config, group.name, field); } } -fn show_field( config: &KV, grpname: &str, field: &CfgField ) { - if field.is_silent() { return } +fn show_field(config: &KV, grpname: &str, field: &CfgField) { + if field.is_silent() { + return; + } let key = field.full_key(grpname); - println!( "{}{}{} - {}", ANSI_FIELD, &key, ANSI_RESET, field.description() ); - println!( " default: {}", field.default_description().unwrap_or(EMPTY_VALUE.to_string())); - let value = config.get(&key).ok() + println!("{}{}{} - {}", ANSI_FIELD, &key, ANSI_RESET, field.description()); + println!(" default: {}", field.default_description().unwrap_or(EMPTY_VALUE.to_string())); + let value = config + .get(&key) + .ok() .map(|s| if field.is_password() { HIDDEN_PASSWORD.to_string() } else { s }) .unwrap_or(EMPTY_VALUE.to_string()); - println!( " current: {}", value ); + println!(" current: {}", value); } /* ***** basic validation ***** */ -fn needs_info( config: &KV, group: &CfgGroup ) -> Vec { +fn needs_info(config: &KV, group: &CfgGroup) -> Vec { let mut acc = Vec::new(); for field in group.fields { if field.is_action_required(config, group.name) { - acc.push( field.key() ); + acc.push(field.key()); } } acc @@ -197,12 +236,12 @@ fn needs_info( config: &KV, group: &CfgGroup ) -> Vec { /// Ask for a choice between several options. First option is the default, /// format options with the first character in brackets like so: `"[E]xample"`. -fn prompt_action( choices: &[&str] ) -> char { +fn prompt_action(choices: &[&str]) -> char { let prompt_message = choices.join(", "); let default_choice = choices[0].to_uppercase().chars().nth(1).unwrap_or_default(); loop { - println!( "{}", prompt_message ); - print!( "Select: " ); + println!("{}", prompt_message); + print!("Select: "); io::stdout().flush().ok(); // Read line and take first non-space character @@ -211,7 +250,10 @@ fn prompt_action( choices: &[&str] ) -> char { let input = input.trim().to_uppercase().chars().next().unwrap_or(default_choice); // Check list of choices for match - if choices.iter().any(|&choice| choice.to_uppercase().chars().nth(1).unwrap_or_default() == input) { + if choices + .iter() + .any(|&choice| choice.to_uppercase().chars().nth(1).unwrap_or_default() == input) + { return input; } else { println!("Invalid choice. Try again!"); @@ -220,7 +262,7 @@ fn prompt_action( choices: &[&str] ) -> char { } /// Read a single line of text. -fn prompt_single_line( ) -> String { +fn prompt_single_line() -> String { print!("New value: "); io::stdout().flush().ok(); let mut input = String::new(); @@ -229,7 +271,7 @@ fn prompt_single_line( ) -> String { } /// Read multiple lines of text, terminated by a line containing just a '.'. -fn prompt_multiline( ) -> String { +fn prompt_multiline() -> String { println!("Enter new value: (end with '.' on a new line)"); let mut acc = String::new(); loop { @@ -244,7 +286,7 @@ fn prompt_multiline( ) -> String { } /// Read a password without echoing it. -fn prompt_password( ) -> String { +fn prompt_password() -> String { let pass = rpassword::prompt_password("New password (not shown) : ").unwrap(); // disabled echo means the newline also isn't shown println!(""); @@ -264,7 +306,12 @@ 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 { key: &'a str, generator: fn() -> Result>, generator_description: &'a str, description: &'a str }, + RandomId { + key: &'a str, + generator: fn() -> Result>, + generator_description: &'a str, + description: &'a str, + }, } /// A group of related config fields. The final key of an inner value will be @@ -285,17 +332,17 @@ struct CfgSpec<'a> { impl<'a> CfgField<'a> { /// Silent fields don't get prompted or shown. fn is_silent(&self) -> bool { - matches!( self, CfgField::Silent { .. } ) + matches!(self, CfgField::Silent { .. }) } /// Password fields will be censored when displayed. fn is_password(&self) -> bool { - matches!( self, CfgField::Password { .. } ) + matches!(self, CfgField::Password { .. }) } /// Optional fields are allowed to be (and stay) empty. fn is_optional(&self) -> bool { - matches!( self, CfgField::Optional { .. } ) + matches!(self, CfgField::Optional { .. }) } /// Reports if the field needs changing or is ok as-is. @@ -304,7 +351,10 @@ impl<'a> CfgField<'a> { /// *necessarily* always have a value. Optional is optional. Only /// Password and RandomId might need actions if they are missing.) fn is_action_required(&self, config: &KV, grpname: &str) -> bool { - if matches!( self, CfgField::Silent { .. } | CfgField::Default { .. } | CfgField::Optional { .. } ) { + if matches!( + self, + CfgField::Silent { .. } | CfgField::Default { .. } | CfgField::Optional { .. } + ) { false } else { config.get(self.full_key(grpname).as_str()).ok().is_none() @@ -319,7 +369,8 @@ impl<'a> CfgField<'a> { CfgField::Optional { key, .. } => key, CfgField::Password { key, .. } => key, CfgField::RandomId { key, .. } => key, - }.to_string() + } + .to_string() } /// Full field name / key used in the DB (currently `{grpname}-{fieldname}`.) @@ -335,7 +386,8 @@ impl<'a> CfgField<'a> { CfgField::Optional { description, .. } => description, CfgField::Password { description, .. } => description, CfgField::RandomId { description, .. } => description, - }.to_string() + } + .to_string() } /// Gets a description of the default value if one exists. @@ -344,7 +396,9 @@ impl<'a> CfgField<'a> { match self { CfgField::Silent { default, .. } => Some(default.to_string()), CfgField::Default { default, .. } => Some(default.to_string()), - CfgField::RandomId { generator_description, .. } => Some(format!("{}{}{}",'(',generator_description,')')), + CfgField::RandomId { generator_description, .. } => { + Some(format!("{}{}{}", '(', generator_description, ')')) + }, _ => None, } } diff --git a/src/hedgedoc.rs b/src/hedgedoc.rs index 96cd72d..9c9949f 100644 --- a/src/hedgedoc.rs +++ b/src/hedgedoc.rs @@ -1,63 +1,61 @@ -use reqwest::blocking::Response; use reqwest::blocking::Client; +use reqwest::blocking::Response; use std::error::Error; // TODO: implement dry-run logic pub struct HedgeDoc { - server_url : String, - is_dry_run : bool, - client : Client, + server_url: String, + is_dry_run: bool, + client: Client, } impl HedgeDoc { - pub fn new( server_url: &str, is_dry_run: bool ) -> Self { + pub fn new(server_url: &str, is_dry_run: bool) -> Self { Self { server_url: server_url.to_string(), is_dry_run, client: Client::new() } } - pub fn format_url( &self, pad_name: &str ) -> String { - format!( "{}/{}", self.server_url, pad_name ) + pub fn format_url(&self, pad_name: &str) -> String { + format!("{}/{}", self.server_url, pad_name) } - fn format_action( &self, pad_name: &str, verb: &str ) -> String { - format!( "{}/{}/{}", self.server_url, pad_name, verb ) + fn format_action(&self, pad_name: &str, verb: &str) -> String { + format!("{}/{}/{}", self.server_url, pad_name, verb) } - fn do_request(&self, url : &str ) -> Result> { - Ok(self.client.get( url ).send().unwrap()) + fn do_request(&self, url: &str) -> Result> { + Ok(self.client.get(url).send().unwrap()) } - fn get_id_from_response( &self, res : Response ) -> String { - res.url().to_string().trim_start_matches( &format!( "{}/", self.server_url ) ).to_string() + fn get_id_from_response(&self, res: Response) -> String { + res.url().to_string().trim_start_matches(&format!("{}/", self.server_url)).to_string() } - pub fn download( &self, pad_name: &str ) -> Result> { - Ok(self.do_request( &self.format_action(pad_name, "download"))?.text()?) + pub fn download(&self, pad_name: &str) -> Result> { + Ok(self.do_request(&self.format_action(pad_name, "download"))?.text()?) } - pub fn create_pad( &self ) -> Result> { - let res = self.do_request( &format!( "{}/new", self.server_url ) ).unwrap(); + pub fn create_pad(&self) -> Result> { + let res = self.do_request(&format!("{}/new", self.server_url)).unwrap(); if res.status().is_success() { Ok(self.get_id_from_response(res)) } else { - Err( format!("Failed to create pad {}", res.status()).into() ) + Err(format!("Failed to create pad {}", res.status()).into()) } } - pub fn import_note( &self, id: Option<&str>, content: String ) -> Result> { + pub fn import_note(&self, id: Option<&str>, content: String) -> Result> { let url = match id { - Some(id) => self.format_url( &format!( "new/{id}" ) ), + Some(id) => self.format_url(&format!("new/{id}")), None => self.format_url("new"), }; - let res = self.client.post(&url) - .header( "Content-Type", "text/markdown" ) - .body(content) - .send()?; + let res = + self.client.post(&url).header("Content-Type", "text/markdown").body(content).send()?; if res.status().is_success() { Ok(self.get_id_from_response(res)) } else { - Err( format!("Failed to import note: {}", res.status()).into() ) + Err(format!("Failed to import note: {}", res.status()).into()) } } } diff --git a/src/main.rs b/src/main.rs index 9c37093..d5b7405 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,12 +32,11 @@ mod mediawiki; mod config_check; - pub mod variables_and_settings; use std::borrow::Cow; -use std::error::Error; use std::env; +use std::error::Error; use std::fs::File; use std::io::Read; // use std::future::Future; @@ -47,9 +46,9 @@ use clap::{Arg, Command}; use regex::Regex; use uuid::Uuid; -use lettre::{Message, SmtpTransport, Transport}; use lettre::message::{header, SinglePart}; use lettre::transport::smtp::authentication::Credentials; +use lettre::{Message, SmtpTransport, Transport}; const FALLBACK_TEMPLATE: &str = variables_and_settings::FALLBACK_TEMPLATE; @@ -86,7 +85,8 @@ fn parse_args() -> Args { .action(clap::ArgAction::Set) .default_value("config.sqlite") .help("specifies an alternate config file"), - ).get_matches(); + ) + .get_matches(); Args { check_mode: *matches.get_one::("check").unwrap(), config_file: matches.get_one::("config_file").unwrap().clone(), @@ -102,12 +102,12 @@ fn main() -> Result<(), Box> { let config = KV::new(config_file).unwrap(); config_check::populate_defaults(&config); if args.check_mode { - return config_check::interactive_check( config ); + return config_check::interactive_check(config); } // config - let hedgedoc = HedgeDoc::new( &config["hedgedoc-server-url"], is_dry_run() ); + let hedgedoc = HedgeDoc::new(&config["hedgedoc-server-url"], is_dry_run()); println!("[START]\nAktueller Zustand der DB:"); - config.dump_redacting(&["email-password","wiki-password","matrix-password"]).ok(); + config.dump_redacting(&["email-password", "wiki-password", "matrix-password"]).ok(); // Dienstage diesen Monat let all_tuesdays: Vec = get_all_weekdays(0, Weekday::Tue); @@ -120,7 +120,7 @@ fn main() -> Result<(), Box> { let vierter_dienstag_nächster_monat: String = all_tuesdays_next_month[3].to_string(); // Daten, die später benutzt werden, definieren - let today= Local::now(); + let today = Local::now(); let today_simple = today.date_naive(); let yesterday = today_simple.pred_opt().unwrap(); let in_1_day = today_simple.succ_opt().unwrap(); @@ -129,23 +129,25 @@ fn main() -> Result<(), Box> { // Nächste Plena nachschauen: - let nächster_plenumtermin: &String = if all_tuesdays[1] >= yesterday { // Für das Pad rumschicken am nächsten Tag wird das Datum einen Tag nach Hinten gesetzt, + let nächster_plenumtermin: &String = if all_tuesdays[1] >= yesterday { + // Für das Pad rumschicken am nächsten Tag wird das Datum einen Tag nach Hinten gesetzt, &zweiter_dienstag - } else { + } else { &vierter_dienstag }; - let übernächster_plenumtermin = if all_tuesdays[1] >= yesterday { // hier das Gleiche. + let übernächster_plenumtermin = if all_tuesdays[1] >= yesterday { + // hier das Gleiche. &vierter_dienstag } else { &zweiter_dienstag_nächster_monat }; - let überübernächster_plenumtermin = if all_tuesdays[1] >= yesterday { // hier das Gleiche. + let überübernächster_plenumtermin = if all_tuesdays[1] >= yesterday { + // hier das Gleiche. &zweiter_dienstag_nächster_monat } else { &vierter_dienstag_nächster_monat }; - // Der Code muss nur für vor dem 2. und vor dem 4. Dienstag gebaut werden, weil im nächsten Monat der Code frühestens 7 Tage vor dem Plenum wieder passt. let in_1_day_is_plenum: bool = check_if_plenum(nächster_plenumtermin.clone(), in_1_day); @@ -153,8 +155,12 @@ fn main() -> Result<(), Box> { let yesterday_was_plenum: bool = check_if_plenum(nächster_plenumtermin.clone(), yesterday); // Pad-Links aus der Datenbank laden: - let current_pad_id = config.get("hedgedoc-last-id").expect("ID des aktuellen Pads undefiniert. Bitte in der DB eintragen oder generieren."); - let future_pad_id = config.get("hedgedoc-next-id").expect("ID des nächsten Pads undefiniert. Bitte in der DB eintragen oder generieren."); + let current_pad_id = config + .get("hedgedoc-last-id") + .expect("ID des aktuellen Pads undefiniert. Bitte in der DB eintragen oder generieren."); + let future_pad_id = config + .get("hedgedoc-next-id") + .expect("ID des nächsten Pads undefiniert. Bitte in der DB eintragen oder generieren."); let mut message_id: Option = None; @@ -172,7 +178,7 @@ fn main() -> Result<(), Box> { if in_1_day_is_plenum { println!("In 1 Tag ist Plenum, deshalb wird eine Erinnerung raus geschickt!"); let tldr_vec = create_tldr(&pad_content_without_top_instructions); - let mut tldr = String::new(); + let mut tldr = String::new(); for element in tldr_vec { tldr.push_str("\n"); tldr.push_str(&element) @@ -180,7 +186,8 @@ fn main() -> Result<(), Box> { if number_of_tops(&pad_content_without_top_instructions) != 0 { // Mail an alle senden, findet statt let betreff = "Morgen ist Plenum!".to_string(); // ADJ_TIMEYWIMEY - let message: String = format!(r#"{email_greeting} + let message: String = format!( + r#"{email_greeting} Es gibt Themen, deshalb wird das morgige Plenum statt finden. Anbei das Plenumspad: {current_pad_link} @@ -190,14 +197,16 @@ Und hier ein TL;DR von den aktuellen Themen: Bis morgen, 20 Uhr! -{email_signature}"#); // ADJ_TIMEYWIMEY +{email_signature}"# + ); // ADJ_TIMEYWIMEY println!("---E-Mail:---\n{}\n-----------", message); // XXX option x expect message_id = Some(mail_versenden(&config, betreff, message).expect("Plenum findet statt. Mail wurde versucht zu senden, konnte aber nicht gesendet werden!")) } else { // Mail an alle senden und absagen let betreff = format!("Plenum am {} fällt mangels Themen aus", nächster_plenumtermin); - let message: String = format!(r#"{email_greeting} + let message: String = format!( + r#"{email_greeting} Es gibt keine Themen, deshalb wird das morgige Plenum leider nicht statt finden. @@ -206,7 +215,9 @@ Hier ist der Link zum Pad vom nächsten Plenum, das am {} statt finden wird: Bis zum nächsten Plenum. -{email_signature}"#, nächster_plenumtermin); +{email_signature}"#, + nächster_plenumtermin + ); println!("---E-Mail:---\n{}\n-----------", message); // XXX option x expect message_id = Some(mail_versenden(&config, betreff, message).expect("Plenum wird abgesagt. Mail wurde versucht zu senden, konnte aber nicht gesendet werden!")) @@ -216,29 +227,38 @@ Bis zum nächsten Plenum. if number_of_tops(&pad_content_without_top_instructions) == 0 { // Mail an alle senden und sagen, dass es noch keine Themen gibt let betreff = format!("Plenum vom {}: Bisher noch keine Themen", nächster_plenumtermin); - let message: String = format!(r#"{email_greeting} + let message: String = format!( + r#"{email_greeting} Es sind bisher leider keine Themen zusammengekommen. Wenn es bis Sonntag Abend keine Themen gibt, wird das Plenum voraussichtlich nicht statt finden. Hier ist der Link zum Pad, wo ihr noch Themen eintragen könnt: {current_pad_link} -{email_signature}"#); +{email_signature}"# + ); println!("---E-Mail:---\n{}\n-----------", message); // XXX option x expect message_id = Some(mail_versenden(&config, betreff, message).expect("Noch nicht genug Themen. Mail wurde versucht zu senden, konnte aber nicht gesendet werden!")) } } else if yesterday_was_plenum { // This logic breaks on 02/2034, but on every other month it works - let old_pad_content = hedgedoc.download(¤t_pad_id).expect("Fehler beim Hedgedoc-Pad-Download!"); + let old_pad_content = + hedgedoc.download(¤t_pad_id).expect("Fehler beim Hedgedoc-Pad-Download!"); // MUSS WIEDER REIN NACH DEM TESTEN: generate_new_pad_for_following_date(übernächster_plenumtermin, überübernächster_plenumtermin, &config).await.expect("Fehler! Plenumspad konnte nicht generiert werden!"); - println!("DATENBANK: aktuelles-plenumspad: {:?} und zukünftiges plenumspad: {:?}", &config.get("hedgedoc-last-id"), &config.get("hedgedoc-next-id")); + println!( + "DATENBANK: aktuelles-plenumspad: {:?} und zukünftiges plenumspad: {:?}", + &config.get("hedgedoc-last-id"), + &config.get("hedgedoc-next-id") + ); - let old_pad_content_without_top_instructions = try_to_remove_top_instructions(old_pad_content); + let old_pad_content_without_top_instructions = + try_to_remove_top_instructions(old_pad_content); let tldr_vec = create_tldr(&old_pad_content_without_top_instructions); - let tldr = tldr_vec.join("\n"); + let tldr = tldr_vec.join("\n"); // XXX nächstes/übernächstes? - let message: String = format!(r#"{email_greeting} + let message: String = format!( + r#"{email_greeting} Anbei das gestrige Plenumspad. Hier sind die Links zum nächsten: {current_pad_link} @@ -252,8 +272,10 @@ Und hier ist das Protokoll des letzten Plenums: {old_pad_content_without_top_instructions} -{email_signature}"#); - let betreff: String = format!("Plenumsprotokoll vom {}: Es gab {} TOPs", nächster_plenumtermin, top_anzahl); +{email_signature}"# + ); + let betreff: String = + format!("Plenumsprotokoll vom {}: Es gab {} TOPs", nächster_plenumtermin, top_anzahl); println!("---E-Mail:---\n{}\n-----------", message); // XXX option x expect message_id = Some(mail_versenden(&config, betreff, message).expect("Mail mit Plenumsprotokoll wurde versucht zu senden, konnte aber nicht gesendet werden!")); @@ -262,19 +284,24 @@ Und hier ist das Protokoll des letzten Plenums: println!("message id: {:?}", message_id); println!("[ENDE]\nAktueller Zustand der DB:"); - config.dump_redacting(&["email-password","matrix-password","wiki-password"]).ok(); - - if config.has_errors() { Err("There were errors.".into()) } else { Ok(()) } + config.dump_redacting(&["email-password", "matrix-password", "wiki-password"]).ok(); + + if config.has_errors() { + Err("There were errors.".into()) + } else { + Ok(()) + } } -fn get_all_weekdays(month_offset: i32, week_day: Weekday) -> Vec{ +fn get_all_weekdays(month_offset: i32, week_day: Weekday) -> Vec { let date = Local::now().date_naive(); let date = match month_offset.signum() { - 0 => Some(date), - 1 => date.checked_add_months(chrono::Months::new(month_offset as u32)), + 0 => Some(date), + 1 => date.checked_add_months(chrono::Months::new(month_offset as u32)), -1 => date.checked_sub_months(chrono::Months::new((-month_offset) as u32)), - _ => unreachable!(), - }.expect("(very) invalid month offset"); + _ => unreachable!(), + } + .expect("(very) invalid month offset"); let month = date.month(); let mut current_date = NaiveDate::from_ymd_opt(date.year(), date.month(), 1); let mut dates = Vec::new(); @@ -290,30 +317,28 @@ fn get_all_weekdays(month_offset: i32, week_day: Weekday) -> Vec{ fn check_if_plenum(infrage_kommendes_plenum: String, date_to_check: NaiveDate) -> bool { // Überprüfen, ob an dem Datum Plenum ist let date_to_check = date_to_check.to_string(); - return if infrage_kommendes_plenum == date_to_check { - true - } else { - false - } + return if infrage_kommendes_plenum == date_to_check { true } else { false }; } -fn number_of_tops (pad_content: &String) -> i32 { +fn number_of_tops(pad_content: &String) -> i32 { // Logik: Wenn irgendwo TOP 2, top2, Top2 oder TOP2 steht, dann gibt es Themen let re = Regex::new(r"^##+ *([Tt][Oo][Pp] +\d[.\d]*.*)$").unwrap(); let m = re.find(&pad_content); m.iter().len() as i32 } -fn create_tldr (pad_content: &String) -> Vec<&str> { +fn create_tldr(pad_content: &String) -> Vec<&str> { // Logik: Wenn irgendwo TOP 2, top2, Top2 oder TOP2 steht, dann gibt es Themen let re_top = Regex::new(r"^##+ *([Tt][Oo][Pp] +\d[.\d]*.*)$").unwrap(); - let tldr: Vec<&str> = re_top.find_iter(&pad_content).map(|m|m.as_str()).collect(); + let tldr: Vec<&str> = re_top.find_iter(&pad_content).map(|m| m.as_str()).collect(); //println!("{:?}", m); tldr //tldr_vec.append() = m.iter() } -fn mail_versenden(config: &KV, betreff: String, inhalt: String) -> std::result::Result> { +fn mail_versenden( + config: &KV, betreff: String, inhalt: String, +) -> std::result::Result> { // Define the email let message_id: String = Uuid::new_v4().to_string() + &String::from("@berlin.ccc.de"); @@ -328,59 +353,74 @@ fn mail_versenden(config: &KV, betreff: String, inhalt: String) -> std::result:: .in_reply_to("19de3985-05b4-42f3-9a6a-e941479be2ed@berlin.ccc.de".to_string()) // Set the subject of the email .subject(betreff) - .singlepart(SinglePart::builder() - .header(header::ContentType::TEXT_PLAIN) - .body(inhalt)) + .singlepart(SinglePart::builder().header(header::ContentType::TEXT_PLAIN).body(inhalt)) .unwrap(); if !is_dry_run() { // Set up the SMTP client - let creds = Credentials::new(config["email-user"].to_string(), config["email-password"].to_string()); + let creds = Credentials::new( + config["email-user"].to_string(), + config["email-password"].to_string(), + ); // Open a remote connection to gmail - let mailer = SmtpTransport::starttls_relay(&config["email-server"])? - .credentials(creds) - .build(); + let mailer = + SmtpTransport::starttls_relay(&config["email-server"])?.credentials(creds).build(); // Send the email match mailer.send(&email) { Ok(_) => println!("Email sent successfully!"), - Err(e) => eprintln!("Could not send email: {:?}", e), } + Err(e) => eprintln!("Could not send email: {:?}", e), + } Ok(message_id) } else { - println!( "[DRY RUN - NOT] sending email\n(raw message:)\n{}", std::str::from_utf8(&email.formatted()).unwrap_or("((UTF-8 error))") ); + println!( + "[DRY RUN - NOT] sending email\n(raw message:)\n{}", + std::str::from_utf8(&email.formatted()).unwrap_or("((UTF-8 error))") + ); Ok("dummy-message-id@localhost".to_string()) } } -fn generate_new_pad_for_following_date(config : KV, hedgedoc : HedgeDoc, übernächster_plenumtermin: &String, überübernächster_plenumtermin: &String, kv: &KV) -> Result<(), Box> { - - match hedgedoc.create_pad( ) { +fn generate_new_pad_for_following_date( + config: KV, hedgedoc: HedgeDoc, übernächster_plenumtermin: &String, + überübernächster_plenumtermin: &String, kv: &KV, +) -> Result<(), Box> { + match hedgedoc.create_pad() { Err(e) => println!("Failed to create pad: {}", e), Ok(pad_id) => { println!("Pad created successfully with ID: {}", pad_id); // Get the most recent plenum template and replace the placeholders: let template_content: String = match config.get("hedgedoc-template-name") { - Ok(content) => hedgedoc.download(&content.clone()).unwrap_or_else(|_| FALLBACK_TEMPLATE.to_string()), + Ok(content) => hedgedoc + .download(&content.clone()) + .unwrap_or_else(|_| FALLBACK_TEMPLATE.to_string()), Err(_) => FALLBACK_TEMPLATE.to_string(), }; // XXX you don't just use the template as-is… - let template_modified: String = replace_placeholders(&template_content, übernächster_plenumtermin, überübernächster_plenumtermin).unwrap_or_else(|error |template_content); // Try regex, if not successful use without regex + let template_modified: String = replace_placeholders( + &template_content, + übernächster_plenumtermin, + überübernächster_plenumtermin, + ) + .unwrap_or_else(|error| template_content); // Try regex, if not successful use without regex - match hedgedoc.import_note( Some(&pad_id), template_modified ) { + match hedgedoc.import_note(Some(&pad_id), template_modified) { Ok(_) => { println!("Pad updated successfully with template content."); - rotate (&pad_id, kv); + rotate(&pad_id, kv); }, Err(e) => println!("Failed to update pad: {}", e), } - } + }, } Ok(()) } -fn replace_placeholders(template: &str, übernächster_plenumtermin: &str, überübernächster_plenumtermin: &str) -> Result> { +fn replace_placeholders( + template: &str, übernächster_plenumtermin: &str, überübernächster_plenumtermin: &str, +) -> Result> { let re_datum = Regex::new(r"\{\{Datum\}\}")?; let result = re_datum.replace_all(template, übernächster_plenumtermin); let re_naechstes_plenum = Regex::new(r"\{\{naechstes-plenum\}\}")?; @@ -388,18 +428,23 @@ fn replace_placeholders(template: &str, übernächster_plenumtermin: &str, über Ok(result.to_string()) } -fn rotate (future_pad_id: &str, kv: &KV) { +fn rotate(future_pad_id: &str, kv: &KV) { let next_plenum_pad = kv.get("zukünftiges-plenumspad").ok(); if next_plenum_pad == None { - kv.set("zukünftiges-plenumspad", &future_pad_id).expect("Fehler beim Beschreiben der Datenbank mit neuem Plenumslink!"); // Beispiel: aktuelles-plenumspad: Ok(Some("eCH24zXGS9S8Stg5xI3aRg")) + kv.set("zukünftiges-plenumspad", &future_pad_id) + .expect("Fehler beim Beschreiben der Datenbank mit neuem Plenumslink!"); + // Beispiel: aktuelles-plenumspad: Ok(Some("eCH24zXGS9S8Stg5xI3aRg")) } else { - kv.set("aktuelles-plenumspad", &next_plenum_pad.unwrap()).expect("Fehler beim Beschreiben der Datenbank mit neuem Plenumslink!"); // Beispiel: aktuelles-plenumspad: Ok(Some("eCH24zXGS9S8Stg5xI3aRg")) - kv.set("zukünftiges-plenumspad", &future_pad_id).expect("Fehler beim Beschreiben der Datenbank mit neuem Plenumslink!"); // Beispiel: aktuelles-plenumspad: Ok(Some("eCH24zXGS9S8Stg5xI3aRg")) + kv.set("aktuelles-plenumspad", &next_plenum_pad.unwrap()) + .expect("Fehler beim Beschreiben der Datenbank mit neuem Plenumslink!"); // Beispiel: aktuelles-plenumspad: Ok(Some("eCH24zXGS9S8Stg5xI3aRg")) + kv.set("zukünftiges-plenumspad", &future_pad_id) + .expect("Fehler beim Beschreiben der Datenbank mit neuem Plenumslink!"); + // Beispiel: aktuelles-plenumspad: Ok(Some("eCH24zXGS9S8Stg5xI3aRg")) } } -fn try_to_remove_top_instructions (pad_content: String) -> String { +fn try_to_remove_top_instructions(pad_content: String) -> String { let re_top_instructions: Regex = Regex::new(r"()").unwrap(); let result: Cow = re_top_instructions.replace_all(&pad_content, "---"); result.to_string() // Wenn es nicht geklappt hat, wird einfach das Pad mit dem Kommentar zurückgegeben -} \ No newline at end of file +} diff --git a/src/mediawiki.rs b/src/mediawiki.rs index 17ba2d5..59fd025 100644 --- a/src/mediawiki.rs +++ b/src/mediawiki.rs @@ -6,18 +6,20 @@ pub fn pad_ins_wiki(old_pad_content: String) { convert_markdown_to_mediawiki_and_save_as_txt(old_pad_content); // Textdatei wieder einlesen - let mut file = File::open("pandoc-output.txt").expect("Fehler beim öffnen der MediaWiki-Textdatei!"); + let mut file = + File::open("pandoc-output.txt").expect("Fehler beim öffnen der MediaWiki-Textdatei!"); let mut contents = String::new(); file.read_to_string(&mut contents).expect("Fehler beim auslesen der MediaWiki-Textdatei!"); // Passwörter aus Datenbank lesen (ToBeDone) let plenum_bot_user = String::from("PlenumBot@PlenumBot-PW1"); let plenum_bot_pw = String::from("**OLD_API_PW_REMOVED**"); - let login_token = login_to_mediawiki(plenum_bot_user.clone(), plenum_bot_pw.clone()).expect("Fehler beim Einloggen!"); + let login_token = login_to_mediawiki(plenum_bot_user.clone(), plenum_bot_pw.clone()) + .expect("Fehler beim Einloggen!"); println!("plenum_bot_user: {plenum_bot_user}, plenum_bot_pw: {plenum_bot_pw}, login_token: {login_token}") } -fn convert_markdown_to_mediawiki_and_save_as_txt (old_pad_content: String) { +fn convert_markdown_to_mediawiki_and_save_as_txt(old_pad_content: String) { //Convert Markdown into Mediawiki // Vanilla pandoc Befehl: pandoc --from markdown --to mediawiki --no-highlight let mut p = pandoc::new(); @@ -25,10 +27,13 @@ fn convert_markdown_to_mediawiki_and_save_as_txt (old_pad_content: String) { p.set_input_format(pandoc::InputFormat::Markdown, vec![]); p.set_output(pandoc::OutputKind::File("./pandoc-output.txt".parse().unwrap())); p.set_output_format(pandoc::OutputFormat::MediaWiki, vec![]); - p.execute().expect("Fehler beim Umwandeln des und speichern des Pads in eine mediawiki-Textdatei"); + p.execute() + .expect("Fehler beim Umwandeln des und speichern des Pads in eine mediawiki-Textdatei"); } -fn login_to_mediawiki (plenum_bot_user: String, plenum_bot_pw: String) -> Result> { +fn login_to_mediawiki( + plenum_bot_user: String, plenum_bot_pw: String, +) -> Result> { //let mut map = HashMap::new(); //map.insert("logintoken", "result"); let username = "cccb-wiki"; @@ -50,7 +55,7 @@ fn login_to_mediawiki (plenum_bot_user: String, plenum_bot_pw: String) -> Result let html_source = resp.text()?; //let login_token: String = map.get("logintoken").unwrap().to_string().clone(); println!("---HTML:---\n{}\n-----------", html_source); - + */ Ok(String::from("unimplemented")) -} \ No newline at end of file +} diff --git a/src/variables_and_settings.rs b/src/variables_and_settings.rs index 5a04652..b855273 100644 --- a/src/variables_and_settings.rs +++ b/src/variables_and_settings.rs @@ -40,4 +40,4 @@ Nächstes Plenum: Am 2024-08-13 um 20 Uhr ---"#; pub const PLENUM_TEMPLATE_URL: &str = "https://md.berlin.ccc.de/plenum-template/download"; -pub const HEDGEDOC_SERVER_URL: &str = "https://md.berlin.ccc.de"; \ No newline at end of file +pub const HEDGEDOC_SERVER_URL: &str = "https://md.berlin.ccc.de";