fish-shell/fish-rust/src/flog.rs
Johannes Altmanninger 77aeb6a2a8 Port execution
Drop support for history file version 1.

ParseExecutionContext no longer contains an OperationContext because in my
first implementation, ParseExecutionContext didn't have interior mutability.
We should probably try to add it back.

Add a few to-do style comments. Search for "todo!" and "PORTING".

Co-authored-by: Xiretza <xiretza@xiretza.xyz>
(complete, wildcard, expand, history, history/file)
Co-authored-by: Henrik Hørlück Berg <36937807+henrikhorluck@users.noreply.github.com>
(builtins/set)
2023-11-15 11:09:48 +01:00

251 lines
8 KiB
Rust

use crate::parse_util::parse_util_unescape_wildcards;
use crate::wchar::prelude::*;
use crate::wildcard::wildcard_match;
use crate::wutil::write_to_fd;
use libc::c_int;
use std::sync::atomic::{AtomicI32, Ordering};
#[rustfmt::skip::macros(category)]
pub mod categories {
use super::wstr;
use crate::wchar::L;
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:literal, $description:literal, $enabled:expr)
) => {
pub static $var: category_t = category_t {
name: L!($name),
description: L!($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:literal, $description:literal, $enabled:expr)) => {
$var
};
(($var:ident, $name:literal, $description:literal)) => {
$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", "Serious unexpected errors (on by default)", true);
(debug, "debug", "Debugging aid (on by default)", true);
(warning, "warning", "Warnings (on by default)", true);
(warning_path, "warning-path", "Warnings about unusable paths for config/history (on by default)", true);
(config, "config", "Finding and reading configuration");
(event, "event", "Firing events");
(exec, "exec", "Errors reported by exec (on by default)", true);
(exec_job_status, "exec-job-status", "Jobs changing status");
(exec_job_exec, "exec-job-exec", "Jobs being executed");
(exec_fork, "exec-fork", "Calls to fork()");
(output_invalid, "output-invalid", "Trying to print invalid output");
(ast_construction, "ast-construction", "Parsing fish AST");
(proc_job_run, "proc-job-run", "Jobs getting started or continued");
(proc_termowner, "proc-termowner", "Terminal ownership events");
(proc_internal_proc, "proc-internal-proc", "Internal (non-forked) process events");
(proc_reap_internal, "proc-reap-internal", "Reaping internal (non-forked) processes");
(proc_reap_external, "proc-reap-external", "Reaping external (forked) processes");
(proc_pgroup, "proc-pgroup", "Process groups");
(env_locale, "env-locale", "Changes to locale variables");
(env_export, "env-export", "Changes to exported variables");
(env_dispatch, "env-dispatch", "Reacting to variables");
(uvar_file, "uvar-file", "Writing/reading the universal variable store");
(uvar_notifier, "uvar-notifier", "Notifications about universal variable changes");
(topic_monitor, "topic-monitor", "Internal details of the topic monitor");
(char_encoding, "char-encoding", "Character encoding issues");
(history, "history", "Command history events");
(history_file, "history-file", "Reading/Writing the history file");
(profile_history, "profile-history", "History performance measurements");
(iothread, "iothread", "Background IO thread events");
(fd_monitor, "fd-monitor", "FD monitor events");
(term_support, "term-support", "Terminal feature detection");
(reader, "reader", "The interactive reader/input system");
(reader_render, "reader-render", "Rendering the command line");
(complete, "complete", "The completion system");
(path, "path", "Searching/using paths");
(screen, "screen", "Screen repaints");
(refcell, "refcell", "Refcell dynamic borrowing");
);
}
/// 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();
if fd < 0 {
return;
}
let _ = write_to_fd(s.as_bytes(), fd);
}
/// The entry point for flogging.
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);
}
};
}
macro_rules! FLOGF {
($category:ident, $fmt: expr, $($elem:expr),+ $(,)*) => {
crate::flog::FLOG!($category, crate::wutil::sprintf!($fmt, $($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, &wc, 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);
}
}
}
/// The flog output fd. Defaults to stderr. A value < 0 disables flog.
static FLOG_FD: AtomicI32 = AtomicI32::new(libc::STDERR_FILENO);
pub fn set_flog_file_fd(fd: c_int) {
FLOG_FD.store(fd, Ordering::Relaxed);
}
#[inline]
pub fn get_flog_file_fd() -> c_int {
FLOG_FD.load(Ordering::Relaxed)
}