notedeck.rs (7341B)
1 #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] 2 // hide console window on Windows in release 3 4 #[cfg(feature = "memory")] 5 use re_memory::AccountingAllocator; 6 7 #[cfg(feature = "memory")] 8 #[global_allocator] 9 static GLOBAL: AccountingAllocator<std::alloc::System> = 10 AccountingAllocator::new(std::alloc::System); 11 12 use notedeck::{DataPath, DataPathType, Notedeck}; 13 use notedeck_chrome::{setup::generate_native_options, Chrome}; 14 use tracing_appender::non_blocking::WorkerGuard; 15 use tracing_subscriber::EnvFilter; 16 17 fn setup_logging(path: &DataPath) -> Option<WorkerGuard> { 18 #[allow(unused_variables)] // need guard to live for lifetime of program 19 let (maybe_non_blocking, maybe_guard) = { 20 let log_path = path.path(DataPathType::Log); 21 // Setup logging to file 22 23 use tracing_appender::{ 24 non_blocking, 25 rolling::{RollingFileAppender, Rotation}, 26 }; 27 28 let file_appender = RollingFileAppender::new( 29 Rotation::DAILY, 30 log_path, 31 format!("notedeck-{}.log", env!("CARGO_PKG_VERSION")), 32 ); 33 34 let (non_blocking, _guard) = non_blocking(file_appender); 35 36 (Some(non_blocking), Some(_guard)) 37 }; 38 39 // Log to stdout (if you run with `RUST_LOG=debug`). 40 if let Some(non_blocking_writer) = maybe_non_blocking { 41 use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt}; 42 43 let console_layer = fmt::layer().with_target(true).with_writer(std::io::stdout); 44 45 // Create the file layer (writes to the file) 46 let file_layer = fmt::layer() 47 .with_ansi(false) 48 .with_writer(non_blocking_writer); 49 50 let env_filter = 51 EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("notedeck=info")); 52 53 // Set up the subscriber to combine both layers 54 tracing_subscriber::registry() 55 .with(console_layer) 56 .with(file_layer) 57 .with(env_filter) 58 .init(); 59 } else { 60 tracing_subscriber::fmt() 61 .with_env_filter(EnvFilter::from_default_env()) 62 .init(); 63 } 64 65 maybe_guard 66 } 67 68 // Desktop 69 #[cfg(not(target_arch = "wasm32"))] 70 #[tokio::main] 71 async fn main() { 72 #[cfg(feature = "memory")] 73 re_memory::accounting_allocator::set_tracking_callstacks(true); 74 75 let base_path = DataPath::default_base_or_cwd(); 76 let path = DataPath::new(base_path.clone()); 77 78 // This guard must be scoped for the duration of the entire program so all logs will be written 79 let _guard = setup_logging(&path); 80 81 let _res = eframe::run_native( 82 "Damus Notedeck", 83 generate_native_options(path), 84 Box::new(|cc| { 85 let args: Vec<String> = std::env::args().collect(); 86 let ctx = &cc.egui_ctx; 87 88 let mut notedeck = Notedeck::new(ctx, base_path, &args); 89 notedeck.setup(ctx); 90 let chrome = Chrome::new_with_apps(cc, &args, &mut notedeck)?; 91 notedeck.set_app(chrome); 92 93 Ok(Box::new(notedeck)) 94 }), 95 ); 96 } 97 98 /* 99 * TODO: nostrdb not supported on web 100 * 101 #[cfg(target_arch = "wasm32")] 102 pub fn main() { 103 // Make sure panics are logged using `console.error`. 104 console_error_panic_hook::set_once(); 105 106 // Redirect tracing to console.log and friends: 107 tracing_wasm::set_as_global_default(); 108 109 wasm_bindgen_futures::spawn_local(async { 110 let web_options = eframe::WebOptions::default(); 111 eframe::start_web( 112 "the_canvas_id", // hardcode it 113 web_options, 114 Box::new(|cc| Box::new(Damus::new(cc, "."))), 115 ) 116 .await 117 .expect("failed to start eframe"); 118 }); 119 } 120 */ 121 122 #[cfg(test)] 123 mod tests { 124 use super::Notedeck; 125 use notedeck_columns::Damus; 126 use std::path::{Path, PathBuf}; 127 128 fn create_tmp_dir() -> PathBuf { 129 tempfile::TempDir::new() 130 .expect("tmp path") 131 .path() 132 .to_path_buf() 133 } 134 135 fn rmrf(path: impl AsRef<Path>) { 136 let _ = std::fs::remove_dir_all(path); 137 } 138 139 /// Ensure dbpath actually sets the dbpath correctly. 140 #[tokio::test] 141 async fn test_dbpath() { 142 let datapath = create_tmp_dir(); 143 let dbpath = create_tmp_dir(); 144 let args: Vec<String> = [ 145 "--testrunner", 146 "--datapath", 147 &datapath.to_str().unwrap(), 148 "--dbpath", 149 &dbpath.to_str().unwrap(), 150 ] 151 .iter() 152 .map(|s| s.to_string()) 153 .collect(); 154 155 let ctx = egui::Context::default(); 156 let _app = Notedeck::new(&ctx, &datapath, &args); 157 158 assert!(Path::new(&dbpath.join("data.mdb")).exists()); 159 assert!(Path::new(&dbpath.join("lock.mdb")).exists()); 160 assert!(!Path::new(&datapath.join("db")).exists()); 161 162 rmrf(datapath); 163 rmrf(dbpath); 164 } 165 166 #[tokio::test] 167 async fn test_column_args() { 168 let tmpdir = create_tmp_dir(); 169 let npub = "npub1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzsevkk5s"; 170 let args: Vec<String> = [ 171 "--testrunner", 172 "--no-keystore", 173 "--pub", 174 npub, 175 "-c", 176 "notifications", 177 "-c", 178 "contacts", 179 ] 180 .iter() 181 .map(|s| s.to_string()) 182 .collect(); 183 184 let ctx = egui::Context::default(); 185 let mut notedeck = Notedeck::new(&ctx, &tmpdir, &args); 186 let unrecognized_args = notedeck.unrecognized_args().clone(); 187 let mut app_ctx = notedeck.app_context(); 188 let app = Damus::new(&mut app_ctx, &args); 189 190 assert_eq!(app.columns(app_ctx.accounts).columns().len(), 2); 191 192 let tl1 = app 193 .columns(app_ctx.accounts) 194 .column(0) 195 .router() 196 .top() 197 .timeline_id() 198 .unwrap(); 199 200 let tl2 = app 201 .columns(app_ctx.accounts) 202 .column(1) 203 .router() 204 .top() 205 .timeline_id() 206 .unwrap(); 207 208 assert_eq!(app.timeline_cache.num_timelines(), 2); 209 assert!(app.timeline_cache.get(&tl1).is_some()); 210 assert!(app.timeline_cache.get(&tl2).is_some()); 211 212 rmrf(tmpdir); 213 } 214 215 #[tokio::test] 216 async fn test_unknown_args() { 217 let tmpdir = create_tmp_dir(); 218 let npub = "npub1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzsevkk5s"; 219 let args: Vec<String> = [ 220 "--testrunner", 221 "--no-keystore", 222 "--unknown-arg", // <-- UNKNOWN 223 "--pub", 224 npub, 225 "-c", 226 "notifications", 227 "-c", 228 "contacts", 229 ] 230 .iter() 231 .map(|s| s.to_string()) 232 .collect(); 233 234 let ctx = egui::Context::default(); 235 let mut notedeck = Notedeck::new(&ctx, &tmpdir, &args); 236 let mut app_ctx = notedeck.app_context(); 237 let app = Damus::new(&mut app_ctx, &args); 238 239 // ensure we recognized all the arguments 240 let completely_unrecognized: Vec<String> = notedeck 241 .unrecognized_args() 242 .intersection(app.unrecognized_args()) 243 .cloned() 244 .collect(); 245 assert_eq!(completely_unrecognized, ["--unknown-arg"]); 246 247 rmrf(tmpdir); 248 } 249 }