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