1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
use std::borrow::Cow;
use std::collections::HashMap;
use std::error::Error as StdError;
use std::fmt;

use crate::kinds::{tag_check, error_name};
use crate::traits::ErrorKind;


/// Error object returned from any EdgeDB call
// This includes boxed error, because propagating through call chain is
// faster when error is just one pointer
#[derive(Debug)]
pub struct Error(pub(crate) Box<Inner>);

/// Tag that is used to group simiar errors
pub struct Tag { pub(crate)  bit: u32 }

#[derive(Debug)]
pub(crate) struct Inner {
    pub code: u32,
    pub messages: Vec<Cow<'static, str>>,
    pub error: Option<Box<dyn StdError + Send + Sync + 'static>>,
    pub headers: HashMap<u16, bytes::Bytes>,
}


impl Error {
    pub fn is<T: ErrorKind>(&self) -> bool {
        T::is_superclass_of(self.0.code)
    }
    pub fn has_tag(&self, tag: Tag) -> bool {
        tag_check(self.0.code, tag.bit)
    }
    pub fn context<S: Into<Cow<'static, str>>>(mut self, msg: S) -> Error {
        self.0.messages.push(msg.into());
        self
    }
    pub fn headers(&self) -> &HashMap<u16, bytes::Bytes> {
        &self.0.headers
    }
    pub fn with_headers(mut self, headers: HashMap<u16, bytes::Bytes>)
        -> Error
    {
        self.0.headers = headers;
        self
    }
    pub fn kind_name(&self) -> &str {
        error_name(self.0.code)
    }
    pub fn kind_debug(&self) -> impl fmt::Display {
        format!("{} [0x{:08X}]", error_name(self.0.code), self.0.code)
    }
    pub fn initial_message(&self) -> Option<&str> {
        self.0.messages.first().map(|m| &m[..])
    }
    pub fn from_code(code: u32) -> Error {
        Error(Box::new(Inner {
            code,
            messages: Vec::new(),
            error: None,
            headers: HashMap::new(),
        }))
    }
    pub fn refine_kind<T: ErrorKind>(mut self) -> Error {
        self.0.code = T::CODE;
        self
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let kind = self.kind_name();
        if f.alternate() {
            write!(f, "{}", kind)?;
            for msg in self.0.messages.iter().rev() {
                write!(f, ": {}", msg)?;
            }
            if let Some(mut src) = self.source() {
                write!(f, ": {}", src)?;
                while let Some(next) = src.source() {
                    write!(f, ": {}", next)?;
                    src = next;
                }
            }

        } else {
            if let Some(last) = self.0.messages.last() {
                write!(f, "{}: {}", kind, last)?;
            } else {
                write!(f, "{}", kind)?;
            }
        }
        Ok(())
    }
}

impl StdError for Error {
    fn source(&self) -> Option<&(dyn StdError + 'static)> {
        self.0.error.as_ref().map(|b| b.as_ref() as &dyn std::error::Error)
    }
}