From 2da4a149c9df93efa5d5199841a5dd162e946bd0 Mon Sep 17 00:00:00 2001 From: Marek Krug Date: Sun, 18 Aug 2024 01:22:06 +0200 Subject: [PATCH 01/10] mediawiki: new features - Updated new_wiki_page - New update_plenum_page function to download the main Plenum Page from Mediawiki, insert the Link to the new Page and replace the content of the mediawiki - new get-page functions - new edit-selection functions (Wiki still in progress) --- src/main.rs | 1 + src/mediawiki.rs | 120 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 114 insertions(+), 7 deletions(-) diff --git a/src/main.rs b/src/main.rs index d3dcc04..3e92654 100644 --- a/src/main.rs +++ b/src/main.rs @@ -171,6 +171,7 @@ fn main() -> Result<(), Box> { &config["wiki-api-user"], &config["wiki-api-secret"], is_dry_run(), + &config["wiki-plenum-page"], ); trace_var_!(wiki); // get next plenum days diff --git a/src/mediawiki.rs b/src/mediawiki.rs index 31b062f..1de7a41 100644 --- a/src/mediawiki.rs +++ b/src/mediawiki.rs @@ -36,10 +36,15 @@ pub const CONFIG: CfgGroup<'static> = CfgGroup { key: "api-secret", description: "API secret / \"password\" used for authenticating as the bot.", }, + CfgField::Default { + key: "plenum-page", + default: "Plenum", + description: "The name of the wiki page where all new plenum pages will be linked.", + }, CfgField::Default { key: "eta", default: "no ETA, program never ran", - description: "ETA message for estimating time the program takes" + description: "ETA message for estimating time the program takes." } ], }; @@ -53,6 +58,7 @@ pub struct Mediawiki { is_dry_run: bool, login_token: String, csrf_token: String, + plenum_main_page_name: String, client: Client, } @@ -76,7 +82,7 @@ pub enum RequestType { impl Mediawiki { pub fn new( - server_url: &str, http_auth_user: &str, http_auth_password: &str, api_user: &str, api_secret: &str, is_dry_run: bool, + server_url: &str, http_auth_user: &str, http_auth_password: &str, api_user: &str, api_secret: &str, is_dry_run: bool, plenum_main_page_name: &str, ) -> Self { Self { server_url: server_url.to_string(), @@ -87,6 +93,7 @@ impl Mediawiki { is_dry_run, login_token: String::new(), csrf_token: String::new(), + plenum_main_page_name: plenum_main_page_name.to_string(), client: Client::builder().cookie_store(true).build().unwrap(), } } @@ -177,14 +184,80 @@ impl Mediawiki { let url = format!("{}/api.php?", self.server_url); let params: Box<[(&str, &str)]> = Box::from([ - ("action", "edit"), // Create and edit pages. + ("action", "edit"), // Create and edit pages. ("format", "json"), - ("title", page_title), // Title of the page to edit. Cannot be used together with pageid. - ("appendtext", page_content), // Add this text to the end of the page or section. Overrides text. + ("title", page_title), // Title of the page to edit. Cannot be used together with pageid. + ("text", page_content), // Add this text to the end of the page or section. Overrides text. ("token", self.csrf_token.as_str()), // A "csrf" token retrieved from action=query&meta=tokens ("bot", "true"), // Mark this edit as a bot edit. ]); - self.make_request(url, params, RequestType::Post) + let request_result = self.make_request(url, params, RequestType::Post); + + self.update_plenum_page(page_title)?; + + request_result + } + /// Downloads the main Plenum Page from Mediawiki, inserts the Link to the new Page and replaces the content of the mediawiki- + pub fn update_plenum_page (&self, new_page_title_to_link_to: &str) -> Result<(), Box> { + // 1. Download Plenum page content + let page_content = self.get_page_content(&self.plenum_main_page_name)?; + println!("---\nPage Content: {}\n---", page_content.red()); + let current_year = "2024"; // TODO: Datumslogik einbauen + let year_section = format!("=== {} ===\n", current_year); + if page_content.contains(&year_section) { + let mut content_split: Vec<&str> = page_content.split(&year_section).collect(); + println!("Length: {}", content_split.len()); + let rest_of_content = content_split.pop().unwrap_or_default(); + let updated_section = format!("{}{}\n* {}", content_split.join(&year_section), year_section, new_page_title_to_link_to); + //format!("{}{}", updated_section, rest_of_content) + } + Ok(()) + } + pub fn get_page_content (&self, page_title: &str) -> Result> { + let url = + format!("{}/api.php?", self.server_url); + let params: Box<[(&str, &str)]> = Box::from([ + ("action", "parse"), // Create and edit pages. + ("prop", "wikitext"), + ("format", "json"), + ("page", page_title), + ("formatversion", "2"), + ]); + let resp: Result> = self.make_request(url, params, RequestType::Get); + let resp = resp?; + let response_deserialized: ParseResponse = serde_json::from_str(&resp)?; + Ok(response_deserialized.parse.wikitext) + } + pub fn get_page_section_title (&self, page_title: &str, section_number: &str) -> Result> { + let url = + format!("{}/api.php?", self.server_url); + let params: Box<[(&str, &str)]> = Box::from([ + ("action", "parse"), // Create and edit pages. + ("contentmodel", "wikitext"), + ("format", "json"), + ("page", page_title), + ]); + let resp: Result> = self.make_request(url, params, RequestType::Get); + let resp = resp?; + todo!() + //let response_deserialized = serde_json::from_str(&resp)?; + //Ok(response_deserialized["parse"]) + } + pub fn edit_section (&self, page_title: &str, text_to_prepend: &str, section_number: &str) -> Result> { + let url = + format!("{}/api.php?", self.server_url); + let params: Box<[(&str, &str)]> = Box::from([ + ("action", "edit"), // Create and edit pages. + ("format", "json"), + ("title", page_title), // Title of the page to edit. Cannot be used together with pageid. + ("section", section_number), // Section identifier. 0 for the top section, new for a new section. Often a positive integer, but can also be non-numeric + ("prependtext", text_to_prepend), // Add this text to the end of the page or section. Overrides text. + ("token", self.csrf_token.as_str()), // A "csrf" token retrieved from action=query&meta=tokens + ("bot", "true"), // Mark this edit as a bot edit. + ]); + let request_result = self.make_request(url, params, RequestType::Post); + + request_result } } @@ -219,6 +292,28 @@ struct QueryTokensCsrf { struct TokensCsrf { csrftoken: String, } +// For get_page_content: +#[derive(Deserialize)] +struct ParseResponse { + parse: ParseContent, +} + +#[derive(Deserialize)] +struct ParseContent { + wikitext: String, +} +// For get_page_section: +/* +#[derive(Deserialize)] +struct ParseSectionResponse { + parse: ParseSectionContent, +} +#[derive(Deserialize)] +struct ParseSectionContent { + sections: , +} + + */ pub fn pad_ins_wiki(old_pad_content: String, wiki: &mut Mediawiki) -> Result<(), Box> { // Login to Wiki and get required tokens for logging in and writing @@ -239,7 +334,10 @@ pub fn pad_ins_wiki(old_pad_content: String, wiki: &mut Mediawiki) -> Result<(), // Convert to mediawiki and make new page let pad_converted = convert_md_to_mediawiki(old_pad_content); println!("Das kommt ins Wiki: {}", pad_converted); - //wiki.new_wiki_page("Page Test 5", &pad_converted)?; JUST AN EXAMPLE + let page_title = "Page Test 5"; + let page_title = format!("{}/{}", wiki.plenum_main_page_name, page_title); // Example: Plenum/13._August_2024 + wiki.new_wiki_page(&page_title, &pad_converted)?; + // Textdatei wieder einlesen @@ -291,3 +389,11 @@ fn convert_md_to_mediawiki(old_pad_content: String) -> String { println!("TEMP: {}", temp.purple()); temp } + +/* +fn create_title (nächster_plenumstermin: String) { + let date_simple = NaiveDate::from(nächster_plenumstermin); + let wiki_page_title = format!("{} {} {}", date_simple.day(), LongMonthName[date_simple.month()], date_simple.year()); +} + + */ From 12450ce5a3ccceaf23b955eceaeee50e5478ee1b Mon Sep 17 00:00:00 2001 From: murmeldin Date: Sun, 25 Aug 2024 18:15:03 +0200 Subject: [PATCH 02/10] mediawiki mutable in main.rs, less unwrap in mediawiki.rs --- src/main.rs | 23 ++++---- src/mediawiki.rs | 139 +++++++++++++++++++---------------------------- 2 files changed, 69 insertions(+), 93 deletions(-) diff --git a/src/main.rs b/src/main.rs index 3e92654..cee6b2b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -164,7 +164,7 @@ fn main() -> Result<(), Box> { config.get("email-in-reply-to").ok(), ); trace_var_!(email); - let wiki = Mediawiki::new( + let mut wiki = Mediawiki::new( &config["wiki-server-url"], &config["wiki-http-user"], &config["wiki-http-password"], @@ -191,7 +191,7 @@ fn main() -> Result<(), Box> { if !matches!(last_state, ProgramState::Normal) { eprintln!("WARNING: last run was a long time ago, resetting state."); last_state = ProgramState::Normal; - do_cleanup(999, &today, &config, &hedgedoc, &email, &wiki)?; + do_cleanup(999, &today, &config, &hedgedoc, &email, &mut wiki)?; // reset will have cleared in-reply-to if it existed let (base, from, to, _) = email.into_parts(); email = SimpleEmail::from_parts(base, from, to, config.get("email-in-reply-to").ok()); @@ -241,7 +241,7 @@ fn main() -> Result<(), Box> { let action: &ST = &TRANSITION_LUT[last_state as usize][intended_state as usize]; verboseln!("Notewendige Aktionen: {}", action.to_string().cyan()); // run action, which is responsible for updating the state as needed - action.get()(delta, &plenum_day, &config, &hedgedoc, &email, &wiki)?; + action.get()(delta, &plenum_day, &config, &hedgedoc, &email, &mut wiki)?; // shutdown config.set("state-last-run", &today.to_string())?; @@ -372,7 +372,7 @@ fn get_pad_info(config: &KV, hedgedoc: &HedgeDoc) -> (String, String, String, us fn do_announcement( ttp: i64, plenum_day: &NaiveDate, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, - _wiki: &Mediawiki, + _wiki: &mut Mediawiki, ) -> Result<(), Box> { NYI!("trace/verbose annotations"); // fetch current pad contents & summarize @@ -405,7 +405,7 @@ fn do_announcement( fn do_reminder( ttp: i64, plenum_day: &NaiveDate, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, - _wiki: &Mediawiki, + _wiki: &mut Mediawiki, ) -> Result<(), Box> { NYI!("trace/verbose annotations"); // fetch current pad contents & summarize @@ -459,7 +459,7 @@ fn do_reminder( #[allow(unused_variables)] fn do_protocol( ttp: i64, plenum_day: &NaiveDate, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, - wiki: &Mediawiki, + wiki: &mut Mediawiki, ) -> Result<(), Box> { NYI!("trace/verbose annotations"); let (current_pad_id, pad_content, toc, n_topics) = get_pad_info(config, hedgedoc); @@ -476,6 +476,7 @@ fn do_protocol( ); let _message_id = send_email(&subject, &body, email, config)?; NYI!("convert to mediawiki"); + mediawiki::pad_ins_wiki(pad_content, wiki)?; NYI!("add to wiki"); config.set("state-name", &ProgramState::Logged.to_string()).ok(); } else { @@ -489,7 +490,7 @@ fn do_protocol( #[allow(unused_must_use)] fn do_cleanup( _ttp: i64, _plenum_day: &NaiveDate, config: &KV, _hedgedoc: &HedgeDoc, _email: &SimpleEmail, - _wiki: &Mediawiki, + _wiki: &mut Mediawiki, ) -> Result<(), Box> { NYI!("trace/verbose annotations"); config.delete("state-toc"); @@ -508,7 +509,7 @@ type TransitionFunction = fn( config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, - wiki: &Mediawiki, + wiki: &mut Mediawiki, ) -> Result<(), Box>; #[rustfmt::skip] @@ -555,14 +556,14 @@ impl Display for ST { } fn nop( - _: i64, _: &NaiveDate, _: &KV, _: &HedgeDoc, _: &SimpleEmail, _: &Mediawiki, + _: i64, _: &NaiveDate, _: &KV, _: &HedgeDoc, _: &SimpleEmail, _: &mut Mediawiki, ) -> Result<(), Box> { Ok(()) } fn do_clean_announcement( ttp: i64, plenum_day: &NaiveDate, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, - wiki: &Mediawiki, + wiki: &mut Mediawiki, ) -> Result<(), Box> { do_cleanup(ttp, plenum_day, config, hedgedoc, email, wiki)?; do_announcement(ttp, plenum_day, config, hedgedoc, email, wiki) @@ -570,7 +571,7 @@ fn do_clean_announcement( fn do_clean_reminder( ttp: i64, plenum_day: &NaiveDate, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, - wiki: &Mediawiki, + wiki: &mut Mediawiki, ) -> Result<(), Box> { do_cleanup(ttp, plenum_day, config, hedgedoc, email, wiki)?; do_reminder(ttp, plenum_day, config, hedgedoc, email, wiki) diff --git a/src/mediawiki.rs b/src/mediawiki.rs index 1de7a41..82b067f 100644 --- a/src/mediawiki.rs +++ b/src/mediawiki.rs @@ -6,6 +6,7 @@ use colored::Colorize; use pandoc::{PandocError, PandocOutput}; use reqwest::blocking::Client; use serde::Deserialize; +use serde_json::json; use crate::config_spec::{CfgField, CfgGroup}; @@ -74,7 +75,7 @@ impl std::fmt::Debug for Mediawiki { } } -pub enum RequestType { +pub enum ValidRequestTypes { Get, Post, PostForEditing @@ -106,7 +107,7 @@ impl Mediawiki { ("type", "login"), ("action", "query") ]); - let resp = self.make_request(url, params, RequestType::Get).unwrap(); + let resp = self.make_request(url, params, ValidRequestTypes::Get)?; let response_deserialized: QueryResponseLogin = serde_json::from_str(&resp)?; Ok(response_deserialized.query.tokens.logintoken) } @@ -118,8 +119,8 @@ impl Mediawiki { ("lgtoken", &self.login_token), ("action", "login") ]); - let resp: Result> = self.make_request(url, params, RequestType::Post); - Ok(resp.unwrap()) + let resp: Result> = self.make_request(url, params, ValidRequestTypes::Post); + Ok(resp?) } pub fn get_csrf_token(&self) -> Result> { // HAS TO BE FIXED let url = @@ -130,16 +131,15 @@ impl Mediawiki { ("formatversion", "2"), ("action", "query") ]); - let resp: Result> = self.make_request(url, params, RequestType::Get); - let resp = resp.unwrap(); + let resp: String = self.make_request(url, params, ValidRequestTypes::Get)?; let response_deserialized: QueryResponseCsrf = serde_json::from_str(&resp)?; Ok(response_deserialized.query.tokens.csrftoken) } - pub fn make_request(&self, url: String, params: Box<[(&str, &str)]>, request_type: RequestType) -> Result> { + pub fn make_request(&self, url: String, params: Box<[(&str, &str)]>, request_type: ValidRequestTypes) -> Result> { let resp: Result> = match match request_type { - RequestType::Get => { + ValidRequestTypes::Get => { self .client .get(url) @@ -147,7 +147,7 @@ impl Mediawiki { .query(¶ms) .send() } - RequestType::Post | RequestType::PostForEditing => { + ValidRequestTypes::Post | ValidRequestTypes::PostForEditing => { self .client .post(url) @@ -160,8 +160,8 @@ impl Mediawiki { Ok(response) => { if response.status().is_success() { match request_type { - RequestType::PostForEditing => Ok(response.text().unwrap()), - _ => Ok(response.text().unwrap()) + ValidRequestTypes::PostForEditing => Ok(response.text()?), + _ => Ok(response.text()?) } } else { @@ -191,7 +191,7 @@ impl Mediawiki { ("token", self.csrf_token.as_str()), // A "csrf" token retrieved from action=query&meta=tokens ("bot", "true"), // Mark this edit as a bot edit. ]); - let request_result = self.make_request(url, params, RequestType::Post); + let request_result = self.make_request(url, params, ValidRequestTypes::Post); self.update_plenum_page(page_title)?; @@ -223,10 +223,10 @@ impl Mediawiki { ("page", page_title), ("formatversion", "2"), ]); - let resp: Result> = self.make_request(url, params, RequestType::Get); + let resp: Result> = self.make_request(url, params, ValidRequestTypes::Get); let resp = resp?; - let response_deserialized: ParseResponse = serde_json::from_str(&resp)?; - Ok(response_deserialized.parse.wikitext) + let resp = json!(resp); + Ok(resp["parse"]["wikitext"].to_string()) } pub fn get_page_section_title (&self, page_title: &str, section_number: &str) -> Result> { let url = @@ -237,7 +237,7 @@ impl Mediawiki { ("format", "json"), ("page", page_title), ]); - let resp: Result> = self.make_request(url, params, RequestType::Get); + let resp: Result> = self.make_request(url, params, ValidRequestTypes::Get); let resp = resp?; todo!() //let response_deserialized = serde_json::from_str(&resp)?; @@ -255,80 +255,22 @@ impl Mediawiki { ("token", self.csrf_token.as_str()), // A "csrf" token retrieved from action=query&meta=tokens ("bot", "true"), // Mark this edit as a bot edit. ]); - let request_result = self.make_request(url, params, RequestType::Post); + let request_result = self.make_request(url, params, ValidRequestTypes::Post); request_result } } -#[derive(Deserialize)] -struct QueryResponseLogin { - batchcomplete: String, - query: QueryTokensLogin, -} - -#[derive(Deserialize)] -struct QueryTokensLogin { - tokens: TokensLogin, -} - -#[derive(Deserialize)] -struct TokensLogin { - logintoken: String, -} - -#[derive(Deserialize)] -struct QueryResponseCsrf { - batchcomplete: bool, - query: crate::mediawiki::QueryTokensCsrf, -} - -#[derive(Deserialize)] -struct QueryTokensCsrf { - tokens: crate::mediawiki::TokensCsrf, -} - -#[derive(Deserialize)] -struct TokensCsrf { - csrftoken: String, -} -// For get_page_content: -#[derive(Deserialize)] -struct ParseResponse { - parse: ParseContent, -} - -#[derive(Deserialize)] -struct ParseContent { - wikitext: String, -} -// For get_page_section: -/* -#[derive(Deserialize)] -struct ParseSectionResponse { - parse: ParseSectionContent, -} -#[derive(Deserialize)] -struct ParseSectionContent { - sections: , -} - - */ - pub fn pad_ins_wiki(old_pad_content: String, wiki: &mut Mediawiki) -> Result<(), Box> { // Login to Wiki and get required tokens for logging in and writing let auth_result = wiki.get_login_token()?; - wiki.login_token.clone_from(&auth_result); - println!("AUTH Success"); + wiki.login_token.clone_from(&auth_result); // Copy the login token to the struct + println!("AUTH Done"); let login_result = wiki.login()?; - println!("LOGIN Success"); - let csrf_token = wiki.get_csrf_token(); - let csrf_token = csrf_token.unwrap_or_else(|e| { - println!("Error while trying to get csrf: {:?}", e); - String::new() - }); - println!("CSRF Success"); - wiki.csrf_token.clone_from(&csrf_token); + println!("LOGIN Done"); + let csrf_token = wiki.get_csrf_token()?; + println!("CSRF Done"); + wiki.csrf_token.clone_from(&csrf_token); // Copy the csrf token to the struct println!("---AUTH RESULT:---\n{}\n---LOGIN RESULT:---\n{:?}\n---CSRF RESULT:---\n{}\n-----------", auth_result, login_result, csrf_token); // Convert to mediawiki and make new page @@ -338,7 +280,6 @@ pub fn pad_ins_wiki(old_pad_content: String, wiki: &mut Mediawiki) -> Result<(), let page_title = format!("{}/{}", wiki.plenum_main_page_name, page_title); // Example: Plenum/13._August_2024 wiki.new_wiki_page(&page_title, &pad_converted)?; - // Textdatei wieder einlesen // Passwörter aus Datenbank lesen (ToBeDone) @@ -397,3 +338,37 @@ fn create_title (nächster_plenumstermin: String) { } */ + +// This has to be defined that way, because both in the login and csrf token, +// the response contains two \\ characters which break the usual deserialization +#[derive(Deserialize)] +struct QueryResponseLogin { + batchcomplete: String, + query: QueryTokensLogin, +} + +#[derive(Deserialize)] +struct QueryTokensLogin { + tokens: TokensLogin, +} + +#[derive(Deserialize)] +struct TokensLogin { + logintoken: String, +} + +#[derive(Deserialize)] +struct QueryResponseCsrf { + batchcomplete: bool, + query: crate::mediawiki::QueryTokensCsrf, +} + +#[derive(Deserialize)] +struct QueryTokensCsrf { + tokens: crate::mediawiki::TokensCsrf, +} + +#[derive(Deserialize)] +struct TokensCsrf { + csrftoken: String, +} \ No newline at end of file From 360883894949fcca5769cbd4af6e1b283af5b9e0 Mon Sep 17 00:00:00 2001 From: nobody Date: Sun, 25 Aug 2024 20:25:29 +0200 Subject: [PATCH 03/10] mediawiki: use OnceCell instead of mut login_token and csrf_token are internally mutable now --- src/main.rs | 22 ++++++++--------- src/mediawiki.rs | 64 ++++++++++++++++++++---------------------------- 2 files changed, 38 insertions(+), 48 deletions(-) diff --git a/src/main.rs b/src/main.rs index cee6b2b..a351283 100644 --- a/src/main.rs +++ b/src/main.rs @@ -164,7 +164,7 @@ fn main() -> Result<(), Box> { config.get("email-in-reply-to").ok(), ); trace_var_!(email); - let mut wiki = Mediawiki::new( + let wiki = Mediawiki::new( &config["wiki-server-url"], &config["wiki-http-user"], &config["wiki-http-password"], @@ -191,7 +191,7 @@ fn main() -> Result<(), Box> { if !matches!(last_state, ProgramState::Normal) { eprintln!("WARNING: last run was a long time ago, resetting state."); last_state = ProgramState::Normal; - do_cleanup(999, &today, &config, &hedgedoc, &email, &mut wiki)?; + do_cleanup(999, &today, &config, &hedgedoc, &email, &wiki)?; // reset will have cleared in-reply-to if it existed let (base, from, to, _) = email.into_parts(); email = SimpleEmail::from_parts(base, from, to, config.get("email-in-reply-to").ok()); @@ -241,7 +241,7 @@ fn main() -> Result<(), Box> { let action: &ST = &TRANSITION_LUT[last_state as usize][intended_state as usize]; verboseln!("Notewendige Aktionen: {}", action.to_string().cyan()); // run action, which is responsible for updating the state as needed - action.get()(delta, &plenum_day, &config, &hedgedoc, &email, &mut wiki)?; + action.get()(delta, &plenum_day, &config, &hedgedoc, &email, &wiki)?; // shutdown config.set("state-last-run", &today.to_string())?; @@ -372,7 +372,7 @@ fn get_pad_info(config: &KV, hedgedoc: &HedgeDoc) -> (String, String, String, us fn do_announcement( ttp: i64, plenum_day: &NaiveDate, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, - _wiki: &mut Mediawiki, + _wiki: &Mediawiki, ) -> Result<(), Box> { NYI!("trace/verbose annotations"); // fetch current pad contents & summarize @@ -405,7 +405,7 @@ fn do_announcement( fn do_reminder( ttp: i64, plenum_day: &NaiveDate, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, - _wiki: &mut Mediawiki, + _wiki: &Mediawiki, ) -> Result<(), Box> { NYI!("trace/verbose annotations"); // fetch current pad contents & summarize @@ -459,7 +459,7 @@ fn do_reminder( #[allow(unused_variables)] fn do_protocol( ttp: i64, plenum_day: &NaiveDate, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, - wiki: &mut Mediawiki, + wiki: &Mediawiki, ) -> Result<(), Box> { NYI!("trace/verbose annotations"); let (current_pad_id, pad_content, toc, n_topics) = get_pad_info(config, hedgedoc); @@ -490,7 +490,7 @@ fn do_protocol( #[allow(unused_must_use)] fn do_cleanup( _ttp: i64, _plenum_day: &NaiveDate, config: &KV, _hedgedoc: &HedgeDoc, _email: &SimpleEmail, - _wiki: &mut Mediawiki, + _wiki: &Mediawiki, ) -> Result<(), Box> { NYI!("trace/verbose annotations"); config.delete("state-toc"); @@ -509,7 +509,7 @@ type TransitionFunction = fn( config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, - wiki: &mut Mediawiki, + wiki: &Mediawiki, ) -> Result<(), Box>; #[rustfmt::skip] @@ -556,14 +556,14 @@ impl Display for ST { } fn nop( - _: i64, _: &NaiveDate, _: &KV, _: &HedgeDoc, _: &SimpleEmail, _: &mut Mediawiki, + _: i64, _: &NaiveDate, _: &KV, _: &HedgeDoc, _: &SimpleEmail, _: &Mediawiki, ) -> Result<(), Box> { Ok(()) } fn do_clean_announcement( ttp: i64, plenum_day: &NaiveDate, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, - wiki: &mut Mediawiki, + wiki: &Mediawiki, ) -> Result<(), Box> { do_cleanup(ttp, plenum_day, config, hedgedoc, email, wiki)?; do_announcement(ttp, plenum_day, config, hedgedoc, email, wiki) @@ -571,7 +571,7 @@ fn do_clean_announcement( fn do_clean_reminder( ttp: i64, plenum_day: &NaiveDate, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, - wiki: &mut Mediawiki, + wiki: &Mediawiki, ) -> Result<(), Box> { do_cleanup(ttp, plenum_day, config, hedgedoc, email, wiki)?; do_reminder(ttp, plenum_day, config, hedgedoc, email, wiki) diff --git a/src/mediawiki.rs b/src/mediawiki.rs index 82b067f..ccf33b3 100644 --- a/src/mediawiki.rs +++ b/src/mediawiki.rs @@ -1,6 +1,7 @@ use std::error::Error; use std::fs::File; use std::io::Read; +use std::cell::OnceCell; use colored::Colorize; use pandoc::{PandocError, PandocOutput}; @@ -57,8 +58,8 @@ pub struct Mediawiki { api_user: String, api_secret: String, is_dry_run: bool, - login_token: String, - csrf_token: String, + login_token: OnceCell, + csrf_token: OnceCell, plenum_main_page_name: String, client: Client, } @@ -92,13 +93,13 @@ impl Mediawiki { api_user: api_user.to_string(), api_secret: api_secret.to_string(), is_dry_run, - login_token: String::new(), - csrf_token: String::new(), + login_token: OnceCell::new(), + csrf_token: OnceCell::new(), plenum_main_page_name: plenum_main_page_name.to_string(), client: Client::builder().cookie_store(true).build().unwrap(), } } - pub fn get_login_token(&self) -> Result> { + pub fn get_login_token(&self) -> Result<(), Box> { let url = format!("{}/api.php?", self.server_url); let params: Box<[(&str, &str)]> = Box::from( [ @@ -109,20 +110,21 @@ impl Mediawiki { ]); let resp = self.make_request(url, params, ValidRequestTypes::Get)?; let response_deserialized: QueryResponseLogin = serde_json::from_str(&resp)?; - Ok(response_deserialized.query.tokens.logintoken) + self.login_token.set(response_deserialized.query.tokens.logintoken)?; + Ok(()) } pub fn login (&self) -> Result> { let url = format!("{}/api.php?", self.server_url); let params: Box<[(&str, &str)]> = Box::from([ ("lgname", self.api_user.as_str()), ("lgpassword", self.api_secret.as_str()), - ("lgtoken", &self.login_token), + ("lgtoken", self.login_token.get().unwrap()), ("action", "login") ]); let resp: Result> = self.make_request(url, params, ValidRequestTypes::Post); Ok(resp?) } - pub fn get_csrf_token(&self) -> Result> { // HAS TO BE FIXED + pub fn get_csrf_token(&self) -> Result<(), Box> { let url = format!("{}/api.php?", self.server_url); let params: Box<[(&str, &str)]> = Box::from([ @@ -133,7 +135,8 @@ impl Mediawiki { ]); let resp: String = self.make_request(url, params, ValidRequestTypes::Get)?; let response_deserialized: QueryResponseCsrf = serde_json::from_str(&resp)?; - Ok(response_deserialized.query.tokens.csrftoken) + self.csrf_token.set(response_deserialized.query.tokens.csrftoken)?; + Ok(()) } pub fn make_request(&self, url: String, params: Box<[(&str, &str)]>, request_type: ValidRequestTypes) -> Result> { @@ -188,7 +191,7 @@ impl Mediawiki { ("format", "json"), ("title", page_title), // Title of the page to edit. Cannot be used together with pageid. ("text", page_content), // Add this text to the end of the page or section. Overrides text. - ("token", self.csrf_token.as_str()), // A "csrf" token retrieved from action=query&meta=tokens + ("token", self.csrf_token.get().unwrap()), // A "csrf" token retrieved from action=query&meta=tokens ("bot", "true"), // Mark this edit as a bot edit. ]); let request_result = self.make_request(url, params, ValidRequestTypes::Post); @@ -223,8 +226,7 @@ impl Mediawiki { ("page", page_title), ("formatversion", "2"), ]); - let resp: Result> = self.make_request(url, params, ValidRequestTypes::Get); - let resp = resp?; + let resp = self.make_request(url, params, ValidRequestTypes::Get)?; let resp = json!(resp); Ok(resp["parse"]["wikitext"].to_string()) } @@ -237,8 +239,7 @@ impl Mediawiki { ("format", "json"), ("page", page_title), ]); - let resp: Result> = self.make_request(url, params, ValidRequestTypes::Get); - let resp = resp?; + let resp = self.make_request(url, params, ValidRequestTypes::Get)?; todo!() //let response_deserialized = serde_json::from_str(&resp)?; //Ok(response_deserialized["parse"]) @@ -252,7 +253,7 @@ impl Mediawiki { ("title", page_title), // Title of the page to edit. Cannot be used together with pageid. ("section", section_number), // Section identifier. 0 for the top section, new for a new section. Often a positive integer, but can also be non-numeric ("prependtext", text_to_prepend), // Add this text to the end of the page or section. Overrides text. - ("token", self.csrf_token.as_str()), // A "csrf" token retrieved from action=query&meta=tokens + ("token", self.csrf_token.get().unwrap()), // A "csrf" token retrieved from action=query&meta=tokens ("bot", "true"), // Mark this edit as a bot edit. ]); let request_result = self.make_request(url, params, ValidRequestTypes::Post); @@ -261,36 +262,23 @@ impl Mediawiki { } } -pub fn pad_ins_wiki(old_pad_content: String, wiki: &mut Mediawiki) -> Result<(), Box> { +pub fn pad_ins_wiki(old_pad_content: String, wiki: &Mediawiki) -> Result<(), Box> { // Login to Wiki and get required tokens for logging in and writing - let auth_result = wiki.get_login_token()?; - wiki.login_token.clone_from(&auth_result); // Copy the login token to the struct - println!("AUTH Done"); + wiki.get_login_token()?; + eprintln!("AUTH Done"); let login_result = wiki.login()?; - println!("LOGIN Done"); - let csrf_token = wiki.get_csrf_token()?; - println!("CSRF Done"); - wiki.csrf_token.clone_from(&csrf_token); // Copy the csrf token to the struct - println!("---AUTH RESULT:---\n{}\n---LOGIN RESULT:---\n{:?}\n---CSRF RESULT:---\n{}\n-----------", auth_result, login_result, csrf_token); + eprintln!("LOGIN Done"); + wiki.get_csrf_token()?; + eprintln!("CSRF Done"); + eprintln!("---LOGIN RESULT:---\n{:?}\n-----------", login_result); // Convert to mediawiki and make new page let pad_converted = convert_md_to_mediawiki(old_pad_content); - println!("Das kommt ins Wiki: {}", pad_converted); + eprintln!("Das kommt ins Wiki: {}", pad_converted); let page_title = "Page Test 5"; let page_title = format!("{}/{}", wiki.plenum_main_page_name, page_title); // Example: Plenum/13._August_2024 wiki.new_wiki_page(&page_title, &pad_converted)?; - // Textdatei wieder einlesen - - // 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!"); - println!("plenum_bot_user: {plenum_bot_user}, plenum_bot_pw: {plenum_bot_pw}, login_token: {login_token}") - - */ Ok(()) } @@ -343,6 +331,7 @@ fn create_title (nächster_plenumstermin: String) { // the response contains two \\ characters which break the usual deserialization #[derive(Deserialize)] struct QueryResponseLogin { + #[allow(dead_code)] batchcomplete: String, query: QueryTokensLogin, } @@ -359,6 +348,7 @@ struct TokensLogin { #[derive(Deserialize)] struct QueryResponseCsrf { + #[allow(dead_code)] batchcomplete: bool, query: crate::mediawiki::QueryTokensCsrf, } @@ -371,4 +361,4 @@ struct QueryTokensCsrf { #[derive(Deserialize)] struct TokensCsrf { csrftoken: String, -} \ No newline at end of file +} From d3681e1699cca509f01a76c0fc59b0013d7fb992 Mon Sep 17 00:00:00 2001 From: nobody Date: Sun, 25 Aug 2024 21:14:05 +0200 Subject: [PATCH 04/10] mediawiki/pandoc: call directly instead of via lib Pandoc crate is confusing and badly documented. Using std::process::command now to avoid temporary files. --- Cargo.toml | 1 - src/mediawiki.rs | 44 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4d9bee8..1321446 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" [dependencies] stdext = "0.3.3" -pandoc = "0.8" chrono = "0.4.38" regex = "1.10.5" futures = "0.3.30" diff --git a/src/mediawiki.rs b/src/mediawiki.rs index ccf33b3..c780443 100644 --- a/src/mediawiki.rs +++ b/src/mediawiki.rs @@ -1,10 +1,10 @@ +use std::cell::OnceCell; use std::error::Error; use std::fs::File; -use std::io::Read; -use std::cell::OnceCell; +use std::io::{Read, Write}; +use std::process::{Command, Output, Stdio}; use colored::Colorize; -use pandoc::{PandocError, PandocOutput}; use reqwest::blocking::Client; use serde::Deserialize; use serde_json::json; @@ -283,18 +283,41 @@ pub fn pad_ins_wiki(old_pad_content: String, wiki: &Mediawiki) -> Result<(), Box } /// Converts one file type into another using pandoc and saves the result as a txt file -fn pandoc_convert( - old_pad_content: String, output_filepath: &str, input_format: pandoc::InputFormat, - output_format: pandoc::OutputFormat, -) -> Result { +fn pandoc_convert(old_pad_content: String) -> Result> { + let mut cmd = Command::new("pandoc") + .args(["--from", "markdown", "--to", "mediawiki", "--no-highlight"]) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn()?; + if let Some(mut stdin) = cmd.stdin.take() { + stdin.write_all(old_pad_content.as_bytes())?; + } + let mut output = String::new(); + if let Some(mut stdout) = cmd.stdout.take() { + stdout.read_to_string(&mut output)?; + } + let mut errmsg = String::new(); + if let Some(mut stderr) = cmd.stderr.take() { + stderr.read_to_string(&mut errmsg)?; + } + let status = cmd.wait()?; + if status.success() { + Ok(output) + } else { + Err( format!("Pandoc error, exit {:?}\n{}", status, errmsg).into() ) + } + /* //Convert Markdown into Mediawiki // Vanilla pandoc Befehl: pandoc --from markdown --to mediawiki --no-highlight let mut p = pandoc::new(); p.set_input(pandoc::InputKind::Pipe(old_pad_content)); p.set_input_format(input_format, vec![]); - p.set_output(pandoc::OutputKind::File(output_filepath.parse().unwrap())); + p.set_output(pandoc::OutputKind::Pipe); // File(output_filepath.parse().unwrap())); p.set_output_format(output_format, vec![]); - p.execute() + let output = p.execute()?; + Ok(output.into()) + */ } @@ -313,7 +336,8 @@ fn convert_md_to_mediawiki(old_pad_content: String) -> String { // TODO: use tempfile="3.3", make it a NamedTempFile::new()?; // or alternatively use piped stdout to avoid files entirely let output_filepath: &str = "./pandoc_mediawiki.txt"; - pandoc_convert(old_pad_content, output_filepath, pandoc::InputFormat::Markdown, pandoc::OutputFormat::MediaWiki).expect("Fehler beim Umwandeln des und speichern des Pads in eine mediawiki-Textdatei"); + pandoc_convert(old_pad_content) + .expect("Fehler beim Umwandeln des und speichern des Pads in eine mediawiki-Textdatei"); let temp = read_txt_file(output_filepath); println!("TEMP: {}", temp.purple()); temp From b04d35ee6bc1b100b15761f87e3547bb33a13d9d Mon Sep 17 00:00:00 2001 From: nobody Date: Mon, 26 Aug 2024 00:45:53 +0200 Subject: [PATCH 05/10] mediawiki: more follow-up cleanup - use macros for conditional status messages - abstract out a pipe() fn --- src/lib.rs | 27 ++++++++++++++++ src/mediawiki.rs | 81 ++++++++++-------------------------------------- 2 files changed, 43 insertions(+), 65 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a3edc39..5f10aed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,33 @@ pub mod mediawiki; pub mod template; use std::env; +use std::error::Error; +use std::io::{Read, Write}; +use std::process::{Command, ExitStatus, Stdio}; + +fn pipe( + program: &str, args: &mut [&str], input: String, +) -> Result<(String, String, ExitStatus), Box> { + let mut cmd = Command::new(program) + .args(args) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn()?; + if let Some(mut stdin) = cmd.stdin.take() { + stdin.write_all(input.as_bytes())?; + } + let mut output = String::new(); + if let Some(mut stdout) = cmd.stdout.take() { + stdout.read_to_string(&mut output)?; + } + let mut errmsg = String::new(); + if let Some(mut stderr) = cmd.stderr.take() { + stderr.read_to_string(&mut errmsg)?; + } + let status = cmd.wait()?; + Ok((output, errmsg, status)) +} /// Checks environment variable `DRY_RUN` to see if any external operations /// should *actually* be done. diff --git a/src/mediawiki.rs b/src/mediawiki.rs index c780443..37df128 100644 --- a/src/mediawiki.rs +++ b/src/mediawiki.rs @@ -1,8 +1,5 @@ use std::cell::OnceCell; use std::error::Error; -use std::fs::File; -use std::io::{Read, Write}; -use std::process::{Command, Output, Stdio}; use colored::Colorize; use reqwest::blocking::Client; @@ -10,6 +7,7 @@ use serde::Deserialize; use serde_json::json; use crate::config_spec::{CfgField, CfgGroup}; +use crate::{trace_var, verboseln}; pub const CONFIG: CfgGroup<'static> = CfgGroup { name: "wiki", @@ -204,7 +202,7 @@ impl Mediawiki { pub fn update_plenum_page (&self, new_page_title_to_link_to: &str) -> Result<(), Box> { // 1. Download Plenum page content let page_content = self.get_page_content(&self.plenum_main_page_name)?; - println!("---\nPage Content: {}\n---", page_content.red()); + trace_var!(page_content); let current_year = "2024"; // TODO: Datumslogik einbauen let year_section = format!("=== {} ===\n", current_year); if page_content.contains(&year_section) { @@ -265,16 +263,16 @@ impl Mediawiki { pub fn pad_ins_wiki(old_pad_content: String, wiki: &Mediawiki) -> Result<(), Box> { // Login to Wiki and get required tokens for logging in and writing wiki.get_login_token()?; - eprintln!("AUTH Done"); + verboseln!("Login token acquired."); let login_result = wiki.login()?; - eprintln!("LOGIN Done"); + verboseln!("Login done."); + trace_var!(login_result); wiki.get_csrf_token()?; - eprintln!("CSRF Done"); - eprintln!("---LOGIN RESULT:---\n{:?}\n-----------", login_result); + verboseln!("CSRF token acquired."); // Convert to mediawiki and make new page - let pad_converted = convert_md_to_mediawiki(old_pad_content); - eprintln!("Das kommt ins Wiki: {}", pad_converted); + let pad_converted = pandoc_convert(old_pad_content)?; + trace_var!(pad_converted); let page_title = "Page Test 5"; let page_title = format!("{}/{}", wiki.plenum_main_page_name, page_title); // Example: Plenum/13._August_2024 wiki.new_wiki_page(&page_title, &pad_converted)?; @@ -282,65 +280,18 @@ pub fn pad_ins_wiki(old_pad_content: String, wiki: &Mediawiki) -> Result<(), Box Ok(()) } -/// Converts one file type into another using pandoc and saves the result as a txt file -fn pandoc_convert(old_pad_content: String) -> Result> { - let mut cmd = Command::new("pandoc") - .args(["--from", "markdown", "--to", "mediawiki", "--no-highlight"]) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn()?; - if let Some(mut stdin) = cmd.stdin.take() { - stdin.write_all(old_pad_content.as_bytes())?; - } - let mut output = String::new(); - if let Some(mut stdout) = cmd.stdout.take() { - stdout.read_to_string(&mut output)?; - } - let mut errmsg = String::new(); - if let Some(mut stderr) = cmd.stderr.take() { - stderr.read_to_string(&mut errmsg)?; - } - let status = cmd.wait()?; +/// Takes a String in the Markdown format and returns a String in the mediawiki Format +fn pandoc_convert(markdown: String) -> Result> { + let (output, errors, status) = crate::pipe( + "pandoc", + &mut ["--from", "markdown", "--to", "mediawiki", "--no-highlight"], + markdown, + )?; if status.success() { Ok(output) } else { - Err( format!("Pandoc error, exit {:?}\n{}", status, errmsg).into() ) + Err(format!("Pandoc error, exit code {:?}\n{}", status, errors).into()) } - /* - //Convert Markdown into Mediawiki - // Vanilla pandoc Befehl: pandoc --from markdown --to mediawiki --no-highlight - let mut p = pandoc::new(); - p.set_input(pandoc::InputKind::Pipe(old_pad_content)); - p.set_input_format(input_format, vec![]); - p.set_output(pandoc::OutputKind::Pipe); // File(output_filepath.parse().unwrap())); - p.set_output_format(output_format, vec![]); - let output = p.execute()?; - Ok(output.into()) - */ -} - - -/// Reads a text file from a specified path and returns it as a String -fn read_txt_file(filepath: &str) -> String { - let mut file = File::open(filepath) - .unwrap_or_else(|_| panic!("Fehler beim öffnen der Textdatei mit Pfad {filepath}!")); - let mut contents = String::new(); - file.read_to_string(&mut contents) - .expect("Fehler beim auslesen der MediaWiki-Textdatei!"); - contents -} - -/// Takes a Sting in the Markdown format and returns a String in the mediawiki Format -fn convert_md_to_mediawiki(old_pad_content: String) -> String { - // TODO: use tempfile="3.3", make it a NamedTempFile::new()?; - // or alternatively use piped stdout to avoid files entirely - let output_filepath: &str = "./pandoc_mediawiki.txt"; - pandoc_convert(old_pad_content) - .expect("Fehler beim Umwandeln des und speichern des Pads in eine mediawiki-Textdatei"); - let temp = read_txt_file(output_filepath); - println!("TEMP: {}", temp.purple()); - temp } /* From aca180d6ba0c671785044a00cad552e9cf7c67d3 Mon Sep 17 00:00:00 2001 From: murmeldin Date: Mon, 14 Oct 2024 16:00:26 +0200 Subject: [PATCH 06/10] new state() function - now, it is possible to use e.g. STATE_OVERRIDE=Waiting to override the state - this is useful for development in order to ignore parts of the config.sqlite --- architecture.md | 3 +++ src/main.rs | 21 ++++++++++++++------- src/mediawiki.rs | 4 ++-- 3 files changed, 19 insertions(+), 9 deletions(-) create mode 100644 architecture.md diff --git a/architecture.md b/architecture.md new file mode 100644 index 0000000..43c562a --- /dev/null +++ b/architecture.md @@ -0,0 +1,3 @@ +# Architecture + +This document is intended to explain how the Plenum Bot works. diff --git a/src/main.rs b/src/main.rs index a351283..a67e886 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,9 @@ use std::env; use std::error::Error; +use std::fmt::Display; +use std::io::IsTerminal; +use std::os::linux::raw::stat; use std::time::Instant; use chrono::{Local, NaiveDate}; @@ -9,18 +12,14 @@ use clap::{Arg, Command}; use colored::Colorize; use regex::Regex; -use std::fmt::Display; -use std::io::IsTerminal; - +use cccron_lib::{trace_var, trace_var_, verboseln}; use cccron_lib::config_spec::{self, CfgField, CfgGroup, CfgSpec}; -use cccron_lib::key_value::KeyValueStore as KV; - use cccron_lib::date; use cccron_lib::email::{self, Email, SimpleEmail}; use cccron_lib::hedgedoc::{self, HedgeDoc}; use cccron_lib::is_dry_run; +use cccron_lib::key_value::{KeyValueStore as KV, KeyValueStore}; use cccron_lib::mediawiki::{self, Mediawiki}; -use cccron_lib::{verboseln,trace_var,trace_var_}; use cccron_lib::NYI; /* ***** Config Spec ***** */ @@ -93,6 +92,14 @@ fn today() -> NaiveDate { .map(|v| NaiveDate::parse_from_str(&v, "%F").expect("'TODAY' hat nicht format YYYY-MM-DD")) .unwrap_or(Local::now().date_naive()) } +/// Gets either the state from the config or overrides it with the state from +/// the environment variable `STATE_OVERRIDE` (for testing purposes.) +fn state(config: &KeyValueStore) -> ProgramState { + match env::var("STATE_OVERRIDE") { + Ok(val) => ProgramState::parse(&val), + Err(_e) => ProgramState::parse(&config["state-name"]) + } +} #[derive(Debug)] struct Args { @@ -182,7 +189,7 @@ fn main() -> Result<(), Box> { let nearest_plenum_days = date::get_matching_dates_around(today, plenum_spec); trace_var!(nearest_plenum_days); // figure out where we are - let mut last_state = ProgramState::parse(&config["state-name"]); + let mut last_state = state(&config); let last_run = config.get("state-last-run").unwrap_or_default(); let last_run = NaiveDate::parse_from_str(&last_run, "%Y-%m-%d").unwrap_or_default(); trace_var!(last_run); diff --git a/src/mediawiki.rs b/src/mediawiki.rs index 37df128..420bef0 100644 --- a/src/mediawiki.rs +++ b/src/mediawiki.rs @@ -6,8 +6,8 @@ use reqwest::blocking::Client; use serde::Deserialize; use serde_json::json; -use crate::config_spec::{CfgField, CfgGroup}; use crate::{trace_var, verboseln}; +use crate::config_spec::{CfgField, CfgGroup}; pub const CONFIG: CfgGroup<'static> = CfgGroup { name: "wiki", @@ -284,7 +284,7 @@ pub fn pad_ins_wiki(old_pad_content: String, wiki: &Mediawiki) -> Result<(), Box fn pandoc_convert(markdown: String) -> Result> { let (output, errors, status) = crate::pipe( "pandoc", - &mut ["--from", "markdown", "--to", "mediawiki", "--no-highlight"], + &mut ["--from", "markdown-auto_identifiers", "--to", "mediawiki", "--no-highlight"], markdown, )?; if status.success() { From bbd9b2c29e71e5128b497ad66bc4069579b411b3 Mon Sep 17 00:00:00 2001 From: murmeldin Date: Wed, 16 Oct 2024 16:10:49 +0200 Subject: [PATCH 07/10] major improvements to plenum pad logic - a lot of new comments for better doc - removed unnecessary functions - main plenum pad logic still in progress - one additional comment in main --- src/main.rs | 2 + src/mediawiki.rs | 148 ++++++++++++++++++++++++++++++----------------- 2 files changed, 96 insertions(+), 54 deletions(-) diff --git a/src/main.rs b/src/main.rs index a67e886..e1bd3a5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -94,6 +94,8 @@ fn today() -> NaiveDate { } /// Gets either the state from the config or overrides it with the state from /// the environment variable `STATE_OVERRIDE` (for testing purposes.) +/// +/// For example, `STATE_OVERRIDE=Waiting` can be used to debug mediawiki. fn state(config: &KeyValueStore) -> ProgramState { match env::var("STATE_OVERRIDE") { Ok(val) => ProgramState::parse(&val), diff --git a/src/mediawiki.rs b/src/mediawiki.rs index 420bef0..fc837d8 100644 --- a/src/mediawiki.rs +++ b/src/mediawiki.rs @@ -1,10 +1,10 @@ use std::cell::OnceCell; use std::error::Error; +use chrono::{Datelike, Utc}; use colored::Colorize; use reqwest::blocking::Client; use serde::Deserialize; -use serde_json::json; use crate::{trace_var, verboseln}; use crate::config_spec::{CfgField, CfgGroup}; @@ -149,11 +149,13 @@ impl Mediawiki { .send() } ValidRequestTypes::Post | ValidRequestTypes::PostForEditing => { + // convert the params into a HashMap for JSON + let params_map: std::collections::HashMap<_, _> = params.iter().cloned().collect(); self .client .post(url) //.basic_auth(&self.http_user, Some(&self.http_password)) ZU TESTZWECKEN ENTFERNT - .form(¶ms) + .json(¶ms_map) .send() } } @@ -179,8 +181,9 @@ impl Mediawiki { }; resp } + /// Creates a completely new wiki page with page_content and page_title as inputs - pub fn new_wiki_page (&self, page_title: &str, page_content: &str) -> Result> { + pub fn new_wiki_page (&self, page_title: &str, page_content: &str, update_main_page: bool) -> Result> { // action=edit&format=json&title=Wikipedia:Sandbox&appendtext=Hello&token=sampleCsrfToken123+\ let url = format!("{}/api.php?", self.server_url); @@ -192,28 +195,72 @@ impl Mediawiki { ("token", self.csrf_token.get().unwrap()), // A "csrf" token retrieved from action=query&meta=tokens ("bot", "true"), // Mark this edit as a bot edit. ]); + + println!("Making request to {} with params: {:?}", url, params); + let request_result = self.make_request(url, params, ValidRequestTypes::Post); - - self.update_plenum_page(page_title)?; - + + if update_main_page { + self.update_plenum_page(page_title)?; + } request_result } - /// Downloads the main Plenum Page from Mediawiki, inserts the Link to the new Page and replaces the content of the mediawiki- + /// This function is responsible for updating the main plenum page: + /// + /// It downloads the main plenum page from Mediawiki, inserts the + /// new Link to the newly uploaded plenum pad and uploads the + /// page back to mediawiki. pub fn update_plenum_page (&self, new_page_title_to_link_to: &str) -> Result<(), Box> { - // 1. Download Plenum page content + // 1. Get the current year: + let current_year = Utc::now().year().to_string(); + let year_pattern = format!("=== {} ===", current_year); + + // 2. Download Plenum page content let page_content = self.get_page_content(&self.plenum_main_page_name)?; - trace_var!(page_content); - let current_year = "2024"; // TODO: Datumslogik einbauen - let year_section = format!("=== {} ===\n", current_year); - if page_content.contains(&year_section) { - let mut content_split: Vec<&str> = page_content.split(&year_section).collect(); - println!("Length: {}", content_split.len()); - let rest_of_content = content_split.pop().unwrap_or_default(); - let updated_section = format!("{}{}\n* {}", content_split.join(&year_section), year_section, new_page_title_to_link_to); - //format!("{}{}", updated_section, rest_of_content) + //trace_var!(page_content); + + // 3. Check if the current year exists + let mut updated_section = String::new(); + if page_content.contains(&year_pattern) { + // 4. Year Heading exists, inserting new link below + let mut content_split: Vec<&str> = page_content.split(&year_pattern).collect(); + let site_content_below_year_heading = content_split.pop().unwrap_or_default(); + let site_content_above_year_heading = content_split.pop().unwrap_or_default(); + updated_section = format!( + "{}{}\n* [[{}]]{}", + site_content_above_year_heading, + year_pattern, + new_page_title_to_link_to, + site_content_below_year_heading); + verboseln!("updated page: {}", updated_section) + } else { + // Scenario: New year: Generating new heading for the current year + // 4. First checking if the last year exists: + let last_year = current_year.parse::()? - 1; // returns e.g. 2023 when given 2024 + let last_year_pattern = format!("=== {} ===", last_year); + if page_content.contains(&last_year_pattern) { + // 2. Secondly, generate a new heading for this year above last year's + verboseln!("new year, adding this year's heading"); + let mut content_split: Vec<&str> = page_content.split(&last_year_pattern).collect(); + let site_content_below_year_heading = content_split.pop().unwrap_or_default(); + let site_content_above_year_heading = content_split.pop().unwrap_or_default(); + updated_section = format!( + "{}{}\n* [[{}]]\n\n{}{}", + site_content_above_year_heading, + year_pattern, + new_page_title_to_link_to, + last_year_pattern, + site_content_below_year_heading); + } else { + println!("There is neither a {}, nor a {} heading on the main plenum Page!", current_year, last_year); + panic!("The Plenum Main Page seems seriously damaged, aborting...") + } } + // uploading the pad again + self.new_wiki_page(&self.plenum_main_page_name, &updated_section, false)?; // nicht auf true setzen, sonst entsteht eine Endlosschleife Ok(()) } + /// This function downloads and returns the contents of a wiki page when given the page's title (e.g. `page_title = Plenum/13._August_2024`) pub fn get_page_content (&self, page_title: &str) -> Result> { let url = format!("{}/api.php?", self.server_url); @@ -225,41 +272,20 @@ impl Mediawiki { ("formatversion", "2"), ]); let resp = self.make_request(url, params, ValidRequestTypes::Get)?; - let resp = json!(resp); - Ok(resp["parse"]["wikitext"].to_string()) - } - pub fn get_page_section_title (&self, page_title: &str, section_number: &str) -> Result> { - let url = - format!("{}/api.php?", self.server_url); - let params: Box<[(&str, &str)]> = Box::from([ - ("action", "parse"), // Create and edit pages. - ("contentmodel", "wikitext"), - ("format", "json"), - ("page", page_title), - ]); - let resp = self.make_request(url, params, ValidRequestTypes::Get)?; - todo!() - //let response_deserialized = serde_json::from_str(&resp)?; - //Ok(response_deserialized["parse"]) - } - pub fn edit_section (&self, page_title: &str, text_to_prepend: &str, section_number: &str) -> Result> { - let url = - format!("{}/api.php?", self.server_url); - let params: Box<[(&str, &str)]> = Box::from([ - ("action", "edit"), // Create and edit pages. - ("format", "json"), - ("title", page_title), // Title of the page to edit. Cannot be used together with pageid. - ("section", section_number), // Section identifier. 0 for the top section, new for a new section. Often a positive integer, but can also be non-numeric - ("prependtext", text_to_prepend), // Add this text to the end of the page or section. Overrides text. - ("token", self.csrf_token.get().unwrap()), // A "csrf" token retrieved from action=query&meta=tokens - ("bot", "true"), // Mark this edit as a bot edit. - ]); - let request_result = self.make_request(url, params, ValidRequestTypes::Post); + let response_deserialized: serde_json::Value = serde_json::from_str(&resp)?; - request_result + let resp = response_deserialized + .get("parse") + .and_then(|parse | parse.get("wikitext")) + .ok_or("Expected field `wikitext` not found, likely no access to main plenum page")?; + Ok(resp.to_string()) } } +/// This function runs at the end of do_protocol() and is responsible for +/// logging in to mediawiki, retrieving the necessary tokens, creating a +/// new wiki page for the current plenum protocol, and for linking the new +/// page to the main plenum overview page. pub fn pad_ins_wiki(old_pad_content: String, wiki: &Mediawiki) -> Result<(), Box> { // Login to Wiki and get required tokens for logging in and writing wiki.get_login_token()?; @@ -273,14 +299,25 @@ pub fn pad_ins_wiki(old_pad_content: String, wiki: &Mediawiki) -> Result<(), Box // Convert to mediawiki and make new page let pad_converted = pandoc_convert(old_pad_content)?; trace_var!(pad_converted); - let page_title = "Page Test 5"; - let page_title = format!("{}/{}", wiki.plenum_main_page_name, page_title); // Example: Plenum/13._August_2024 - wiki.new_wiki_page(&page_title, &pad_converted)?; + + // Create a new wiki page plenum_main_page_name/page_title, e.g. under Plenum/13._August_2024 + // and past the converted pad there + verboseln!("wiki: uploading converted pad"); + // let page_title = "14._August_2024"; + let page_title = "14._AUGUST_2024"; + let page_title = format!("{}/{}", wiki.plenum_main_page_name, page_title); + wiki.new_wiki_page(&page_title, &pad_converted, true)?; + // Download the main plenum page and insert the new page_title as a link + verboseln!("wiki: updating main plenum page"); + + wiki.update_plenum_page(&page_title)?; + verboseln!("Finished successfully with wiki"); Ok(()) } -/// Takes a String in the Markdown format and returns a String in the mediawiki Format +/// Takes a String in the Markdown format, converts it and returns it in +/// the Mediawiki format fn pandoc_convert(markdown: String) -> Result> { let (output, errors, status) = crate::pipe( "pandoc", @@ -288,6 +325,7 @@ fn pandoc_convert(markdown: String) -> Result> { markdown, )?; if status.success() { + println!("Resultat von Pandoc: {}", output); Ok(output) } else { Err(format!("Pandoc error, exit code {:?}\n{}", status, errors).into()) @@ -302,8 +340,10 @@ fn create_title (nächster_plenumstermin: String) { */ -// This has to be defined that way, because both in the login and csrf token, -// the response contains two \\ characters which break the usual deserialization +/// Deserialization must be done this way because the response contains +/// two `\\` characters in both the login and csrf tokens, which breaks the +/// usual deserialization + #[derive(Deserialize)] struct QueryResponseLogin { #[allow(dead_code)] From 659e9f258aedc3fb7efa0b6928deb74258232a6a Mon Sep 17 00:00:00 2001 From: murmeldin Date: Wed, 27 Nov 2024 20:34:07 +0100 Subject: [PATCH 08/10] mediawiki work in progress --- src/main.rs | 5 +- src/mediawiki.rs | 279 ++++++++++++++++++++++++++++++++--------------- 2 files changed, 191 insertions(+), 93 deletions(-) diff --git a/src/main.rs b/src/main.rs index e1bd3a5..5bea989 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,11 +7,10 @@ use std::io::IsTerminal; use std::os::linux::raw::stat; use std::time::Instant; -use chrono::{Local, NaiveDate}; +use chrono::{Local, NaiveDate, Utc, DateTime}; use clap::{Arg, Command}; use colored::Colorize; use regex::Regex; - use cccron_lib::{trace_var, trace_var_, verboseln}; use cccron_lib::config_spec::{self, CfgField, CfgGroup, CfgSpec}; use cccron_lib::date; @@ -485,7 +484,7 @@ fn do_protocol( ); let _message_id = send_email(&subject, &body, email, config)?; NYI!("convert to mediawiki"); - mediawiki::pad_ins_wiki(pad_content, wiki)?; + mediawiki::pad_ins_wiki(pad_content, wiki, plenum_day)?; NYI!("add to wiki"); config.set("state-name", &ProgramState::Logged.to_string()).ok(); } else { diff --git a/src/mediawiki.rs b/src/mediawiki.rs index fc837d8..d43971e 100644 --- a/src/mediawiki.rs +++ b/src/mediawiki.rs @@ -1,7 +1,7 @@ use std::cell::OnceCell; use std::error::Error; -use chrono::{Datelike, Utc}; +use chrono::{Datelike, NaiveDate, Utc}; use colored::Colorize; use reqwest::blocking::Client; use serde::Deserialize; @@ -183,89 +183,105 @@ impl Mediawiki { } /// Creates a completely new wiki page with page_content and page_title as inputs - pub fn new_wiki_page (&self, page_title: &str, page_content: &str, update_main_page: bool) -> Result> { - // action=edit&format=json&title=Wikipedia:Sandbox&appendtext=Hello&token=sampleCsrfToken123+\ - let url = - format!("{}/api.php?", self.server_url); - let params: Box<[(&str, &str)]> = Box::from([ - ("action", "edit"), // Create and edit pages. - ("format", "json"), - ("title", page_title), // Title of the page to edit. Cannot be used together with pageid. - ("text", page_content), // Add this text to the end of the page or section. Overrides text. - ("token", self.csrf_token.get().unwrap()), // A "csrf" token retrieved from action=query&meta=tokens - ("bot", "true"), // Mark this edit as a bot edit. - ]); - - println!("Making request to {} with params: {:?}", url, params); - - let request_result = self.make_request(url, params, ValidRequestTypes::Post); - - if update_main_page { - self.update_plenum_page(page_title)?; + pub fn new_wiki_page(&self, page_title: &str, page_content: &str, update_main_page: bool) -> Result> { + // Prevent dry run from making actual wiki edits + if self.is_dry_run { + println!("Dry run: Would create wiki page '{}' with content {}", page_title, page_content); + return Ok("Dry run - no actual page created".to_string()); + } + + // Ensure we have a CSRF token + if self.csrf_token.get().is_none() { + return Err("CSRF token not set. Call get_csrf_token() first.".into()); + } + + let url = format!("{}/api.php", self.server_url); + let params: Box<[(&str, &str)]> = Box::from([ + ("action", "edit"), + ("format", "json"), + ("title", page_title), + ("text", page_content), + ("token", self.csrf_token.get().unwrap()), + ("createonly", "true"), // Prevent overwriting existing pages + ("bot", "true"), + ]); + + // Log the request details for debugging + verboseln!("Creating wiki page: {} at {}", page_title, url); + + // Make the request to create the page + let request_result = self.make_request(url, params, ValidRequestTypes::PostForEditing)?; + + // Parse the response to check for success + let response: serde_json::Value = serde_json::from_str(&request_result)?; + + // Check if the page creation was successful + if let Some(edit) = response.get("edit") { + if edit.get("result").and_then(|r| r.as_str()) == Some("Success") { + verboseln!("Successfully created wiki page: {}", page_title); + + // Update the main plenum page if requested + if update_main_page { + self.update_plenum_page(page_title)?; + } + + Ok(request_result) + } else { + Err(format!("Failed to create wiki page. Response: {}", response).into()) + } + } else { + Err(format!("Unexpected response when creating wiki page: {}", response).into()) } - request_result } + /// This function is responsible for updating the main plenum page: /// /// It downloads the main plenum page from Mediawiki, inserts the /// new Link to the newly uploaded plenum pad and uploads the /// page back to mediawiki. - pub fn update_plenum_page (&self, new_page_title_to_link_to: &str) -> Result<(), Box> { - // 1. Get the current year: + pub fn update_plenum_page(&self, new_page_title_to_link_to: &str) -> Result<(), Box> { let current_year = Utc::now().year().to_string(); - let year_pattern = format!("=== {} ===", current_year); - - // 2. Download Plenum page content - let page_content = self.get_page_content(&self.plenum_main_page_name)?; - //trace_var!(page_content); - - // 3. Check if the current year exists - let mut updated_section = String::new(); - if page_content.contains(&year_pattern) { - // 4. Year Heading exists, inserting new link below - let mut content_split: Vec<&str> = page_content.split(&year_pattern).collect(); - let site_content_below_year_heading = content_split.pop().unwrap_or_default(); - let site_content_above_year_heading = content_split.pop().unwrap_or_default(); - updated_section = format!( - "{}{}\n* [[{}]]{}", - site_content_above_year_heading, - year_pattern, - new_page_title_to_link_to, - site_content_below_year_heading); - verboseln!("updated page: {}", updated_section) - } else { - // Scenario: New year: Generating new heading for the current year - // 4. First checking if the last year exists: - let last_year = current_year.parse::()? - 1; // returns e.g. 2023 when given 2024 - let last_year_pattern = format!("=== {} ===", last_year); - if page_content.contains(&last_year_pattern) { - // 2. Secondly, generate a new heading for this year above last year's - verboseln!("new year, adding this year's heading"); - let mut content_split: Vec<&str> = page_content.split(&last_year_pattern).collect(); - let site_content_below_year_heading = content_split.pop().unwrap_or_default(); - let site_content_above_year_heading = content_split.pop().unwrap_or_default(); - updated_section = format!( - "{}{}\n* [[{}]]\n\n{}{}", - site_content_above_year_heading, - year_pattern, - new_page_title_to_link_to, - last_year_pattern, - site_content_below_year_heading); + let year_heading_pattern = format!("=== {} ===", current_year); + + // Download Plenum page content + let mut page_content = self.get_page_content(&self.plenum_main_page_name)?; + + // check if the current year heading pattern exists + if !page_content.contains(&year_heading_pattern) { + // If not, add a new year heading pattern + let last_year = (current_year.parse::()? - 1).to_string(); + let last_year_heading_pattern = format!("=== {} ===", last_year); + + if page_content.contains(&last_year_heading_pattern) { + // add a new year heading pattern before the last year one + let parts: Vec<&str> = page_content.split(&last_year_heading_pattern).collect(); + page_content = format!( + "{}=== {} ===\n* [[{}]]\n\n{}{}", + parts[0], current_year, new_page_title_to_link_to, + last_year_heading_pattern, + parts[1] + ); } else { - println!("There is neither a {}, nor a {} heading on the main plenum Page!", current_year, last_year); - panic!("The Plenum Main Page seems seriously damaged, aborting...") + // Fallback: add the current year heading to the end of the page + page_content.push_str(&format!("\n\n=== {} ===\n* [[{}]]", current_year, new_page_title_to_link_to)); } + } else { + // Paste the link below the current year heading + page_content = page_content.replace( + &year_heading_pattern, + &format!("{}\n* [[{}]]", year_heading_pattern, new_page_title_to_link_to) + ); } - // uploading the pad again - self.new_wiki_page(&self.plenum_main_page_name, &updated_section, false)?; // nicht auf true setzen, sonst entsteht eine Endlosschleife + + // refresh page + self.new_wiki_page(&self.plenum_main_page_name, &page_content, false)?; Ok(()) } /// This function downloads and returns the contents of a wiki page when given the page's title (e.g. `page_title = Plenum/13._August_2024`) - pub fn get_page_content (&self, page_title: &str) -> Result> { - let url = - format!("{}/api.php?", self.server_url); + pub fn get_page_content(&self, page_title: &str) -> Result> { + let url = format!("{}/api.php?", self.server_url); let params: Box<[(&str, &str)]> = Box::from([ - ("action", "parse"), // Create and edit pages. + ("action", "parse"), ("prop", "wikitext"), ("format", "json"), ("page", page_title), @@ -273,26 +289,118 @@ impl Mediawiki { ]); let resp = self.make_request(url, params, ValidRequestTypes::Get)?; let response_deserialized: serde_json::Value = serde_json::from_str(&resp)?; - - let resp = response_deserialized + + let wikitext = response_deserialized .get("parse") - .and_then(|parse | parse.get("wikitext")) - .ok_or("Expected field `wikitext` not found, likely no access to main plenum page")?; - Ok(resp.to_string()) + .and_then(|parse| parse.get("wikitext")) + .and_then(|text| text.as_str()) + .ok_or("Expected field `wikitext` not found")?; + + Ok(wikitext.to_string()) } + + pub fn test_wiki_write(&self) -> Result<(), Box> { + // Generate a unique test page title + let test_page_title = format!( + "TestPage/WikiWriteTest-{}", + chrono::Utc::now().format("%Y%m%d%H%M%S") + ); + + // Test content to write + let test_content = format!( + "Wiki Write Test\n\nThis is a test page generated at {}. \nIt can be safely deleted.", + chrono::Utc::now() + ); + + // Ensure login token and CSRF token are set + self.get_login_token()?; + let _login_result = self.login()?; + self.get_csrf_token()?; + + let url = format!("{}/api.php", self.server_url); + let params: Box<[(&str, &str)]> = Box::from([ + ("action", "edit"), + ("format", "json"), + ("title", &test_page_title), + ("text", &test_content), + ("token", self.csrf_token.get().unwrap()), + ("createonly", "true"), + ("bot", "true"), + ]); + + // Manually print out all parameters for debugging + println!("Debug - URL: {}", url); + println!("Debug - Parameters:"); + for (key, value) in params.iter() { + println!(" {}: {}", key, value); + } + + // Make request and capture the full response + let request_result = match self.client + .post(&url) + .basic_auth(&self.http_user, Some(&self.http_password)) + .form(¶ms) + .send() + { + Ok(response) => { + println!("Debug - Response Status: {}", response.status()); + println!("Debug - Response Headers: {:?}", response.headers()); + + match response.text() { + Ok(text) => { + println!("Debug - Raw Response Body:\n{}", text); + text + }, + Err(e) => { + return Err(format!("Failed to read response body: {}", e).into()); + } + } + }, + Err(e) => { + return Err(format!("Request failed: {}", e).into()); + } + }; + + // Attempt to parse the response + match serde_json::from_str::(&request_result) { + Ok(response) => { + println!("Debug - Parsed Response: {}", response); + + if let Some(edit) = response.get("edit") { + if edit.get("result").and_then(|r| r.as_str()) == Some("Success") { + println!("✅ Successfully created test page: {}", test_page_title); + Ok(()) + } else { + Err(format!("Failed to create page. Response: {}", response).into()) + } + } else { + Err(format!("Unexpected response: {}", response).into()) + } + } + Err(e) => { + Err(format!("JSON parsing failed. Error: {}. Raw response: {}", e, request_result).into()) + } + } + } + } /// This function runs at the end of do_protocol() and is responsible for /// logging in to mediawiki, retrieving the necessary tokens, creating a /// new wiki page for the current plenum protocol, and for linking the new /// page to the main plenum overview page. -pub fn pad_ins_wiki(old_pad_content: String, wiki: &Mediawiki) -> Result<(), Box> { +pub fn pad_ins_wiki(old_pad_content: String, wiki: &Mediawiki, plenum_date: &NaiveDate) -> Result<(), Box> { + // Use the provided date or default to current date + let date = plenum_date; + wiki.test_wiki_write()?; // Login to Wiki and get required tokens for logging in and writing wiki.get_login_token()?; verboseln!("Login token acquired."); + let login_result = wiki.login()?; verboseln!("Login done."); trace_var!(login_result); + wiki.get_csrf_token()?; verboseln!("CSRF token acquired."); @@ -301,17 +409,12 @@ pub fn pad_ins_wiki(old_pad_content: String, wiki: &Mediawiki) -> Result<(), Box trace_var!(pad_converted); // Create a new wiki page plenum_main_page_name/page_title, e.g. under Plenum/13._August_2024 - // and past the converted pad there verboseln!("wiki: uploading converted pad"); - // let page_title = "14._August_2024"; - let page_title = "14._AUGUST_2024"; - let page_title = format!("{}/{}", wiki.plenum_main_page_name, page_title); - wiki.new_wiki_page(&page_title, &pad_converted, true)?; + let page_title = create_page_title(date); + let full_page_title = format!("{}/{}", wiki.plenum_main_page_name, page_title); + + wiki.new_wiki_page(&full_page_title, &pad_converted, true)?; - // Download the main plenum page and insert the new page_title as a link - verboseln!("wiki: updating main plenum page"); - - wiki.update_plenum_page(&page_title)?; verboseln!("Finished successfully with wiki"); Ok(()) } @@ -332,14 +435,10 @@ fn pandoc_convert(markdown: String) -> Result> { } } -/* -fn create_title (nächster_plenumstermin: String) { - let date_simple = NaiveDate::from(nächster_plenumstermin); - let wiki_page_title = format!("{} {} {}", date_simple.day(), LongMonthName[date_simple.month()], date_simple.year()); +fn create_page_title(date: &NaiveDate) -> String { + date.format("%d. %B %Y").to_string() } - */ - /// Deserialization must be done this way because the response contains /// two `\\` characters in both the login and csrf tokens, which breaks the /// usual deserialization From 20b8ce40de39452f8ea17f76830fcd9b8c602ac2 Mon Sep 17 00:00:00 2001 From: murmeldin Date: Tue, 10 Dec 2024 21:04:14 +0100 Subject: [PATCH 09/10] restructuring of mediawiki (still in progress) --- src/main.rs | 2 +- src/mediawiki.rs | 91 +++++++++++++++++++++++++++++++----------------- 2 files changed, 61 insertions(+), 32 deletions(-) diff --git a/src/main.rs b/src/main.rs index 5bea989..44c2687 100644 --- a/src/main.rs +++ b/src/main.rs @@ -178,7 +178,7 @@ fn main() -> Result<(), Box> { &config["wiki-http-password"], &config["wiki-api-user"], &config["wiki-api-secret"], - is_dry_run(), + false, // is_dry_run(), // TODO: Remove this false in order for actually letting dry_run affecting if mediawiki is run &config["wiki-plenum-page"], ); trace_var_!(wiki); diff --git a/src/mediawiki.rs b/src/mediawiki.rs index d43971e..99092b1 100644 --- a/src/mediawiki.rs +++ b/src/mediawiki.rs @@ -5,6 +5,7 @@ use chrono::{Datelike, NaiveDate, Utc}; use colored::Colorize; use reqwest::blocking::Client; use serde::Deserialize; +use serde_json::{json, Value}; use crate::{trace_var, verboseln}; use crate::config_spec::{CfgField, CfgGroup}; @@ -97,30 +98,57 @@ impl Mediawiki { client: Client::builder().cookie_store(true).build().unwrap(), } } - pub fn get_login_token(&self) -> Result<(), Box> { - let url = - format!("{}/api.php?", self.server_url); - let params: Box<[(&str, &str)]> = Box::from( [ - ("format", "json"), + pub fn login (&self) -> Result<(), Box> { + let url = format!("{}/api.php?", self.server_url); + // retrieve login token first + let params_0: Box<[(&str, &str)]> = Box::from([ + ("action", "query"), ("meta", "tokens"), ("type", "login"), - ("action", "query") + ("format", "json") ]); - let resp = self.make_request(url, params, ValidRequestTypes::Get)?; - let response_deserialized: QueryResponseLogin = serde_json::from_str(&resp)?; - self.login_token.set(response_deserialized.query.tokens.logintoken)?; - Ok(()) - } - pub fn login (&self) -> Result> { - let url = format!("{}/api.php?", self.server_url); - let params: Box<[(&str, &str)]> = Box::from([ - ("lgname", self.api_user.as_str()), - ("lgpassword", self.api_secret.as_str()), - ("lgtoken", self.login_token.get().unwrap()), - ("action", "login") + verboseln!("Login params: {:?}", params_0); + let resp_0: String = self.make_request(url.clone(), params_0, ValidRequestTypes::Get)?; + verboseln!("Raw response login_0: {}", resp_0.yellow()); + let resp_0_deserialized: serde_json::Value = serde_json::from_str(&resp_0)?; + verboseln!("login0 deserialized"); + + let login_token = resp_0_deserialized + .pointer("/query/tokens/logintoken") + .and_then(|v| v.as_str()) + .map(|token| token.replace("+\\", "")) + .ok_or("Login token not found")?; + + self.login_token.set(login_token)?; + + verboseln!("login0 finished"); + let login_token: String = self.login_token.clone().into_inner().unwrap(); + verboseln!("using {login_token} as login"); + let params_1: Box<[(&str, &str)]> = Box::from([ + ("action", "login"), + ("lgname", &self.api_user), + ("lgpassword", &self.api_secret), + ("lgtoken", &login_token), + ("format", "json") ]); - let resp: Result> = self.make_request(url, params, ValidRequestTypes::Post); - Ok(resp?) + verboseln!("Login params: {:?}", params_1); + let resp_1: String = self.client + .post(&url) + // .basic_auth(&self.http_user, Some(&self.http_password)) // TODO: ZU TESTZWECKEN ENTFERNT + .form(¶ms_1) + .send()? + .text()?; + verboseln!("Raw response login_1: {}", resp_1.yellow()); + let resp_1_deserialized: serde_json::Value = serde_json::from_str(&resp_1)?; + if let Some(result) = resp_1_deserialized.get("login").and_then(|l| l.get("result")) { + if result.as_str() == Some("Success") { + Ok(()) + } else { + Err(format!("Login failed. Response: {}", resp_1_deserialized).into()) + } + } else { + Err(format!("Unexpected login response: {}", resp_1_deserialized).into()) + } } pub fn get_csrf_token(&self) -> Result<(), Box> { let url = @@ -128,12 +156,18 @@ impl Mediawiki { let params: Box<[(&str, &str)]> = Box::from([ ("format", "json"), ("meta", "tokens"), - ("formatversion", "2"), ("action", "query") ]); let resp: String = self.make_request(url, params, ValidRequestTypes::Get)?; + verboseln!("Raw response csrf: {}", resp); let response_deserialized: QueryResponseCsrf = serde_json::from_str(&resp)?; - self.csrf_token.set(response_deserialized.query.tokens.csrftoken)?; + let token = response_deserialized.query.tokens.csrftoken; + verboseln!("Parsed token: '{}'", token); + if token == "+\\" { + return Err("Failed to parse CSRF token. Response was only '+\\'. Please check your Bot API credentials".into()); + } + self.csrf_token.set(token)?; + verboseln!("CSRF token acquired: {}", self.csrf_token.get().unwrap()); Ok(()) } @@ -144,7 +178,7 @@ impl Mediawiki { self .client .get(url) - //.basic_auth(&self.http_user, Some(&self.http_password)) ZU TESTZWECKEN ENTFERNT + //.basic_auth(&self.http_user, Some(&self.http_password)) // TODO: ZU TESTZWECKEN ENTFERNT .query(¶ms) .send() } @@ -154,8 +188,8 @@ impl Mediawiki { self .client .post(url) - //.basic_auth(&self.http_user, Some(&self.http_password)) ZU TESTZWECKEN ENTFERNT - .json(¶ms_map) + //.basic_auth(&self.http_user, Some(&self.http_password)) // TODO: ZU TESTZWECKEN ENTFERNT + .query(¶ms_map) .send() } } @@ -313,7 +347,6 @@ impl Mediawiki { ); // Ensure login token and CSRF token are set - self.get_login_token()?; let _login_result = self.login()?; self.get_csrf_token()?; @@ -382,7 +415,6 @@ impl Mediawiki { } } } - } /// This function runs at the end of do_protocol() and is responsible for @@ -394,9 +426,7 @@ pub fn pad_ins_wiki(old_pad_content: String, wiki: &Mediawiki, plenum_date: &Nai let date = plenum_date; wiki.test_wiki_write()?; // Login to Wiki and get required tokens for logging in and writing - wiki.get_login_token()?; - verboseln!("Login token acquired."); - + verboseln!("logging in..."); let login_result = wiki.login()?; verboseln!("Login done."); trace_var!(login_result); @@ -463,7 +493,6 @@ struct TokensLogin { #[derive(Deserialize)] struct QueryResponseCsrf { #[allow(dead_code)] - batchcomplete: bool, query: crate::mediawiki::QueryTokensCsrf, } From 9583d9c37da1b4011bd83ed1e7131e2a7dd8955e Mon Sep 17 00:00:00 2001 From: murmeldin Date: Tue, 10 Dec 2024 21:21:20 +0100 Subject: [PATCH 10/10] removed Cargo.lock and shell.nix from .gitignore --- .gitignore | 3 +- Cargo.lock | 2896 ++++++++++++++++++++++++++++++++++++++++++++++++++++ shell.nix | 25 + 3 files changed, 2922 insertions(+), 2 deletions(-) create mode 100644 Cargo.lock create mode 100644 shell.nix diff --git a/.gitignore b/.gitignore index b1bc3f1..32418c4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,8 @@ /target /.idea /src/debug_emails.txt -Cargo.lock *.sqlite *.sqlite3 .direnv -shell.nix pandoc*.txt +.envrc diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..0feffcd --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2896 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy 0.7.35", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", +] + +[[package]] +name = "arraydeque" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" + +[[package]] +name = "async-trait" +version = "0.1.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" + +[[package]] +name = "cc" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.6", +] + +[[package]] +name = "chumsky" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eebd66744a15ded14960ab4ccdbfb51ad3b81f51f3f04a80adac98c985396c9" +dependencies = [ + "hashbrown 0.14.5", + "stacker", +] + +[[package]] +name = "clap" +version = "4.5.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "colored" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +dependencies = [ + "lazy_static", + "windows-sys 0.48.0", +] + +[[package]] +name = "config" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68578f196d2a33ff61b27fae256c3164f65e36382648e30666dde05b8cc9dfdf" +dependencies = [ + "async-trait", + "convert_case", + "json5", + "nom", + "pathdiff", + "ron", + "rust-ini", + "serde", + "serde_json", + "toml", + "yaml-rust2", +] + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.15", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9" +dependencies = [ + "cookie", + "document-features", + "idna", + "log", + "publicsuffix", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dlv-list" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +dependencies = [ + "const-random", +] + +[[package]] +name = "document-features" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" +dependencies = [ + "litrs", +] + +[[package]] +name = "email-encoding" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea3d894bbbab314476b265f9b2d46bf24b123a36dd0e96b06a1b49545b9d9dcc" +dependencies = [ + "base64 0.22.1", + "memchr", +] + +[[package]] +name = "email_address" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "fastrand" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.0-rc.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a78f88e84d239c7f2619ae8b091603c26208e1cb322571f5a29d6806f56ee5e" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "rustix", + "wasi 0.13.3+wasi-0.2.2", + "wasm-bindgen", + "windows-targets 0.52.6", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "headers" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" +dependencies = [ + "base64 0.21.7", + "bytes", + "headers-core", + "http", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" +dependencies = [ + "http", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "hostname" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9c7c7c8ac16c798734b8a24560c1362120597c40d5e1459f09498f8f6c8f2ba" +dependencies = [ + "cfg-if", + "libc", + "windows", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +dependencies = [ + "equivalent", + "hashbrown 0.15.2", +] + +[[package]] +name = "ipnet" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "js-sys" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a865e038f7f6ed956f788f0d7d60c541fff74c7bd74272c5d4cf15c63743e705" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lettre" +version = "0.11.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0161e452348e399deb685ba05e55ee116cae9410f4f51fe42d597361444521d9" +dependencies = [ + "base64 0.22.1", + "chumsky", + "email-encoding", + "email_address", + "fastrand", + "futures-util", + "hostname", + "httpdate", + "idna", + "mime", + "native-tls", + "nom", + "percent-encoding", + "quoted_printable", + "socket2", + "tokio", + "url", +] + +[[package]] +name = "libc" +version = "0.2.167" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" + +[[package]] +name = "libsqlite3-sys" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" +dependencies = [ + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "mediawiki" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df5d9c97ad0abca7d5d4517c7435ab52936bde6d115d77d7271d208f44a51e6e" +dependencies = [ + "base64 0.22.1", + "chrono", + "config", + "futures", + "hmac", + "nanoid", + "reqwest", + "serde", + "serde_json", + "sha1", + "tokio", + "unicode-case-mapping", + "url", + "urlencoding", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", +] + +[[package]] +name = "nanoid" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ffa00dec017b5b1a8b7cf5e2c008bfda1aa7e0697ac1508b491fdf2622fb4d8" +dependencies = [ + "rand 0.8.5", +] + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "openssl" +version = "0.10.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ordered-multimap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" +dependencies = [ + "dlv-list", + "hashbrown 0.14.5", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" +dependencies = [ + "memchr", + "thiserror 1.0.69", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "plenum_bot" +version = "0.1.0" +dependencies = [ + "chrono", + "clap", + "colored", + "futures", + "headers", + "lettre", + "log", + "mediawiki", + "nom", + "rand 0.9.0-beta.1", + "regex", + "reqwest", + "rpassword", + "rusqlite", + "serde", + "serde_json", + "stdext", + "uuid", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy 0.7.35", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psl-types" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" + +[[package]] +name = "psm" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" +dependencies = [ + "cc", +] + +[[package]] +name = "publicsuffix" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf" +dependencies = [ + "idna", + "psl-types", +] + +[[package]] +name = "quinn" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.3", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" +dependencies = [ + "bytes", + "getrandom 0.2.15", + "rand 0.8.5", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.3", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a626c6807713b15cac82a6acaccd6043c9a5408c24baae07611fec3f243da" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "quoted_printable" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "640c9bd8497b02465aeef5375144c26062e0dcd5939dfcbb0f5db76cb8c17c73" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.0-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8478de76992f2825a1052cc2ae9d1401cdb62687761d4100ddd69a73dc3dc48" +dependencies = [ + "rand_chacha 0.9.0-beta.1", + "rand_core 0.9.0-beta.1", + "zerocopy 0.8.11", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f16da77124f4ee9fabd55ce6540866e9101431863b4876de58b68797f331adf2" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.0-beta.1", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.15", +] + +[[package]] +name = "rand_core" +version = "0.9.0-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a98fa0b8309344136abe6244130311e76997e546f76fae8054422a7539b43df7" +dependencies = [ + "getrandom 0.3.0-rc.0", + "zerocopy 0.8.11", +] + +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "reqwest" +version = "0.12.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" +dependencies = [ + "base64 0.22.1", + "bytes", + "cookie", + "cookie_store", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pemfile", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tokio-rustls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "windows-registry", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.15", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "ron" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" +dependencies = [ + "base64 0.21.7", + "bitflags", + "serde", + "serde_derive", +] + +[[package]] +name = "rpassword" +version = "7.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f" +dependencies = [ + "libc", + "rtoolbox", + "windows-sys 0.48.0", +] + +[[package]] +name = "rtoolbox" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "rusqlite" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae" +dependencies = [ + "bitflags", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink 0.9.1", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rust-ini" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0698206bcb8882bf2a9ecb4c1e7785db57ff052297085a6efd4fe42302068a" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" + +[[package]] +name = "rustix" +version = "0.38.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.23.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +dependencies = [ + "web-time", +] + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stacker" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.59.0", +] + +[[package]] +name = "stdext" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4af28eeb7c18ac2dbdb255d40bee63f203120e1db6b0024b177746ebec7049c1" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tempfile" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +dependencies = [ + "thiserror-impl 2.0.3", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.41.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "unicode-case-mapping" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b92e07ac57786e12073609c8a91417884306b753d974108ca48c8434ed9b99cb" + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +dependencies = [ + "getrandom 0.2.15", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d15e63b4482863c109d70a7b8706c1e364eb6ea449b201a76c5b89cedcec2d5c" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d36ef12e3aaca16ddd3f67922bc63e48e953f126de60bd33ccc0101ef9998cd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dfaf8f50e5f293737ee323940c7d8b08a66a95a419223d9f41610ca08b0833d" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "705440e08b42d3e4b36de7d66c944be628d579796b8090bfa3471478a2260051" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" + +[[package]] +name = "web-sys" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a98bc3c33f0fe7e59ad7cd041b89034fa82a7c2d4365ca538dda6cdaf513863c" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.26.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "yaml-rust2" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8902160c4e6f2fb145dbe9d6760a75e3c9522d8bf796ed7047c85919ac7115f8" +dependencies = [ + "arraydeque", + "encoding_rs", + "hashlink 0.8.4", +] + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cce3b5629d87654b53a49002acc2ce64aa5aa7255f5c718374a37ac7fd98c218" +dependencies = [ + "zerocopy-derive 0.8.11", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74a82c26c3986af2623ec9eb890ff4aa19c006e30a1133dc9bd1830ec1612e20" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..42abca8 --- /dev/null +++ b/shell.nix @@ -0,0 +1,25 @@ +{pkgs ? import {}}: let + rust-toolchain = pkgs.symlinkJoin { + name = "rust-toolchain"; + paths = with pkgs; [rustc cargo rustPlatform.rustcSrc rustfmt clippy]; + }; +in + pkgs.mkShell { + nativeBuildInputs = with pkgs.buildPackages; [ + rust-toolchain + + pkg-config + xe + xz + + cargo-tarpaulin + openssl + sqlite + gcc + gnumake + + # dotnet-sdk_8 + ]; + + RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}"; + }