args.rs (6116B)
1 use notedeck::FilterState; 2 3 use crate::timeline::{PubkeySource, Timeline, TimelineKind, TimelineTab}; 4 use enostr::{Filter, Pubkey}; 5 use nostrdb::Ndb; 6 use tracing::{debug, error, info}; 7 8 pub struct ColumnsArgs { 9 pub columns: Vec<ArgColumn>, 10 pub since_optimize: bool, 11 pub textmode: bool, 12 } 13 14 impl ColumnsArgs { 15 pub fn parse(args: &[String]) -> Self { 16 let mut res = Self { 17 columns: vec![], 18 since_optimize: true, 19 textmode: false, 20 }; 21 22 let mut i = 0; 23 let len = args.len(); 24 while i < len { 25 let arg = &args[i]; 26 27 if arg == "--textmode" { 28 res.textmode = true; 29 } else if arg == "--no-since-optimize" { 30 res.since_optimize = false; 31 } else if arg == "--filter" { 32 i += 1; 33 let filter = if let Some(next_arg) = args.get(i) { 34 next_arg 35 } else { 36 error!("filter argument missing?"); 37 continue; 38 }; 39 40 if let Ok(filter) = Filter::from_json(filter) { 41 res.columns.push(ArgColumn::Generic(vec![filter])); 42 } else { 43 error!("failed to parse filter '{}'", filter); 44 } 45 } else if arg == "--column" || arg == "-c" { 46 i += 1; 47 let column_name = if let Some(next_arg) = args.get(i) { 48 next_arg 49 } else { 50 error!("column argument missing"); 51 continue; 52 }; 53 54 if let Some(rest) = column_name.strip_prefix("contacts:") { 55 if let Ok(pubkey) = Pubkey::parse(rest) { 56 info!("contact column for user {}", pubkey.hex()); 57 res.columns 58 .push(ArgColumn::Timeline(TimelineKind::contact_list( 59 PubkeySource::Explicit(pubkey), 60 ))) 61 } else { 62 error!("error parsing contacts pubkey {}", rest); 63 continue; 64 } 65 } else if column_name == "contacts" { 66 res.columns 67 .push(ArgColumn::Timeline(TimelineKind::contact_list( 68 PubkeySource::DeckAuthor, 69 ))) 70 } else if let Some(notif_pk_str) = column_name.strip_prefix("notifications:") { 71 if let Ok(pubkey) = Pubkey::parse(notif_pk_str) { 72 info!("got notifications column for user {}", pubkey.hex()); 73 res.columns 74 .push(ArgColumn::Timeline(TimelineKind::notifications( 75 PubkeySource::Explicit(pubkey), 76 ))) 77 } else { 78 error!("error parsing notifications pubkey {}", notif_pk_str); 79 continue; 80 } 81 } else if column_name == "notifications" { 82 debug!("got notification column for default user"); 83 res.columns 84 .push(ArgColumn::Timeline(TimelineKind::notifications( 85 PubkeySource::DeckAuthor, 86 ))) 87 } else if column_name == "profile" { 88 debug!("got profile column for default user"); 89 res.columns.push(ArgColumn::Timeline(TimelineKind::profile( 90 PubkeySource::DeckAuthor, 91 ))) 92 } else if column_name == "universe" { 93 debug!("got universe column"); 94 res.columns 95 .push(ArgColumn::Timeline(TimelineKind::Universe)) 96 } else if let Some(profile_pk_str) = column_name.strip_prefix("profile:") { 97 if let Ok(pubkey) = Pubkey::parse(profile_pk_str) { 98 info!("got profile column for user {}", pubkey.hex()); 99 res.columns.push(ArgColumn::Timeline(TimelineKind::profile( 100 PubkeySource::Explicit(pubkey), 101 ))) 102 } else { 103 error!("error parsing profile pubkey {}", profile_pk_str); 104 continue; 105 } 106 } 107 } else if arg == "--filter-file" || arg == "-f" { 108 i += 1; 109 let filter_file = if let Some(next_arg) = args.get(i) { 110 next_arg 111 } else { 112 error!("filter file argument missing?"); 113 continue; 114 }; 115 116 let data = if let Ok(data) = std::fs::read(filter_file) { 117 data 118 } else { 119 error!("failed to read filter file '{}'", filter_file); 120 continue; 121 }; 122 123 if let Some(filter) = std::str::from_utf8(&data) 124 .ok() 125 .and_then(|s| Filter::from_json(s).ok()) 126 { 127 res.columns.push(ArgColumn::Generic(vec![filter])); 128 } else { 129 error!("failed to parse filter in '{}'", filter_file); 130 } 131 } 132 133 i += 1; 134 } 135 136 res 137 } 138 } 139 140 /// A way to define columns from the commandline. Can be column kinds or 141 /// generic queries 142 #[derive(Debug)] 143 pub enum ArgColumn { 144 Timeline(TimelineKind), 145 Generic(Vec<Filter>), 146 } 147 148 impl ArgColumn { 149 pub fn into_timeline(self, ndb: &Ndb, user: Option<&[u8; 32]>) -> Option<Timeline> { 150 match self { 151 ArgColumn::Generic(filters) => Some(Timeline::new( 152 TimelineKind::Generic, 153 FilterState::ready(filters), 154 TimelineTab::full_tabs(), 155 )), 156 ArgColumn::Timeline(tk) => tk.into_timeline(ndb, user), 157 } 158 } 159 }