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 }