use crate::debug::tracks::debug_tracks;
use crate::tracker::{DynTracker, TrackSpan, Tracker};
use crate::Code;
use nom::{AsBytes, InputIter, InputLength, InputTake, Offset, Slice};
use nom_locate::LocatedSpan;
use std::cell::RefCell;
use std::error::Error;
use std::fmt::{Debug, Formatter};
use std::marker::PhantomData;
use std::ops::{RangeFrom, RangeTo};
pub struct StdTracker<C, T>
where
T: AsBytes + Copy,
C: Code,
{
track: bool,
data: RefCell<TrackingData<C, T>>,
}
struct TrackingData<C, T>
where
T: AsBytes + Copy,
C: Code,
{
func: Vec<C>,
track: Vec<Track<C, T>>,
}
pub struct Tracks<C, T>(pub Vec<Track<C, T>>)
where
T: AsBytes + Copy,
C: Code;
impl<C, T> Default for TrackingData<C, T>
where
T: AsBytes + Copy,
C: Code,
{
fn default() -> Self {
Self {
func: Default::default(),
track: Default::default(),
}
}
}
impl<C, T> StdTracker<C, T>
where
T: AsBytes + Copy,
C: Code,
{
pub fn new() -> Self {
Self {
track: true,
data: Default::default(),
}
}
pub fn tracking(mut self, track: bool) -> Self {
self.track = track;
self
}
pub fn set_tracking(&mut self, track: bool) {
self.track = track;
}
pub fn is_tracking(&self) -> bool {
self.track
}
pub fn span<'s>(&'s self, text: T) -> TrackSpan<'s, C, T>
where
T: 's,
{
TrackSpan::new_extra(text, DynTracker(self))
}
pub fn results(&self) -> Tracks<C, T> {
Tracks(self.data.replace(TrackingData::default()).track)
}
}
impl<C, T> Debug for Tracks<C, T>
where
T: AsBytes + Copy + Debug,
C: Code,
T: Offset
+ InputTake
+ InputIter
+ InputLength
+ Slice<RangeFrom<usize>>
+ Slice<RangeTo<usize>>,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
debug_tracks(f, f.width().into(), &self.0)
}
}
impl<C, T> Tracker<C, T> for StdTracker<C, T>
where
T: AsBytes + Copy,
C: Code,
{
fn enter(&self, func: C, span: &LocatedSpan<T, ()>) {
self.push_func(func);
self.track_enter(span);
}
fn debug(&self, span: &LocatedSpan<T, ()>, debug: String) {
self.track_debug(span, debug);
}
fn info(&self, span: &LocatedSpan<T, ()>, info: &'static str) {
self.track_info(span, info);
}
fn warn(&self, span: &LocatedSpan<T, ()>, warn: &'static str) {
self.track_warn(span, warn);
}
fn exit_ok(&self, span: &LocatedSpan<T, ()>, parsed: &LocatedSpan<T, ()>) {
self.track_exit_ok(span, parsed);
self.pop_func();
}
fn exit_err(&self, span: &LocatedSpan<T, ()>, code: C, err: &dyn Error) {
self.track_exit_err(span, code, err);
self.pop_func()
}
}
impl<C, T> StdTracker<C, T>
where
T: AsBytes + Copy,
C: Code,
{
fn push_func(&self, func: C) {
self.data.borrow_mut().func.push(func);
}
fn pop_func(&self) {
self.data.borrow_mut().func.pop();
}
fn func(&self) -> C {
*self
.data
.borrow()
.func
.last()
.expect("Vec<FnCode> is empty. forgot to trace.enter()")
}
fn parent_vec(&self) -> Vec<C> {
self.data.borrow().func.clone()
}
}
impl<C, T> StdTracker<C, T>
where
T: AsBytes + Copy,
C: Code,
{
fn track_enter(&self, span: &LocatedSpan<T, ()>) {
if self.track {
let parent = self.parent_vec();
let func = self.func();
self.data.borrow_mut().track.push(Track::Enter(EnterTrack {
func,
span: *span,
parents: parent,
}));
}
}
fn track_debug(&self, span: &LocatedSpan<T, ()>, debug: String) {
if self.track {
let parent = self.parent_vec();
let func = self.func();
self.data.borrow_mut().track.push(Track::Debug(DebugTrack {
func,
span: *span,
debug,
parents: parent,
}));
}
}
fn track_info(&self, span: &LocatedSpan<T, ()>, info: &'static str) {
if self.track {
let parent = self.parent_vec();
let func = self.func();
self.data.borrow_mut().track.push(Track::Info(InfoTrack {
func,
info,
span: *span,
parents: parent,
}));
}
}
fn track_warn(&self, span: &LocatedSpan<T, ()>, warn: &'static str) {
if self.track {
let parent = self.parent_vec();
let func = self.func();
self.data.borrow_mut().track.push(Track::Warn(WarnTrack {
func,
warn,
span: *span,
parents: parent,
}));
}
}
fn track_exit_ok(&self, span: &LocatedSpan<T, ()>, parsed: &LocatedSpan<T, ()>) {
if self.track {
let parent = self.parent_vec();
let func = self.func();
self.data.borrow_mut().track.push(Track::Ok(OkTrack {
func,
span: *span,
parsed: *parsed,
parents: parent.clone(),
}));
self.data.borrow_mut().track.push(Track::Exit(ExitTrack {
func,
parents: parent,
_phantom: Default::default(),
}));
}
}
fn track_exit_err(&self, span: &LocatedSpan<T, ()>, code: C, err: &dyn Error) {
if self.track {
let err_str = if let Some(cause) = err.source() {
cause.to_string()
} else {
err.to_string()
};
let parent = self.parent_vec();
let func = self.func();
self.data.borrow_mut().track.push(Track::Err(ErrTrack {
func,
code,
span: *span,
err: err_str,
parents: parent.clone(),
}));
self.data.borrow_mut().track.push(Track::Exit(ExitTrack {
func,
parents: parent,
_phantom: Default::default(),
}));
}
}
}
#[allow(missing_docs)]
pub enum Track<C, T>
where
T: Copy,
C: Code,
{
Enter(EnterTrack<C, T>),
Debug(DebugTrack<C, T>),
Info(InfoTrack<C, T>),
Warn(WarnTrack<C, T>),
Ok(OkTrack<C, T>),
Err(ErrTrack<C, T>),
Exit(ExitTrack<C, T>),
}
pub struct EnterTrack<C, T>
where
T: Copy,
C: Code,
{
pub func: C,
pub span: LocatedSpan<T, ()>,
pub parents: Vec<C>,
}
pub struct DebugTrack<C, T>
where
T: Copy,
C: Code,
{
pub func: C,
pub span: LocatedSpan<T, ()>,
pub debug: String,
pub parents: Vec<C>,
}
pub struct InfoTrack<C, T>
where
T: Copy,
C: Code,
{
pub func: C,
pub info: &'static str,
pub span: LocatedSpan<T, ()>,
pub parents: Vec<C>,
}
pub struct WarnTrack<C, T>
where
T: Copy,
C: Code,
{
pub func: C,
pub warn: &'static str,
pub span: LocatedSpan<T, ()>,
pub parents: Vec<C>,
}
pub struct OkTrack<C, T>
where
T: Copy,
C: Code,
{
pub func: C,
pub span: LocatedSpan<T, ()>,
pub parsed: LocatedSpan<T, ()>,
pub parents: Vec<C>,
}
pub struct ErrTrack<C, T>
where
T: Copy,
C: Code,
{
pub func: C,
pub code: C,
pub span: LocatedSpan<T, ()>,
pub err: String,
pub parents: Vec<C>,
}
pub struct ExitTrack<C, T>
where
T: Copy,
C: Code,
{
pub func: C,
pub parents: Vec<C>,
pub _phantom: PhantomData<LocatedSpan<T, ()>>,
}
impl<C, T> Track<C, T>
where
T: Copy,
C: Code,
{
pub fn func(&self) -> C {
match self {
Track::Enter(v) => v.func,
Track::Info(v) => v.func,
Track::Warn(v) => v.func,
Track::Debug(v) => v.func,
Track::Ok(v) => v.func,
Track::Err(v) => v.func,
Track::Exit(v) => v.func,
}
}
}