You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
188 lines
5.0 KiB
188 lines
5.0 KiB
use std::{
|
|
fs::{File, OpenOptions},
|
|
io::{Read, Seek, SeekFrom},
|
|
path::PathBuf,
|
|
sync::mpsc::{channel, Receiver, Sender},
|
|
time::Duration,
|
|
};
|
|
|
|
use notify::{event, Config, PollWatcher, RecursiveMode, Watcher, Event};
|
|
use regex::Regex;
|
|
|
|
#[cfg(target_os = "windows")]
|
|
use std::os::windows::fs::OpenOptionsExt;
|
|
|
|
struct LogFileReader {
|
|
tx: Sender<String>,
|
|
file_path: PathBuf,
|
|
contents: Vec<u8>,
|
|
position: u64,
|
|
}
|
|
|
|
impl Drop for LogFileReader {
|
|
fn drop(&mut self) {
|
|
log::debug!("LogFileReader dropped");
|
|
}
|
|
}
|
|
|
|
#[cfg(target_os = "windows")]
|
|
fn share_mode(options: &mut OpenOptions) {
|
|
options.share_mode(0x00000004 | 0x00000001 | 0x00000002);
|
|
}
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
fn share_mode(options: &mut OpenOptions) {}
|
|
|
|
impl LogFileReader {
|
|
fn new(file: &PathBuf, tx: Sender<String>) -> Result<Self, std::io::Error> {
|
|
let mut lfr = LogFileReader {
|
|
tx,
|
|
file_path: file.to_path_buf(),
|
|
contents: Vec::new(),
|
|
position: 0,
|
|
};
|
|
|
|
let mut options = OpenOptions::new();
|
|
|
|
options.read(true).write(false);
|
|
|
|
share_mode(&mut options);
|
|
|
|
let mut file = options.open(file)?;
|
|
lfr.position = file
|
|
.seek(SeekFrom::End(0))
|
|
.expect("Could not start at end of file");
|
|
|
|
Ok(lfr)
|
|
}
|
|
|
|
fn on_write_handler(&mut self) {
|
|
self.contents.truncate(0);
|
|
|
|
let mut options = OpenOptions::new();
|
|
|
|
options.read(true).write(false);
|
|
|
|
share_mode(&mut options);
|
|
|
|
let mut file = options
|
|
.open(&self.file_path)
|
|
.expect("Could not re-open file");
|
|
|
|
let _ = file.seek(SeekFrom::Start(self.position));
|
|
|
|
self.position += file.read_to_end(&mut self.contents).unwrap() as u64;
|
|
|
|
for line in String::from_utf8_lossy(&self.contents).lines() {
|
|
let line = line.to_string();
|
|
if !line.is_empty() {
|
|
self.tx.send(line).expect("Could not send line to receiver");
|
|
}
|
|
}
|
|
|
|
// do_process(contents)
|
|
}
|
|
}
|
|
|
|
pub fn poe_client_log_receiver(path: PathBuf) -> (Receiver<String>, PollWatcher) {
|
|
let (tx, rx) = channel();
|
|
let mut lfr = LogFileReader::new(&path, tx).unwrap();
|
|
|
|
(rx, watch_file_for_writes(path, Box::new(move || lfr.on_write_handler())))
|
|
}
|
|
|
|
|
|
const POE_STEAM_APP_ID: u32 = 238960;
|
|
pub fn get_poe_path() -> Option<PathBuf> {
|
|
if let Some(mut steam_dir) = steamlocate::SteamDir::locate() {
|
|
if let Some(app) = steam_dir.app(&POE_STEAM_APP_ID) {
|
|
let mut path = app.path.clone();
|
|
path.push("logs");
|
|
path.push("Client.txt");
|
|
return Some(path);
|
|
}
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
fn entered_filtered_rx<'a>(rx: &'a Receiver<String>) -> impl Iterator<Item = String> + 'a {
|
|
rx.iter().filter_map(|str| {
|
|
if str.contains("You have entered ") {
|
|
Some(str)
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
}
|
|
|
|
/// Returns an iterator of strings with the id
|
|
pub fn area_filtered_rx<'a>(rx: &'a Receiver<String>) -> impl Iterator<Item = String> + 'a {
|
|
// Generating level 68 area "1_SideArea5_6" with seed 4103532853
|
|
let reg = Regex::new("Generating level ([0-9]+) area \"(.+)\"").unwrap();
|
|
let area_id_location = 2;
|
|
rx.try_iter().filter_map(move |str| {
|
|
let caps = reg.captures(&str);
|
|
if let Some(caps) = caps {
|
|
Some(caps[area_id_location].to_string())
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
}
|
|
|
|
pub fn filter_func(str : String) -> Option<String> {
|
|
let reg = Regex::new("Generating level ([0-9]+) area \"(.+)\"").unwrap();
|
|
let area_id_location = 2;
|
|
|
|
let caps = reg.captures(&str);
|
|
if let Some(caps) = caps {
|
|
Some(caps[area_id_location].to_string())
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn blocking_area_filtered_rx<'a>(rx: &'a Receiver<String>) -> impl Iterator<Item = String> + 'a {
|
|
// Generating level 68 area "1_SideArea5_6" with seed 4103532853
|
|
|
|
rx.iter()
|
|
}
|
|
|
|
fn watch_file_for_writes(path: PathBuf, mut on_write_handler: Box<dyn FnMut() + Send>) -> PollWatcher
|
|
|
|
{
|
|
|
|
// Create a watcher object, delivering debounced events.
|
|
// The notification back-end is selected based on the platform.
|
|
let config = Config::default().with_poll_interval(Duration::from_millis(150));
|
|
let mut watcher = PollWatcher::new(
|
|
move |e: Result<Event, _>| match e {
|
|
Ok(e2) => {
|
|
match e2.kind {
|
|
|
|
notify::EventKind::Modify(_) => {
|
|
on_write_handler()
|
|
},
|
|
_ => (),
|
|
|
|
}
|
|
},
|
|
|
|
e => log::error!("Error watching for file something something: {e:?}"),
|
|
},
|
|
config,
|
|
)
|
|
.unwrap();
|
|
|
|
//let mut watcher = watcher(tx, Duration::from_millis(100)).unwrap();
|
|
|
|
// Add a path to be watched. All files and directories at that path and
|
|
// below will be monitored for changes.
|
|
watcher
|
|
.watch(path.as_path(), RecursiveMode::NonRecursive)
|
|
.unwrap();
|
|
watcher
|
|
}
|