Skip to main content

eve_log_parser/
watcher.rs

1//! Provides the necessary utils to monitor logfile changes and return the as they're happening
2//!
3
4use std::{
5    fs::{self, File},
6    io::{Read, Seek, SeekFrom},
7    path::PathBuf,
8};
9
10use futures::{
11    channel::mpsc::{channel, Receiver},
12    future, SinkExt, Stream, StreamExt, TryStreamExt,
13};
14use home::home_dir;
15use log::{debug, error, info};
16use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher};
17
18use crate::{models::Log, parse_log_line};
19
20/// Returns the log directory for EVE.
21/// This should be the proper Windows path but checks that it's the same under Linux should be
22/// done
23pub fn get_log_folder() -> PathBuf {
24    let mut path = home_dir().unwrap(); // home directory
25    path.push("Documents");
26    path.push("EVE");
27    path.push("logs");
28    path.push("Gamelogs");
29    path
30}
31
32/// Returns all new logs sent to a file as a stream
33pub async fn watch_log_file(logfile: PathBuf) -> impl Stream<Item = Log> {
34    let (mut watcher, rx) = create_watcher().unwrap();
35
36    watcher
37        .watch(&logfile, RecursiveMode::NonRecursive)
38        .unwrap();
39    info!("Watcher started on file {:?}", &logfile);
40
41    let mut log_contents = fs::read_to_string(&logfile).unwrap();
42    let mut pos_in_file = log_contents.len() as u64;
43
44    rx.into_stream()
45        .inspect_err(|err| error!("Error in the watcher: {}", err))
46        .filter(|element| {
47            future::ready(match element {
48                Ok(event) => filter_ok_events(event),
49                _ => false,
50            })
51        })
52        .map(move |_| -> Option<Log> {
53            let mut f = File::open(&logfile).unwrap();
54            f.seek(SeekFrom::Start(pos_in_file)).unwrap();
55
56            pos_in_file = f.metadata().unwrap().len();
57
58            log_contents.clear();
59            f.read_to_string(&mut log_contents).unwrap();
60            info!("new content: {}", log_contents);
61            parse_log_line(&log_contents)
62        })
63        .filter_map(future::ready) // removes Nones from the stream
64}
65
66/// This function will filter events to keep only the events of type `Event::ModifyKind`
67fn filter_ok_events(event: &Event) -> bool {
68    if let notify::event::EventKind::Modify(notify::event::ModifyKind::Data(_)) = event.kind {
69        return true;
70    }
71    false
72}
73
74/// Initiates a watcher and the receiver with default configuration
75fn create_watcher() -> notify::Result<(RecommendedWatcher, Receiver<notify::Result<Event>>)> {
76    debug!("Starting to create a watcher and the receiver");
77    let (mut tx, rx) = channel(1);
78    let watcher = RecommendedWatcher::new(
79        move |res| {
80            futures::executor::block_on(async {
81                tx.send(res).await.unwrap();
82            })
83        },
84        Config::default(),
85    )?;
86
87    Ok((watcher, rx))
88}