knowledge-search

git clone git://archive.git.mtrnord.blog/MTRNord/knowledge-search.git
Log | Files | Refs | README | LICENSE

config.rs (11506B)


      1 use std::io::Write;
      2 
      3 use kdl::{KdlDocument, KdlEntry, KdlIdentifier, KdlNode};
      4 use miette::{Diagnostic, NamedSource, SourceSpan};
      5 use thiserror::Error;
      6 
      7 pub struct Config {
      8     pub homeserver_url: String,
      9     pub indradb_endpoint: String,
     10     pub auth_data: AuthData,
     11 }
     12 
     13 pub enum AuthData {
     14     UsernamePassword(String, String),
     15     AccessToken(String, String, String),
     16 }
     17 
     18 #[derive(Debug, Error, Diagnostic)]
     19 #[error("Incorrect Config")]
     20 #[diagnostic()]
     21 pub struct MissingFieldError {
     22     #[source_code]
     23     src: NamedSource,
     24 
     25     #[help]
     26     advice: String,
     27 
     28     #[label]
     29     snippet: SourceSpan,
     30 }
     31 
     32 fn parse(input: &str) -> miette::Result<KdlDocument> {
     33     Ok(input.parse::<KdlDocument>()?)
     34 }
     35 
     36 fn missing_field(source: String, snippet: &SourceSpan, help: &str) {
     37     let error: miette::Result<()> = Err(MissingFieldError {
     38         src: NamedSource::new("config.kdl", source),
     39         snippet: *snippet,
     40         advice: help.to_string(),
     41     }
     42     .into());
     43 
     44     // This is stupid. But oh well.
     45     if let Err(error) = error {
     46         eprintln!("{error:?}");
     47         std::process::exit(1);
     48     }
     49 }
     50 
     51 #[allow(clippy::unwrap_used)]
     52 #[allow(clippy::too_many_lines)]
     53 pub fn load() -> Config {
     54     let source = std::fs::read_to_string("config.kdl")
     55         .expect("Unable to open config.kdl file. Is it present?");
     56     match parse(&source) {
     57         Ok(config) => {
     58             let matrix = config.get("matrix");
     59             if let Some(matrix) = matrix {
     60                 if let Some(matrix_children) = matrix.children() {
     61                     let homeserver_url_entry = matrix_children.get("homeserver_url");
     62                     let homeserver_url = if let Some(homeserver_url_entry) = homeserver_url_entry {
     63                         if let Some(homeserver_url) = homeserver_url_entry.entries().first() {
     64                             if let Some(homeserver_url) = homeserver_url.value().as_string() {
     65                                 homeserver_url.to_string()
     66                             } else {
     67                                 missing_field(source, homeserver_url.span(),"\"homeserver_url\" field has incorrect type. Make sure it is actually a string. Try adding quotes.");
     68                                 unreachable!();
     69                             }
     70                         } else {
     71                             missing_field(source, homeserver_url_entry.span(),"\"homeserver_url\" field has no value. Make sure it is actually a string. Try adding quotes.");
     72                             unreachable!();
     73                         }
     74                     } else {
     75                         missing_field(source,matrix_children.span(),"Missing \"homeserver_url\" field. See the example config for how to define it");
     76                         unreachable!();
     77                     };
     78                     let username_entry = matrix_children.get("username");
     79                     let username = if let Some(username_entry) = username_entry {
     80                         if let Some(username) = username_entry.entries().first() {
     81                             if let Some(username) = username.value().as_string() {
     82                                 username.to_string()
     83                             } else {
     84                                 missing_field(source, username.span(),"\"username\" field has incorrect type. Make sure it is actually a string. Try adding quotes.");
     85                                 unreachable!();
     86                             }
     87                         } else {
     88                             missing_field(source, username_entry.span(),"\"username\" field has no value. Make sure it is actually a string. Try adding quotes.");
     89                             unreachable!();
     90                         }
     91                     } else {
     92                         missing_field(source,matrix_children.span(),"Missing \"username\" field. See the example config for how to define it");
     93                         unreachable!();
     94                     };
     95                     let password_entry = matrix_children.get("password");
     96                     let password = if let Some(password_entry) = password_entry {
     97                         if let Some(password) = password_entry.entries().first() {
     98                             if let Some(password) = password.value().as_string() {
     99                                 Some(password.to_string())
    100                             } else {
    101                                 missing_field(source, password.span(),"\"password\" field has incorrect type. Make sure it is actually a string. Try adding quotes.");
    102                                 unreachable!();
    103                             }
    104                         } else {
    105                             missing_field(source, password_entry.span(),"\"password\" field has no value. Make sure it is actually a string. Try adding quotes.");
    106                             unreachable!();
    107                         }
    108                     } else {
    109                         None
    110                     };
    111 
    112                     let indradb_address_entry = config.get("indradb_address");
    113                     let indradb_endpoint = if let Some(indradb_address_entry) =
    114                         indradb_address_entry
    115                     {
    116                         if let Some(indradb_address) = indradb_address_entry.entries().first() {
    117                             if let Some(indradb_address) = indradb_address.value().as_string() {
    118                                 indradb_address.to_string()
    119                             } else {
    120                                 missing_field(source, indradb_address.span(),"\"indradb_address\" field has incorrect type. Make sure it is actually a string. Try adding quotes.");
    121                                 unreachable!();
    122                             }
    123                         } else {
    124                             missing_field(source, indradb_address_entry.span(),"\"indradb_address\" field has no value. Make sure it is actually a string. Try adding quotes.");
    125                             unreachable!();
    126                         }
    127                     } else {
    128                         missing_field(source,config.span(),"Missing \"indradb_address\" field. See the example config for how to define it");
    129                         unreachable!();
    130                     };
    131 
    132                     let access_token_entry = matrix_children.get("access_token");
    133                     let access_token = if let Some(access_token_entry) = access_token_entry {
    134                         if let Some(access_token) = access_token_entry.entries().first() {
    135                             if let Some(access_token) = access_token.value().as_string() {
    136                                 Some(access_token.to_string())
    137                             } else {
    138                                 missing_field(source, access_token.span(),"\"access_token\" field has incorrect type. Please reset it to be username and password.");
    139                                 unreachable!();
    140                             }
    141                         } else {
    142                             missing_field(source, access_token_entry.span(),"\"access_token\" field has no value. Please reset it to be username and password.");
    143                             unreachable!();
    144                         }
    145                     } else {
    146                         None
    147                     };
    148                     let device_id_entry = matrix_children.get("device_id");
    149                     let device_id = if let Some(device_id_entry) = device_id_entry {
    150                         if let Some(device_id) = device_id_entry.entries().first() {
    151                             if let Some(device_id) = device_id.value().as_string() {
    152                                 Some(device_id.to_string())
    153                             } else {
    154                                 missing_field(source, device_id.span(),"\"device_id\" field has incorrect type. Please reset it to be username and password.");
    155                                 unreachable!();
    156                             }
    157                         } else {
    158                             missing_field(source, device_id_entry.span(),"\"device_id\" field has no value. Please reset it to be username and password.");
    159                             unreachable!();
    160                         }
    161                     } else {
    162                         None
    163                     };
    164 
    165                     let auth_data = if let Some(password) = password {
    166                         AuthData::UsernamePassword(username, password)
    167                     } else {
    168                         // In theory this is safe :D
    169                         AuthData::AccessToken(username, access_token.unwrap(), device_id.unwrap())
    170                     };
    171 
    172                     Config {
    173                         homeserver_url,
    174                         indradb_endpoint,
    175                         auth_data,
    176                     }
    177                 } else {
    178                     missing_field(
    179                         source,
    180                         matrix.span(),
    181                         "Missing \"matrix\" choldren. See the example config for how to define it",
    182                     );
    183                     unreachable!();
    184                 }
    185             } else {
    186                 missing_field(
    187                     source,
    188                     config.span(),
    189                     "Missing \"matrix\" section. See the example config for how to define it",
    190                 );
    191                 unreachable!();
    192             }
    193         }
    194         Err(error) => {
    195             eprintln!("{error:?}");
    196             std::process::exit(1);
    197         }
    198     }
    199 }
    200 
    201 pub fn write_access_token(access_token: String, device_id: String) -> color_eyre::Result<()> {
    202     let source = std::fs::read_to_string("config.kdl")
    203         .expect("Unable to open config.kdl file. Is it present?");
    204     let config = parse(&source);
    205     match config {
    206         Ok(mut config) => {
    207             let matrix = config.get_mut("matrix");
    208             if let Some(matrix) = matrix {
    209                 if let Some(matrix_children) = matrix.children_mut() {
    210                     let matrix_nodes = matrix_children.nodes_mut();
    211                     let access_token_identifier = KdlIdentifier::from("access_token");
    212                     let device_id_identifier = KdlIdentifier::from("device_id");
    213 
    214                     let mut access_token_node = KdlNode::new(access_token_identifier);
    215                     let access_token_entry = KdlEntry::new(access_token);
    216                     access_token_node.entries_mut().push(access_token_entry);
    217 
    218                     let mut device_id_node = KdlNode::new(device_id_identifier);
    219                     let device_id_entry = KdlEntry::new(device_id);
    220                     device_id_node.entries_mut().push(device_id_entry);
    221 
    222                     matrix_nodes.push(access_token_node);
    223                     matrix_nodes.push(device_id_node);
    224                     matrix_nodes.retain(|node| node.name().value() != "password");
    225 
    226                     matrix_nodes.sort_by(sort_by_name);
    227                 }
    228                 matrix.fmt();
    229             }
    230 
    231             let mut file = std::fs::OpenOptions::new()
    232                 .write(true)
    233                 .truncate(true)
    234                 .open("config.kdl")?;
    235 
    236             file.write_all(config.to_string().as_bytes())?;
    237             file.flush()?;
    238             Ok(())
    239         }
    240         Err(error) => {
    241             let err = format!("{error:?}");
    242             Err(color_eyre::eyre::eyre!("{}", err))
    243         }
    244     }
    245 }
    246 
    247 fn sort_by_name(x: &KdlNode, y: &KdlNode) -> std::cmp::Ordering {
    248     x.name().value().cmp(y.name().value())
    249 }