notedeck

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

args.rs (8641B)


      1 use crate::column::{ColumnKind, PubkeySource};
      2 use crate::filter::FilterState;
      3 use crate::timeline::Timeline;
      4 use enostr::{Filter, Keypair, Pubkey, SecretKey};
      5 use nostrdb::Ndb;
      6 use tracing::{debug, error, info};
      7 
      8 pub struct Args {
      9     pub columns: Vec<ArgColumn>,
     10     pub relays: Vec<String>,
     11     pub is_mobile: Option<bool>,
     12     pub keys: Vec<Keypair>,
     13     pub since_optimize: bool,
     14     pub light: bool,
     15     pub debug: bool,
     16     pub dbpath: Option<String>,
     17 }
     18 
     19 impl Args {
     20     pub fn parse(args: &[String]) -> Self {
     21         let mut res = Args {
     22             columns: vec![],
     23             relays: vec![],
     24             is_mobile: None,
     25             keys: vec![],
     26             light: false,
     27             since_optimize: true,
     28             debug: false,
     29             dbpath: None,
     30         };
     31 
     32         let mut i = 0;
     33         let len = args.len();
     34         while i < len {
     35             let arg = &args[i];
     36 
     37             if arg == "--mobile" {
     38                 res.is_mobile = Some(true);
     39             } else if arg == "--light" {
     40                 res.light = true;
     41             } else if arg == "--dark" {
     42                 res.light = false;
     43             } else if arg == "--debug" {
     44                 res.debug = true;
     45             } else if arg == "--pub" || arg == "--npub" {
     46                 i += 1;
     47                 let pubstr = if let Some(next_arg) = args.get(i) {
     48                     next_arg
     49                 } else {
     50                     error!("sec argument missing?");
     51                     continue;
     52                 };
     53 
     54                 if let Ok(pk) = Pubkey::parse(pubstr) {
     55                     res.keys.push(Keypair::only_pubkey(pk));
     56                 } else {
     57                     error!(
     58                         "failed to parse {} argument. Make sure to use hex or npub.",
     59                         arg
     60                     );
     61                 }
     62             } else if arg == "--sec" || arg == "--nsec" {
     63                 i += 1;
     64                 let secstr = if let Some(next_arg) = args.get(i) {
     65                     next_arg
     66                 } else {
     67                     error!("sec argument missing?");
     68                     continue;
     69                 };
     70 
     71                 if let Ok(sec) = SecretKey::parse(secstr) {
     72                     res.keys.push(Keypair::from_secret(sec));
     73                 } else {
     74                     error!(
     75                         "failed to parse {} argument. Make sure to use hex or nsec.",
     76                         arg
     77                     );
     78                 }
     79             } else if arg == "--no-since-optimize" {
     80                 res.since_optimize = false;
     81             } else if arg == "--filter" {
     82                 i += 1;
     83                 let filter = if let Some(next_arg) = args.get(i) {
     84                     next_arg
     85                 } else {
     86                     error!("filter argument missing?");
     87                     continue;
     88                 };
     89 
     90                 if let Ok(filter) = Filter::from_json(filter) {
     91                     res.columns.push(ArgColumn::Generic(vec![filter]));
     92                 } else {
     93                     error!("failed to parse filter '{}'", filter);
     94                 }
     95             } else if arg == "--dbpath" {
     96                 i += 1;
     97                 let path = if let Some(next_arg) = args.get(i) {
     98                     next_arg
     99                 } else {
    100                     error!("dbpath argument missing?");
    101                     continue;
    102                 };
    103                 res.dbpath = Some(path.clone());
    104             } else if arg == "-r" || arg == "--relay" {
    105                 i += 1;
    106                 let relay = if let Some(next_arg) = args.get(i) {
    107                     next_arg
    108                 } else {
    109                     error!("relay argument missing?");
    110                     continue;
    111                 };
    112                 res.relays.push(relay.clone());
    113             } else if arg == "--column" || arg == "-c" {
    114                 i += 1;
    115                 let column_name = if let Some(next_arg) = args.get(i) {
    116                     next_arg
    117                 } else {
    118                     error!("column argument missing");
    119                     continue;
    120                 };
    121 
    122                 if let Some(rest) = column_name.strip_prefix("contacts:") {
    123                     if let Ok(pubkey) = Pubkey::parse(rest) {
    124                         info!("contact column for user {}", pubkey.hex());
    125                         res.columns.push(ArgColumn::Column(ColumnKind::contact_list(
    126                             PubkeySource::Explicit(pubkey),
    127                         )))
    128                     } else {
    129                         error!("error parsing contacts pubkey {}", rest);
    130                         continue;
    131                     }
    132                 } else if column_name == "contacts" {
    133                     res.columns.push(ArgColumn::Column(ColumnKind::contact_list(
    134                         PubkeySource::DeckAuthor,
    135                     )))
    136                 } else if let Some(notif_pk_str) = column_name.strip_prefix("notifications:") {
    137                     if let Ok(pubkey) = Pubkey::parse(notif_pk_str) {
    138                         info!("got notifications column for user {}", pubkey.hex());
    139                         res.columns
    140                             .push(ArgColumn::Column(ColumnKind::notifications(
    141                                 PubkeySource::Explicit(pubkey),
    142                             )))
    143                     } else {
    144                         error!("error parsing notifications pubkey {}", notif_pk_str);
    145                         continue;
    146                     }
    147                 } else if column_name == "notifications" {
    148                     debug!("got notification column for default user");
    149                     res.columns
    150                         .push(ArgColumn::Column(ColumnKind::notifications(
    151                             PubkeySource::DeckAuthor,
    152                         )))
    153                 } else if column_name == "profile" {
    154                     debug!("got profile column for default user");
    155                     res.columns.push(ArgColumn::Column(ColumnKind::profile(
    156                         PubkeySource::DeckAuthor,
    157                     )))
    158                 } else if column_name == "universe" {
    159                     debug!("got universe column");
    160                     res.columns.push(ArgColumn::Column(ColumnKind::Universe))
    161                 } else if let Some(profile_pk_str) = column_name.strip_prefix("profile:") {
    162                     if let Ok(pubkey) = Pubkey::parse(profile_pk_str) {
    163                         info!("got profile column for user {}", pubkey.hex());
    164                         res.columns.push(ArgColumn::Column(ColumnKind::profile(
    165                             PubkeySource::Explicit(pubkey),
    166                         )))
    167                     } else {
    168                         error!("error parsing profile pubkey {}", profile_pk_str);
    169                         continue;
    170                     }
    171                 }
    172             } else if arg == "--filter-file" || arg == "-f" {
    173                 i += 1;
    174                 let filter_file = if let Some(next_arg) = args.get(i) {
    175                     next_arg
    176                 } else {
    177                     error!("filter file argument missing?");
    178                     continue;
    179                 };
    180 
    181                 let data = if let Ok(data) = std::fs::read(filter_file) {
    182                     data
    183                 } else {
    184                     error!("failed to read filter file '{}'", filter_file);
    185                     continue;
    186                 };
    187 
    188                 if let Some(filter) = std::str::from_utf8(&data)
    189                     .ok()
    190                     .and_then(|s| Filter::from_json(s).ok())
    191                 {
    192                     res.columns.push(ArgColumn::Generic(vec![filter]));
    193                 } else {
    194                     error!("failed to parse filter in '{}'", filter_file);
    195                 }
    196             }
    197 
    198             i += 1;
    199         }
    200 
    201         if res.columns.is_empty() {
    202             let ck = ColumnKind::contact_list(PubkeySource::DeckAuthor);
    203             info!("No columns set, setting up defaults: {:?}", ck);
    204             res.columns.push(ArgColumn::Column(ck));
    205         }
    206 
    207         res
    208     }
    209 }
    210 
    211 /// A way to define columns from the commandline. Can be column kinds or
    212 /// generic queries
    213 pub enum ArgColumn {
    214     Column(ColumnKind),
    215     Generic(Vec<Filter>),
    216 }
    217 
    218 impl ArgColumn {
    219     pub fn into_timeline(self, ndb: &Ndb, user: Option<&[u8; 32]>) -> Option<Timeline> {
    220         match self {
    221             ArgColumn::Generic(filters) => Some(Timeline::new(
    222                 ColumnKind::Generic,
    223                 FilterState::ready(filters),
    224             )),
    225             ArgColumn::Column(ck) => ck.into_timeline(ndb, user),
    226         }
    227     }
    228 }