notedeck

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

notedeck.rs (8043B)


      1 #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
      2 // hide console window on Windows in release
      3 use notedeck_chrome::setup::{generate_native_options, setup_chrome};
      4 
      5 use notedeck::{DataPath, DataPathType, Notedeck};
      6 use notedeck_columns::Damus;
      7 use tracing_appender::non_blocking::WorkerGuard;
      8 use tracing_subscriber::EnvFilter;
      9 
     10 // Entry point for wasm
     11 //#[cfg(target_arch = "wasm32")]
     12 //use wasm_bindgen::prelude::*;
     13 
     14 fn setup_logging(path: &DataPath) -> Option<WorkerGuard> {
     15     #[allow(unused_variables)] // need guard to live for lifetime of program
     16     let (maybe_non_blocking, maybe_guard) = {
     17         let log_path = path.path(DataPathType::Log);
     18         // Setup logging to file
     19 
     20         use tracing_appender::{
     21             non_blocking,
     22             rolling::{RollingFileAppender, Rotation},
     23         };
     24 
     25         let file_appender = RollingFileAppender::new(
     26             Rotation::DAILY,
     27             log_path,
     28             format!("notedeck-{}.log", env!("CARGO_PKG_VERSION")),
     29         );
     30 
     31         let (non_blocking, _guard) = non_blocking(file_appender);
     32 
     33         (Some(non_blocking), Some(_guard))
     34     };
     35 
     36     // Log to stdout (if you run with `RUST_LOG=debug`).
     37     if let Some(non_blocking_writer) = maybe_non_blocking {
     38         use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt};
     39 
     40         let console_layer = fmt::layer().with_target(true).with_writer(std::io::stdout);
     41 
     42         // Create the file layer (writes to the file)
     43         let file_layer = fmt::layer()
     44             .with_ansi(false)
     45             .with_writer(non_blocking_writer);
     46 
     47         let env_filter =
     48             EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("notedeck=info"));
     49 
     50         // Set up the subscriber to combine both layers
     51         tracing_subscriber::registry()
     52             .with(console_layer)
     53             .with(file_layer)
     54             .with(env_filter)
     55             .init();
     56     } else {
     57         tracing_subscriber::fmt()
     58             .with_env_filter(EnvFilter::from_default_env())
     59             .init();
     60     }
     61 
     62     maybe_guard
     63 }
     64 
     65 // Desktop
     66 #[cfg(not(target_arch = "wasm32"))]
     67 #[tokio::main]
     68 async fn main() {
     69     let base_path = DataPath::default_base_or_cwd();
     70     let path = DataPath::new(base_path.clone());
     71 
     72     // This guard must be scoped for the duration of the entire program so all logs will be written
     73     let _guard = setup_logging(&path);
     74 
     75     let _res = eframe::run_native(
     76         "Damus Notedeck",
     77         generate_native_options(path),
     78         Box::new(|cc| {
     79             let args: Vec<String> = std::env::args().collect();
     80             let ctx = &cc.egui_ctx;
     81             let mut notedeck = Notedeck::new(ctx, base_path, &args);
     82             setup_chrome(ctx, notedeck.args(), notedeck.theme());
     83 
     84             let damus = Damus::new(&mut notedeck.app_context(), &args);
     85 
     86             // ensure we recognized all the arguments
     87             let completely_unrecognized: Vec<String> = notedeck
     88                 .unrecognized_args()
     89                 .intersection(damus.unrecognized_args())
     90                 .cloned()
     91                 .collect();
     92             assert!(
     93                 completely_unrecognized.is_empty(),
     94                 "unrecognized args: {:?}",
     95                 completely_unrecognized
     96             );
     97 
     98             // TODO: move "chrome" frame over Damus app somehow
     99             notedeck.set_app(damus);
    100 
    101             Ok(Box::new(notedeck))
    102         }),
    103     );
    104 }
    105 
    106 /*
    107  * TODO: nostrdb not supported on web
    108  *
    109 #[cfg(target_arch = "wasm32")]
    110 pub fn main() {
    111     // Make sure panics are logged using `console.error`.
    112     console_error_panic_hook::set_once();
    113 
    114     // Redirect tracing to console.log and friends:
    115     tracing_wasm::set_as_global_default();
    116 
    117     wasm_bindgen_futures::spawn_local(async {
    118         let web_options = eframe::WebOptions::default();
    119         eframe::start_web(
    120             "the_canvas_id", // hardcode it
    121             web_options,
    122             Box::new(|cc| Box::new(Damus::new(cc, "."))),
    123         )
    124         .await
    125         .expect("failed to start eframe");
    126     });
    127 }
    128 */
    129 
    130 #[cfg(test)]
    131 mod tests {
    132     use super::{Damus, Notedeck};
    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::new(&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 = Notedeck::new(&ctx, &tmpdir, &args);
    193         let unrecognized_args = notedeck.unrecognized_args().clone();
    194         let mut app_ctx = notedeck.app_context();
    195         let app = Damus::new(&mut app_ctx, &args);
    196 
    197         // ensure we recognized all the arguments
    198         let completely_unrecognized: Vec<String> = unrecognized_args
    199             .intersection(app.unrecognized_args())
    200             .cloned()
    201             .collect();
    202         assert!(
    203             completely_unrecognized.is_empty(),
    204             "unrecognized args: {:?}",
    205             completely_unrecognized
    206         );
    207 
    208         assert_eq!(app.columns(app_ctx.accounts).columns().len(), 2);
    209 
    210         let tl1 = app
    211             .columns(app_ctx.accounts)
    212             .column(0)
    213             .router()
    214             .top()
    215             .timeline_id()
    216             .unwrap();
    217 
    218         let tl2 = app
    219             .columns(app_ctx.accounts)
    220             .column(1)
    221             .router()
    222             .top()
    223             .timeline_id()
    224             .unwrap();
    225 
    226         assert_eq!(app.timeline_cache.timelines.len(), 2);
    227         assert!(app.timeline_cache.timelines.get(&tl1).is_some());
    228         assert!(app.timeline_cache.timelines.get(&tl2).is_some());
    229 
    230         rmrf(tmpdir);
    231     }
    232 
    233     #[tokio::test]
    234     async fn test_unknown_args() {
    235         let tmpdir = create_tmp_dir();
    236         let npub = "npub1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzsevkk5s";
    237         let args: Vec<String> = [
    238             "--testrunner",
    239             "--no-keystore",
    240             "--unknown-arg", // <-- UNKNOWN
    241             "--pub",
    242             npub,
    243             "-c",
    244             "notifications",
    245             "-c",
    246             "contacts",
    247         ]
    248         .iter()
    249         .map(|s| s.to_string())
    250         .collect();
    251 
    252         let ctx = egui::Context::default();
    253         let mut notedeck = Notedeck::new(&ctx, &tmpdir, &args);
    254         let mut app_ctx = notedeck.app_context();
    255         let app = Damus::new(&mut app_ctx, &args);
    256 
    257         // ensure we recognized all the arguments
    258         let completely_unrecognized: Vec<String> = notedeck
    259             .unrecognized_args()
    260             .intersection(app.unrecognized_args())
    261             .cloned()
    262             .collect();
    263         assert_eq!(completely_unrecognized, ["--unknown-arg"]);
    264 
    265         rmrf(tmpdir);
    266     }
    267 }