diff --git a/src/lib.rs b/src/lib.rs index 52b49fd79f..6972adcb4a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -130,6 +130,7 @@ pub use crate::status::{StatusEntry, StatusIter, StatusOptions, StatusShow, Stat pub use crate::submodule::{Submodule, SubmoduleUpdateOptions}; pub use crate::tag::Tag; pub use crate::time::{IndexTime, Time}; +pub use crate::tracing::{trace_set, TraceLevel}; pub use crate::transaction::Transaction; pub use crate::tree::{Tree, TreeEntry, TreeIter, TreeWalkMode, TreeWalkResult}; pub use crate::treebuilder::TreeBuilder; @@ -690,6 +691,7 @@ mod submodule; mod tag; mod tagforeach; mod time; +mod tracing; mod transaction; mod tree; mod treebuilder; diff --git a/src/tracing.rs b/src/tracing.rs new file mode 100644 index 0000000000..f348575774 --- /dev/null +++ b/src/tracing.rs @@ -0,0 +1,82 @@ +use std::sync::atomic::{AtomicUsize, Ordering}; + +use libc::c_char; + +use crate::{panic, raw, util::Binding}; + +/// Available tracing levels. When tracing is set to a particular level, +/// callers will be provided tracing at the given level and all lower levels. +#[derive(Copy, Clone, Debug)] +pub enum TraceLevel { + /// No tracing will be performed. + None, + + /// Severe errors that may impact the program's execution + Fatal, + + /// Errors that do not impact the program's execution + Error, + + /// Warnings that suggest abnormal data + Warn, + + /// Informational messages about program execution + Info, + + /// Detailed data that allows for debugging + Debug, + + /// Exceptionally detailed debugging data + Trace, +} + +impl Binding for TraceLevel { + type Raw = raw::git_trace_level_t; + unsafe fn from_raw(raw: raw::git_trace_level_t) -> Self { + match raw { + raw::GIT_TRACE_NONE => Self::None, + raw::GIT_TRACE_FATAL => Self::Fatal, + raw::GIT_TRACE_ERROR => Self::Error, + raw::GIT_TRACE_WARN => Self::Warn, + raw::GIT_TRACE_INFO => Self::Info, + raw::GIT_TRACE_DEBUG => Self::Debug, + raw::GIT_TRACE_TRACE => Self::Trace, + _ => panic!("Unknown git trace level"), + } + } + fn raw(&self) -> raw::git_trace_level_t { + match *self { + Self::None => raw::GIT_TRACE_NONE, + Self::Fatal => raw::GIT_TRACE_FATAL, + Self::Error => raw::GIT_TRACE_ERROR, + Self::Warn => raw::GIT_TRACE_WARN, + Self::Info => raw::GIT_TRACE_INFO, + Self::Debug => raw::GIT_TRACE_DEBUG, + Self::Trace => raw::GIT_TRACE_TRACE, + } + } +} + +pub type TracingCb = fn(TraceLevel, &str); + +static CALLBACK: AtomicUsize = AtomicUsize::new(0); + +/// +pub fn trace_set(level: TraceLevel, cb: TracingCb) -> bool { + CALLBACK.store(cb as usize, Ordering::SeqCst); + + unsafe { + raw::git_trace_set(level.raw(), Some(tracing_cb_c)); + } + + return true; +} + +extern "C" fn tracing_cb_c(level: raw::git_trace_level_t, msg: *const c_char) { + let cb = CALLBACK.load(Ordering::SeqCst); + panic::wrap(|| unsafe { + let cb: TracingCb = std::mem::transmute(cb); + let msg = std::ffi::CStr::from_ptr(msg).to_str().unwrap(); + cb(Binding::from_raw(level), msg); + }); +}