aboutsummaryrefslogtreecommitdiff
path: root/libs/nak/src/logging.rs
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-02-14 02:45:12 -0600
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-02-14 02:45:25 -0600
commit817e8f5cd26739c69d930d21cd9dc4c0b6e4984e (patch)
tree52aed11d6751bb7d20b06acf197d56fac113203d /libs/nak/src/logging.rs
parent51a9f8f197727f00896e5de44569b098923527dd (diff)
Add FUSE external mapping support, BG3/Oblivion Remastered fixes, fomod-plus and NaK integration
FUSE VFS now deploys non-data-dir mod mappings (Paks, OBSE, UE4SS, etc.) via real symlinks and injects file-level data-dir mappings (plugins.txt, loadorder.txt) into the VFS tree. Fixes game launches for Oblivion Remastered (Root Builder path resolution, script extender support) and BG3 (Wine prefix documents directory, file mapper symlinks on Linux). Vendors mo2-fomod-plus plugin and NaK crate for FOMOD installer and game finder/runtime support. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'libs/nak/src/logging.rs')
-rw-r--r--libs/nak/src/logging.rs99
1 files changed, 99 insertions, 0 deletions
diff --git a/libs/nak/src/logging.rs b/libs/nak/src/logging.rs
new file mode 100644
index 0000000..8c0f91a
--- /dev/null
+++ b/libs/nak/src/logging.rs
@@ -0,0 +1,99 @@
+//! Logging for Fluorine Manager.
+//!
+//! All log messages are:
+//! 1. Written to `~/.var/app/com.fluorine.manager/logs/nak.log` (always)
+//! 2. Forwarded to an optional callback set via `set_log_callback()` (for MOBase::log)
+
+use std::fs;
+use std::io::Write;
+use std::path::PathBuf;
+use std::sync::Mutex;
+
+/// Signature for the log callback: (level, message)
+///
+/// Levels: "info", "warning", "error", "install", "action", "download"
+type LogCallback = Box<dyn Fn(&str, &str) + Send + Sync>;
+
+static LOG_CALLBACK: Mutex<Option<LogCallback>> = Mutex::new(None);
+
+/// Set the global log callback. Call once at startup from FFI.
+pub fn set_log_callback(cb: impl Fn(&str, &str) + Send + Sync + 'static) {
+ if let Ok(mut guard) = LOG_CALLBACK.lock() {
+ *guard = Some(Box::new(cb));
+ }
+}
+
+/// Get the log directory path.
+fn log_dir() -> PathBuf {
+ let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
+ PathBuf::from(home).join(".var/app/com.fluorine.manager/logs")
+}
+
+/// Get the log file path.
+fn log_file_path() -> PathBuf {
+ log_dir().join("nak.log")
+}
+
+fn emit(level: &str, message: &str) {
+ // Write to file (always, even before callback is set)
+ write_to_file(level, message);
+
+ // Forward to callback if set
+ if let Ok(guard) = LOG_CALLBACK.lock() {
+ if let Some(ref cb) = *guard {
+ cb(level, message);
+ }
+ }
+}
+
+fn write_to_file(level: &str, message: &str) {
+ let path = log_file_path();
+
+ // Ensure log directory exists (only try once per message, cheap no-op if exists)
+ if let Some(parent) = path.parent() {
+ let _ = fs::create_dir_all(parent);
+ }
+
+ // Rotate if over 2MB
+ if let Ok(meta) = fs::metadata(&path) {
+ if meta.len() > 2 * 1024 * 1024 {
+ let old = path.with_extension("log.old");
+ let _ = fs::rename(&path, &old);
+ }
+ }
+
+ let Ok(mut file) = fs::OpenOptions::new()
+ .create(true)
+ .append(true)
+ .open(&path)
+ else {
+ return;
+ };
+
+ let timestamp = chrono::Local::now().format("%H:%M:%S");
+ let _ = writeln!(file, "[{}] [{}] {}", timestamp, level.to_uppercase(), message);
+}
+
+pub fn log_info(message: &str) {
+ emit("info", message);
+}
+
+pub fn log_warning(message: &str) {
+ emit("warning", message);
+}
+
+pub fn log_error(message: &str) {
+ emit("error", message);
+}
+
+pub fn log_install(message: &str) {
+ emit("install", message);
+}
+
+pub fn log_action(message: &str) {
+ emit("action", message);
+}
+
+pub fn log_download(message: &str) {
+ emit("download", message);
+}