use crate::config::CFG;
use crate::config::VIEWER_SERVED_MIME_TYPES_HMAP;
use crate::filename::MarkupLanguage;
use crate::settings::ARGS;
use crate::settings::LAUNCH_EDITOR;
use crate::viewer::error::ViewerError;
use crate::viewer::sse_server::manage_connections;
use crate::viewer::sse_server::SseToken;
use crate::viewer::watcher::FileWatcher;
use crate::viewer::web_browser::launch_web_browser;
use std::net::TcpListener;
use std::path::PathBuf;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::SyncSender;
use std::sync::{Arc, Mutex};
use std::thread;
use std::thread::JoinHandle;
use std::time::Duration;
use std::time::Instant;
const BROWSER_INSTANCE_MIN_UPTIME: u128 = 3000;
pub const LOCALHOST: &str = "localhost";
#[derive(Clone, Default, Debug)]
pub struct Viewer {}
impl Viewer {
pub fn run(doc: PathBuf) {
match Self::run2(doc) {
Ok(_) => (),
Err(e) => {
log::warn!("Viewer::run(): {}", e);
}
}
}
#[inline]
fn run2(doc: PathBuf) -> Result<(), ViewerError> {
match MarkupLanguage::from(
doc.extension()
.unwrap_or_default()
.to_str()
.unwrap_or_default(),
) {
MarkupLanguage::Unknown => return Ok(()),
MarkupLanguage::None => return Err(ViewerError::MarkupLanguageNone),
_ => (),
};
let listener = if let Some(p) = ARGS.port {
TcpListener::bind((LOCALHOST, p))?
} else {
TcpListener::bind((LOCALHOST, 0))?
};
let localport = listener.local_addr()?.port();
lazy_static::initialize(&VIEWER_SERVED_MIME_TYPES_HMAP);
log::debug!(
"Viewer::run(): \
Besides the note's HTML rendition, we only serve files with the following\
listed extensions: \n{}",
&VIEWER_SERVED_MIME_TYPES_HMAP
.keys()
.map(|s| {
let mut s = s.to_string();
s.push_str(", ");
s
})
.collect::<String>()
);
let event_tx_list: Arc<Mutex<Vec<SyncSender<SseToken>>>> = Arc::new(Mutex::new(Vec::new()));
let doc_ = doc.clone();
let event_tx_list_ = event_tx_list.clone();
thread::spawn(move || manage_connections(event_tx_list_, listener, doc_));
let terminate_on_browser_disconnect = Arc::new(AtomicBool::new(false));
let terminate_on_browser_disconnect_ = terminate_on_browser_disconnect.clone();
let watcher_handle: JoinHandle<_> = thread::spawn(move || {
match FileWatcher::new(doc, event_tx_list, terminate_on_browser_disconnect_) {
Ok(mut w) => w.run(),
Err(e) => {
log::warn!("Can not start file watcher, giving up: {}", e);
}
}
});
let url = format!("http://{}:{}", LOCALHOST, localport);
log::info!("Viewer::run(): launching browser with URL: {}", url);
if CFG.viewer.startup_delay > 0 {
thread::sleep(Duration::from_millis(CFG.viewer.startup_delay as u64));
};
let browser_start = Instant::now();
launch_web_browser(&url)?;
if browser_start.elapsed().as_millis() < BROWSER_INSTANCE_MIN_UPTIME {
if !*LAUNCH_EDITOR {
terminate_on_browser_disconnect.store(true, Ordering::SeqCst);
};
watcher_handle.join().unwrap();
}
Ok(())
}
}