use core::fmt::{self, Debug, Display, Write};
#[derive(Debug, Clone)]
pub(crate) enum Context {
Generated(OofGeneratedContext),
Custom(String),
None,
}
impl Default for Context {
fn default() -> Self {
Context::None
}
}
impl From<OofGeneratedContext> for Context {
fn from(c: OofGeneratedContext) -> Self {
Context::Generated(c)
}
}
impl Display for Context {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Generated(c) => Display::fmt(c, f),
Self::Custom(m) => Display::fmt(m, f),
Self::None => write!(f, "Error encountered"),
}
}
}
impl Context {
pub(crate) fn is_none(&self) -> bool {
matches!(self, Context::None)
}
pub(crate) fn is_custom(&self) -> bool {
matches!(self, Context::Custom(_))
}
pub(crate) fn is_generated(&self) -> bool {
matches!(self, Context::Generated(_))
}
}
#[derive(Debug, Clone)]
pub struct OofGeneratedContext {
receiver: OofReceiver,
chain: Vec<OofMethod>,
returns_option: bool,
}
impl Display for OofGeneratedContext {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.receiver)?;
let is_multiline = self.chain.len() > 2;
if is_multiline && !f.alternate() {
let mut indented = Indented {
inner: f,
number: None,
started: false,
};
for method in &self.chain {
write!(indented, "\n.{method}")?;
}
} else {
for method in &self.chain {
write!(f, ".{method}")?;
}
}
if self.returns_option {
write!(f, " returned `None`")?;
} else {
write!(f, " failed")?;
}
Ok(())
}
}
impl OofGeneratedContext {
pub fn new(receiver: OofReceiver) -> Self {
Self {
returns_option: false,
receiver,
chain: Vec::new(),
}
}
pub fn with_capacity(receiver: OofReceiver, capacity: usize) -> Self {
Self {
returns_option: false,
receiver,
chain: Vec::with_capacity(capacity),
}
}
pub fn with_method(mut self, method: OofMethod) -> Self {
self.chain.push(method);
self
}
pub fn returns_option(&mut self) {
self.returns_option = true;
}
}
impl OofGeneratedContext {
pub fn fmt_args(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.receiver.args_exists() || self.chain.iter().any(|m| !m.args.is_empty()) {
writeln!(f, "\nParameters:")?;
let mut indented = Indented {
inner: f,
number: None,
started: false,
};
self.receiver.fmt_args(&mut indented)?;
for method in &self.chain {
method.fmt_args(&mut indented)?;
}
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub enum OofReceiver {
Ident(OofIdent),
Method(OofMethod),
Arg(OofArg),
}
impl From<OofIdent> for OofReceiver {
fn from(i: OofIdent) -> Self {
Self::Ident(i)
}
}
impl From<OofMethod> for OofReceiver {
fn from(m: OofMethod) -> Self {
Self::Method(m)
}
}
impl From<OofArg> for OofReceiver {
fn from(m: OofArg) -> Self {
Self::Arg(m)
}
}
impl Display for OofReceiver {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Ident(i) => Display::fmt(i, f),
Self::Method(m) => Display::fmt(m, f),
Self::Arg(a) => Display::fmt(a, f),
}
}
}
impl OofReceiver {
pub fn args_exists(&self) -> bool {
match self {
Self::Arg(_) => true,
Self::Method(m) => !m.args.is_empty(),
Self::Ident(_) => false,
}
}
pub fn fmt_args(&self, f: &mut impl Write) -> fmt::Result {
match self {
Self::Arg(a) => writeln!(f, "{a:#}"),
Self::Method(m) => m.fmt_args(f),
Self::Ident(_) => Ok(()),
}
}
}
#[derive(Debug, Clone)]
pub struct OofMethod {
is_async: bool,
name: &'static str,
args: Vec<OofArg>,
}
impl Display for OofMethod {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name)?;
write!(f, "(")?;
match self.args.as_slice() {
[rest @ .., last] => {
for arg in rest {
write!(f, "{}, ", arg)?;
}
write!(f, "{}", last)?;
}
[] => {}
}
write!(f, ")")?;
if self.is_async {
write!(f, ".await")?;
}
Ok(())
}
}
impl OofMethod {
fn fmt_args(&self, f: &mut impl Write) -> fmt::Result {
for arg in &self.args {
writeln!(f, "{arg:#}")?;
}
Ok(())
}
}
impl OofMethod {
pub fn new(is_async: bool, name: &'static str, args: Vec<OofArg>) -> OofMethod {
Self {
is_async,
name,
args,
}
}
}
#[derive(Debug, Clone)]
pub struct OofIdent {
name: &'static str,
is_async: bool,
}
impl Display for OofIdent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name)?;
if self.is_async {
write!(f, ".await")?;
}
Ok(())
}
}
impl OofIdent {
pub fn new(is_async: bool, name: &'static str) -> OofIdent {
Self { name, is_async }
}
}
#[derive(Debug, Clone)]
pub struct OofArg {
index: usize,
ty: &'static str,
display: Option<String>,
}
impl Display for OofArg {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "${}", self.index)?;
if f.alternate() {
write!(f, ": {}", self.ty)?;
if let Some(display) = &self.display {
write!(f, " = {display}")?;
}
}
Ok(())
}
}
impl OofArg {
pub fn new(index: usize, ty: &'static str, display: Option<String>) -> Self {
Self { index, ty, display }
}
}
#[cfg(feature = "location")]
#[non_exhaustive]
#[derive(Debug, Copy, Clone)]
pub struct Location {
file: &'static str,
line: u32,
column: u32,
}
#[cfg(feature = "location")]
impl Default for Location {
#[inline]
#[cfg_attr(feature = "location", track_caller)]
fn default() -> Self {
Self::caller()
}
}
#[cfg(feature = "location")]
impl fmt::Display for Location {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{file}:{line}:{column}",
file = self.file,
line = self.line,
column = self.column,
)
}
}
#[cfg(feature = "location")]
impl Location {
pub fn new(file: &'static str, line: u32, column: u32) -> Self {
Self { file, line, column }
}
#[inline]
#[cfg_attr(feature = "location", track_caller)]
pub fn caller() -> Self {
let loc = core::panic::Location::caller();
Self::new(loc.file(), loc.line(), loc.column())
}
}
pub(crate) struct Indented<'a, D> {
pub(crate) inner: &'a mut D,
pub(crate) number: Option<usize>,
pub(crate) started: bool,
}
impl<T> Write for Indented<'_, T>
where
T: Write,
{
fn write_str(&mut self, s: &str) -> fmt::Result {
for (i, line) in s.split('\n').enumerate() {
if !self.started {
self.started = true;
match self.number {
Some(number) => write!(self.inner, "{: >5}: ", number)?,
None => self.inner.write_str(" ")?,
}
} else if i > 0 {
self.inner.write_char('\n')?;
if self.number.is_some() {
self.inner.write_str(" ")?;
} else {
self.inner.write_str(" ")?;
}
}
self.inner.write_str(line)?;
}
Ok(())
}
}