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 }