murmeldin
e4444b8118
- mediawiki.rs: - new make_request function for easier post- and get-requests - new new_wiki_page function that creates new wiki pages - working csrf-Token-function - fixed read_txt_file function: Now properly working - pad_ins_wiki-function is now the main trigger for all the actions that should be done on day after plenum - hedgedoc.rs + mediawiki.rs: Better error handling for common offline edge case - main: ansi art moved in function, Timer and eta for time prediction during development - still to do in mediawiki.rs: Date logic for pad generation and function for modifying the main Plenum Page
150 lines
5.2 KiB
Rust
150 lines
5.2 KiB
Rust
use crate::config_spec::{CfgField, CfgGroup};
|
|
use regex::Regex;
|
|
use reqwest::blocking::Client;
|
|
use reqwest::blocking::Response;
|
|
use std::error::Error;
|
|
|
|
pub const CONFIG: CfgGroup<'static> = CfgGroup {
|
|
name: "hedgedoc",
|
|
description: "HedgeDoc markdown pad server settings",
|
|
fields: &[
|
|
CfgField::Default {
|
|
key: "server-url",
|
|
default: "https://md.berlin.ccc.de",
|
|
description: "Hedgedoc server storing the pads.",
|
|
},
|
|
CfgField::Default {
|
|
key: "template-name",
|
|
default: "plenum-template",
|
|
description: "Name of the pad containing the template to use.",
|
|
},
|
|
CfgField::Generated {
|
|
key: "last-id",
|
|
generator: make_pad_id,
|
|
generator_description: "Makes a new pad that's completely empty.",
|
|
description: "ID of last plenum's pad.",
|
|
},
|
|
CfgField::Generated {
|
|
key: "next-id",
|
|
generator: make_pad_id,
|
|
generator_description: "Makes a new pad that's completely empty.",
|
|
description: "ID of next plenum's pad.",
|
|
},
|
|
],
|
|
};
|
|
|
|
#[derive(Debug)]
|
|
pub struct HedgeDoc {
|
|
server_url: String,
|
|
is_dry_run: bool,
|
|
client: Client,
|
|
}
|
|
|
|
impl HedgeDoc {
|
|
pub fn new(server_url: &str, is_dry_run: bool) -> Self {
|
|
Self { server_url: server_url.to_string(), is_dry_run, client: Client::new() }
|
|
}
|
|
|
|
pub fn format_url(&self, pad_name: &str) -> String {
|
|
format!("{}/{}", self.server_url, pad_name)
|
|
}
|
|
|
|
fn format_action(&self, pad_name: &str, verb: &str) -> String {
|
|
format!("{}/{}/{}", self.server_url, pad_name, verb)
|
|
}
|
|
|
|
fn do_request(&self, url: &str) -> Result<Response, Box<dyn Error>> {
|
|
match self.client.get(url).send() {
|
|
Ok(response) => {
|
|
if response.status().is_success() {
|
|
Ok(response)
|
|
} else {
|
|
Err(format!("Failed to connect to hedgedoc server: HTTP status code {}", response.status()).into())
|
|
}
|
|
}
|
|
Err(e) => {
|
|
if e.is_connect() {
|
|
Err("Failed to connect to hedgedoc server. Please check your internet connection or the server URL.".into())
|
|
} else {
|
|
Err(format!("An error occurred while sending the request to the hedgedoc server: {}", e).into())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
fn get_id_from_response(&self, res: Response) -> String {
|
|
res.url().to_string().trim_start_matches(&format!("{}/", self.server_url)).to_string()
|
|
}
|
|
|
|
pub fn download(&self, pad_name: &str) -> Result<String, Box<dyn Error>> {
|
|
Ok(self.do_request(&self.format_action(pad_name, "download"))?.text()?)
|
|
}
|
|
|
|
pub fn create_pad(&self) -> Result<String, Box<dyn Error>> {
|
|
if self.is_dry_run {
|
|
todo!("NYI: sane dry-run behavior")
|
|
}
|
|
let res = self.do_request(&format!("{}/new", self.server_url)).unwrap();
|
|
if res.status().is_success() {
|
|
Ok(self.get_id_from_response(res))
|
|
} else {
|
|
Err(format!("Failed to create pad {}", res.status()).into())
|
|
}
|
|
}
|
|
|
|
pub fn import_note(&self, id: Option<&str>, content: String) -> Result<String, Box<dyn Error>> {
|
|
if self.is_dry_run {
|
|
todo!("NYI: sane dry-run behavior")
|
|
}
|
|
let url = match id {
|
|
Some(id) => self.format_url(&format!("new/{id}")),
|
|
None => self.format_url("new"),
|
|
};
|
|
|
|
let res =
|
|
self.client.post(&url).header("Content-Type", "text/markdown").body(content).send()?;
|
|
|
|
if res.status().is_success() {
|
|
Ok(self.get_id_from_response(res))
|
|
} else {
|
|
Err(format!("Failed to import note: {}", res.status()).into())
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn extract_metadata(pad_content: String) -> String {
|
|
let re_yaml = Regex::new(r"(?s)---\s*(.*?)\s*(?:\.\.\.|---)").unwrap();
|
|
re_yaml.captures_iter(&pad_content).map(|c| c[1].to_string()).collect::<Vec<_>>().join("\n")
|
|
}
|
|
|
|
pub fn strip_metadata(pad_content: String) -> String {
|
|
let re_yaml = Regex::new(r"(?s)---\s*.*?\s*(?:\.\.\.|---)").unwrap();
|
|
let pad_content = re_yaml.replace_all(&pad_content, "").to_string();
|
|
let re_comment = Regex::new(r"(?s)<!--.*?-->").unwrap();
|
|
re_comment.replace_all(&pad_content, "").to_string()
|
|
}
|
|
|
|
pub fn summarize(pad_content: String) -> String {
|
|
// 1. remove HTML comments
|
|
let pad_content = strip_metadata(pad_content);
|
|
// 2. accumulate topic lines
|
|
let re_header = Regex::new(r"^\s*##(#*) TOP ([\d.]+\s*.*?)\s*#*$").unwrap();
|
|
let mut result: Vec<String> = Vec::new();
|
|
for line in pad_content.lines() {
|
|
if let Some(captures) = re_header.captures(line) {
|
|
let indent = " ".repeat(captures.get(1).unwrap().as_str().len());
|
|
let title = captures.get(2).unwrap().as_str();
|
|
result.push(format!("{}{}", indent, title));
|
|
}
|
|
}
|
|
result.join("\n")
|
|
}
|
|
|
|
/// For the config, make a new pad ID (by actually making a pad.)
|
|
fn make_pad_id(
|
|
_key: &str, config: &crate::key_value::KeyValueStore, is_dry_run: bool,
|
|
) -> Result<String, Box<dyn Error>> {
|
|
HedgeDoc::new(&config.get("hedgedoc-server-url").unwrap(), is_dry_run).create_pad()
|
|
}
|