notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

args.rs (7377B)


      1 use std::collections::BTreeSet;
      2 
      3 use crate::timeline::TimelineKind;
      4 use enostr::{Filter, Pubkey};
      5 use tracing::{debug, error, info};
      6 
      7 pub struct ColumnsArgs {
      8     pub columns: Vec<ArgColumn>,
      9     pub since_optimize: bool,
     10     pub textmode: bool,
     11     pub scramble: bool,
     12     pub no_media: bool,
     13 }
     14 
     15 impl ColumnsArgs {
     16     pub fn parse(args: &[String], deck_author: Option<&Pubkey>) -> (Self, BTreeSet<String>) {
     17         let mut unrecognized_args = BTreeSet::new();
     18         let mut res = Self {
     19             columns: vec![],
     20             since_optimize: true,
     21             textmode: false,
     22             scramble: false,
     23             no_media: false,
     24         };
     25 
     26         let mut i = 0;
     27         let len = args.len();
     28         while i < len {
     29             let arg = &args[i];
     30 
     31             if arg == "--textmode" {
     32                 res.textmode = true;
     33             } else if arg == "--no-since-optimize" {
     34                 res.since_optimize = false;
     35             } else if arg == "--scramble" {
     36                 res.scramble = true;
     37             } else if arg == "--no-media" {
     38                 res.no_media = true;
     39             } else if arg == "--filter" {
     40                 i += 1;
     41                 let filter = if let Some(next_arg) = args.get(i) {
     42                     next_arg
     43                 } else {
     44                     error!("filter argument missing?");
     45                     continue;
     46                 };
     47 
     48                 if let Ok(filter) = Filter::from_json(filter) {
     49                     res.columns.push(ArgColumn::Generic(vec![filter]));
     50                 } else {
     51                     error!("failed to parse filter '{}'", filter);
     52                 }
     53             } else if arg == "--column" || arg == "-c" {
     54                 i += 1;
     55                 let column_name = if let Some(next_arg) = args.get(i) {
     56                     next_arg
     57                 } else {
     58                     error!("column argument missing");
     59                     continue;
     60                 };
     61 
     62                 if let Some(rest) = column_name.strip_prefix("contacts:") {
     63                     if let Ok(pubkey) = Pubkey::parse(rest) {
     64                         info!("contact column for user {}", pubkey.hex());
     65                         res.columns
     66                             .push(ArgColumn::Timeline(TimelineKind::contact_list(pubkey)))
     67                     } else {
     68                         error!("error parsing contacts pubkey {}", rest);
     69                         continue;
     70                     }
     71                 } else if column_name == "contacts" {
     72                     if let Some(deck_author) = deck_author {
     73                         res.columns
     74                             .push(ArgColumn::Timeline(TimelineKind::contact_list(
     75                                 deck_author.to_owned(),
     76                             )));
     77                     } else {
     78                         panic!("No accounts available, could not handle implicit pubkey contacts column");
     79                     }
     80                 } else if column_name == "search" {
     81                     i += 1;
     82                     let search = if let Some(next_arg) = args.get(i) {
     83                         next_arg
     84                     } else {
     85                         error!("search filter argument missing?");
     86                         continue;
     87                     };
     88 
     89                     res.columns.push(ArgColumn::Timeline(TimelineKind::search(
     90                         search.to_string(),
     91                     )));
     92                 } else if let Some(notif_pk_str) = column_name.strip_prefix("notifications:") {
     93                     if let Ok(pubkey) = Pubkey::parse(notif_pk_str) {
     94                         info!("got notifications column for user {}", pubkey.hex());
     95                         res.columns
     96                             .push(ArgColumn::Timeline(TimelineKind::notifications(pubkey)));
     97                     } else {
     98                         error!("error parsing notifications pubkey {}", notif_pk_str);
     99                         continue;
    100                     }
    101                 } else if column_name == "notifications" {
    102                     debug!("got notification column for default user");
    103                     if let Some(deck_author) = deck_author {
    104                         res.columns
    105                             .push(ArgColumn::Timeline(TimelineKind::notifications(
    106                                 deck_author.to_owned(),
    107                             )));
    108                     } else {
    109                         panic!("Tried to push notifications timeline with no available users");
    110                     }
    111                 } else if column_name == "profile" {
    112                     debug!("got profile column for default user");
    113                     if let Some(deck_author) = deck_author {
    114                         res.columns.push(ArgColumn::Timeline(TimelineKind::profile(
    115                             deck_author.to_owned(),
    116                         )));
    117                     } else {
    118                         panic!("Tried to push profile timeline with no available users");
    119                     }
    120                 } else if column_name == "universe" {
    121                     debug!("got universe column");
    122                     res.columns
    123                         .push(ArgColumn::Timeline(TimelineKind::Universe))
    124                 } else if let Some(profile_pk_str) = column_name.strip_prefix("profile:") {
    125                     if let Ok(pubkey) = Pubkey::parse(profile_pk_str) {
    126                         info!("got profile column for user {}", pubkey.hex());
    127                         res.columns
    128                             .push(ArgColumn::Timeline(TimelineKind::profile(pubkey)))
    129                     } else {
    130                         error!("error parsing profile pubkey {}", profile_pk_str);
    131                         continue;
    132                     }
    133                 }
    134             } else if arg == "--filter-file" || arg == "-f" {
    135                 i += 1;
    136                 let filter_file = if let Some(next_arg) = args.get(i) {
    137                     next_arg
    138                 } else {
    139                     error!("filter file argument missing?");
    140                     continue;
    141                 };
    142 
    143                 let data = if let Ok(data) = std::fs::read(filter_file) {
    144                     data
    145                 } else {
    146                     error!("failed to read filter file '{}'", filter_file);
    147                     continue;
    148                 };
    149 
    150                 if let Some(filter) = std::str::from_utf8(&data)
    151                     .ok()
    152                     .and_then(|s| Filter::from_json(s).ok())
    153                 {
    154                     res.columns.push(ArgColumn::Generic(vec![filter]));
    155                 } else {
    156                     error!("failed to parse filter in '{}'", filter_file);
    157                 }
    158             } else {
    159                 unrecognized_args.insert(arg.clone());
    160             }
    161 
    162             i += 1;
    163         }
    164 
    165         (res, unrecognized_args)
    166     }
    167 }
    168 
    169 /// A way to define columns from the commandline. Can be column kinds or
    170 /// generic queries
    171 #[derive(Debug)]
    172 pub enum ArgColumn {
    173     Timeline(TimelineKind),
    174     Generic(Vec<Filter>),
    175 }
    176 
    177 impl ArgColumn {
    178     pub fn into_timeline_kind(self) -> TimelineKind {
    179         match self {
    180             ArgColumn::Generic(_filters) => {
    181                 // TODO: fix generic filters by referencing some filter map
    182                 TimelineKind::Generic(0)
    183             }
    184             ArgColumn::Timeline(tk) => tk,
    185         }
    186     }
    187 }