notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

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 }