yab 0.2.0

Yet Another Benchmarking framework powered by `cachegrind`
//! Benchmark identifiers.

use std::{
    fmt,
    hash::{Hash, Hasher},
    panic::Location,
};

/// Benchmark identifier supplied to [`Bencher`](crate::Bencher) functions.
#[derive(Debug, Clone)]
pub struct BenchmarkId {
    pub(crate) name: String,
    pub(crate) location: &'static Location<'static>,
    pub(crate) args: Option<String>, // TODO: is this needed?
    pub(crate) capture: Option<&'static str>,
}

impl PartialEq for BenchmarkId {
    fn eq(&self, other: &Self) -> bool {
        self.name == other.name && self.args == other.args
    }
}

impl PartialEq<&str> for BenchmarkId {
    fn eq(&self, other: &&str) -> bool {
        if let Some(args) = &self.args {
            self.name.len() + 1 + args.len() == other.len()
                && other.starts_with(&self.name)
                && other.ends_with(args)
                && other.as_bytes()[self.name.len()] == b'/'
        } else {
            self.name == *other
        }
    }
}

impl Eq for BenchmarkId {}

impl Hash for BenchmarkId {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.name.hash(state);
        self.args.hash(state);
    }
}

impl fmt::Display for BenchmarkId {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str(&self.name)?;
        if let Some(args) = &self.args {
            write!(formatter, "/{args}")?;
        }
        if let Some(capture) = self.capture {
            write!(formatter, "/{capture}")?;
        }
        Ok(())
    }
}

impl<S: Into<String>> From<S> for BenchmarkId {
    #[track_caller]
    fn from(name: S) -> Self {
        Self {
            name: name.into(),
            location: Location::caller(),
            args: None,
            capture: None,
        }
    }
}

impl BenchmarkId {
    /// Creates an ID consisting of the base function name, and an argument representation.
    #[track_caller]
    pub fn new(name: impl Into<String>, args: impl fmt::Display) -> Self {
        Self {
            name: name.into(),
            location: Location::caller(),
            args: Some(args.to_string()),
            capture: None,
        }
    }

    pub(crate) fn to_string_without_capture(&self) -> String {
        if self.capture.is_some() {
            let mut without_capture = self.clone();
            without_capture.capture = None;
            without_capture.to_string()
        } else {
            self.to_string()
        }
    }
}