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.
Nothing/src-tauri/src/poe_reader.rs

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
}