fish-shell/fish-rust/src/flog.rs
Mahmoud Al-Qudsi cce78eeb43 Update env_var_to_ffi() to take an Option<EnvVar>
It wasn't possible to handle cases where vars.get() returned `None` then forward
that to C++, but now we can.
2023-05-25 16:54:07 -05:00

243 lines
7.8 KiB
Rust

use crate::ffi::{get_flog_file_fd, wildcard_match};
use crate::parse_util::parse_util_unescape_wildcards;
use crate::wchar::{widestrs, wstr, WString};
use crate::wchar_ext::WExt;
use crate::wchar_ffi::WCharToFFI;
use std::io::Write;
use std::os::unix::io::{FromRawFd, IntoRawFd, RawFd};
use std::sync::atomic::Ordering;
#[rustfmt::skip::macros(category)]
#[widestrs]
pub mod categories {
use super::wstr;
use std::sync::atomic::AtomicBool;
pub struct category_t {
pub name: &'static wstr,
pub description: &'static wstr,
pub enabled: AtomicBool,
}
/// Macro to declare a static variable identified by $var,
/// with the given name and description, and optionally enabled by default.
macro_rules! declare_category {
(
($var:ident, $name:expr, $description:expr, $enabled:expr)
) => {
pub static $var: category_t = category_t {
name: $name,
description: $description,
enabled: AtomicBool::new($enabled),
};
};
(
($var:ident, $name:expr, $description:expr)
) => {
declare_category!(($var, $name, $description, false));
};
}
/// Macro to extract the variable name for a category.
macro_rules! category_name {
(($var:ident, $name:expr, $description:expr, $enabled:expr)) => {
$var
};
(($var:ident, $name:expr, $description:expr)) => {
$var
};
}
macro_rules! categories {
(
// A repetition of categories, separated by semicolons.
$($cats:tt);*
// Allow trailing semicolon.
$(;)?
) => {
// Declare each category.
$(
declare_category!($cats);
)*
// Define a function which gives you a Vector of all categories.
pub fn all_categories() -> Vec<&'static category_t> {
vec![
$(
& category_name!($cats),
)*
]
}
};
}
categories!(
(error, "error"L, "Serious unexpected errors (on by default)"L, true);
(debug, "debug"L, "Debugging aid (on by default)"L, true);
(warning, "warning"L, "Warnings (on by default)"L, true);
(warning_path, "warning-path"L, "Warnings about unusable paths for config/history (on by default)"L, true);
(config, "config"L, "Finding and reading configuration"L);
(event, "event"L, "Firing events"L);
(exec, "exec"L, "Errors reported by exec (on by default)"L, true);
(exec_job_status, "exec-job-status"L, "Jobs changing status"L);
(exec_job_exec, "exec-job-exec"L, "Jobs being executed"L);
(exec_fork, "exec-fork"L, "Calls to fork()"L);
(output_invalid, "output-invalid"L, "Trying to print invalid output"L);
(ast_construction, "ast-construction"L, "Parsing fish AST"L);
(proc_job_run, "proc-job-run"L, "Jobs getting started or continued"L);
(proc_termowner, "proc-termowner"L, "Terminal ownership events"L);
(proc_internal_proc, "proc-internal-proc"L, "Internal (non-forked) process events"L);
(proc_reap_internal, "proc-reap-internal"L, "Reaping internal (non-forked) processes"L);
(proc_reap_external, "proc-reap-external"L, "Reaping external (forked) processes"L);
(proc_pgroup, "proc-pgroup"L, "Process groups"L);
(env_locale, "env-locale"L, "Changes to locale variables"L);
(env_export, "env-export"L, "Changes to exported variables"L);
(env_dispatch, "env-dispatch"L, "Reacting to variables"L);
(uvar_file, "uvar-file"L, "Writing/reading the universal variable store"L);
(uvar_notifier, "uvar-notifier"L, "Notifications about universal variable changes"L);
(topic_monitor, "topic-monitor"L, "Internal details of the topic monitor"L);
(char_encoding, "char-encoding"L, "Character encoding issues"L);
(history, "history"L, "Command history events"L);
(history_file, "history-file"L, "Reading/Writing the history file"L);
(profile_history, "profile-history"L, "History performance measurements"L);
(iothread, "iothread"L, "Background IO thread events"L);
(fd_monitor, "fd-monitor"L, "FD monitor events"L);
(term_support, "term-support"L, "Terminal feature detection"L);
(reader, "reader"L, "The interactive reader/input system"L);
(reader_render, "reader-render"L, "Rendering the command line"L);
(complete, "complete"L, "The completion system"L);
(path, "path"L, "Searching/using paths"L);
(screen, "screen"L, "Screen repaints"L);
);
}
/// FLOG formats values. By default we would like to use Display, and fall back to Debug.
/// However that would require specialization. So instead we make two "separate" traits, bring them both in scope,
/// and let Rust figure it out.
/// Clients can opt a Debug type into Floggable by implementing FloggableDebug:
/// impl FloggableDebug for MyType {}
pub trait FloggableDisplay {
/// Return a string representation of this thing.
fn to_flog_str(&self) -> String;
}
impl<T: std::fmt::Display> FloggableDisplay for T {
fn to_flog_str(&self) -> String {
self.to_string()
}
}
pub trait FloggableDebug: std::fmt::Debug {
fn to_flog_str(&self) -> String {
format!("{:?}", self)
}
}
/// Write to our FLOG file.
pub fn flog_impl(s: &str) {
let fd = get_flog_file_fd().0 as RawFd;
if fd < 0 {
return;
}
let mut file = unsafe { std::fs::File::from_raw_fd(fd) };
let _ = file.write(s.as_bytes());
// Ensure the file is not closed.
file.into_raw_fd();
}
macro_rules! FLOG {
($category:ident, $($elem:expr),+ $(,)*) => {
if crate::flog::categories::$category.enabled.load(std::sync::atomic::Ordering::Relaxed) {
#[allow(unused_imports)]
use crate::flog::{FloggableDisplay, FloggableDebug};
let mut vs = vec![format!("{}:", crate::flog::categories::$category.name)];
$(
{
vs.push($elem.to_flog_str())
}
)+
// We don't use locking here so we have to append our own newline to avoid multiple writes.
let mut v = vs.join(" ");
v.push('\n');
crate::flog::flog_impl(&v);
}
};
}
// TODO implement.
macro_rules! FLOGF {
($category:ident, $($elem:expr),+ $(,)*) => {
crate::flog::FLOG!($category, $($elem),*);
}
}
macro_rules! should_flog {
($category:ident) => {
crate::flog::categories::$category
.enabled
.load(std::sync::atomic::Ordering::Relaxed)
};
}
pub(crate) use {should_flog, FLOG, FLOGF};
/// For each category, if its name matches the wildcard, set its enabled to the given sense.
fn apply_one_wildcard(wc_esc: &wstr, sense: bool) {
let wc = parse_util_unescape_wildcards(wc_esc);
let mut match_found = false;
for cat in categories::all_categories() {
if wildcard_match(&cat.name.to_ffi(), &wc.to_ffi(), false) {
cat.enabled.store(sense, Ordering::Relaxed);
match_found = true;
}
}
if !match_found {
eprintln!("Failed to match debug category: {wc_esc}");
}
}
/// Set the active flog categories according to the given wildcard \p wc.
pub fn activate_flog_categories_by_pattern(wc_ptr: &wstr) {
let mut wc: WString = wc_ptr.into();
// Normalize underscores to dashes, allowing the user to be sloppy.
for c in wc.as_char_slice_mut() {
if *c == '_' {
*c = '-';
}
}
for s in wc.split(',') {
if s.starts_with('-') {
apply_one_wildcard(s.slice_from(1), false);
} else {
apply_one_wildcard(s, true);
}
}
}