show_my_errors/
annotation.rs

1use super::{Error, Result};
2use std::{
3    fmt::{self, Display},
4    ops::Range,
5};
6
7/// Annotation severity
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum Severity {
10    Info,
11    Warning,
12    Error,
13}
14
15impl Display for Severity {
16    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
17        match self {
18            Self::Info => f.write_str("info"),
19            Self::Warning => f.write_str("warning"),
20            Self::Error => f.write_str("error"),
21        }
22    }
23}
24
25/// Info about annotation. You can create these manually
26/// and then pass to [`AnnotationList::add`](crate::AnnotationList::add)
27/// or just use [`AnnotationList`](crate::AnnotationList)s helper methods
28#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct Annotation {
30    range: Range<usize>,
31    /// `header` will be shown above error message
32    pub header: Option<String>,
33    /// `text` will be shown near annotated fragment.
34    /// Note that fragment will be highlighted even if `text` is `None`.
35    /// To disable this, pass a zero length range when creating the annotation.
36    pub text: Option<String>,
37    pub severity: Severity,
38}
39
40/// Something that can be converted to `Option<String>`.
41/// You probably shouldn't implement this trait yourself, it's here only
42/// to simplify annotation creation syntax.
43pub trait AnnotationText {
44    fn into_option_string(self) -> Option<String>;
45}
46
47impl AnnotationText for String {
48    fn into_option_string(self) -> Option<String> {
49        Some(self)
50    }
51}
52
53impl AnnotationText for &'_ str {
54    fn into_option_string(self) -> Option<String> {
55        Some(self.into())
56    }
57}
58
59impl AnnotationText for Option<String> {
60    fn into_option_string(self) -> Option<String> {
61        self
62    }
63}
64
65impl Annotation {
66    /// Create new annotation.
67    /// Will return [`Error::InvalidRange`] if provided range has `start > end`.
68    /// You can pass `&str`, `String` or `Option<String>` as header and text arguments.
69    /// ```rust
70    /// # use show_my_errors::{Annotation, Severity, Error};
71    /// assert_eq!(
72    ///     Annotation::new(0..5, Severity::Info, "header", "text").unwrap(),
73    ///     Annotation::new(
74    ///         0..5, Severity::Info, Some("header".into()), Some("text".into())
75    ///     ).unwrap()
76    /// );
77    /// assert!(Annotation::new(0..5, Severity::Warning, None, None).is_ok());
78    /// assert_eq!(
79    ///     Annotation::new(5..0, Severity::Info, "h", "t"), Err(Error::InvalidRange(5, 0))
80    /// );
81    /// ```
82    pub fn new(
83        range: Range<usize>,
84        severity: Severity,
85        header: impl AnnotationText,
86        text: impl AnnotationText,
87    ) -> Result<Self> {
88        if range.end < range.start {
89            Err(Error::InvalidRange(range.start, range.end))
90        } else {
91            Ok(Self {
92                range,
93                severity,
94                header: header.into_option_string(),
95                text: text.into_option_string(),
96            })
97        }
98    }
99
100    /// Create a new [`Severity::Info`] annotation
101    pub fn info(
102        range: Range<usize>,
103        header: impl AnnotationText,
104        text: impl AnnotationText,
105    ) -> Result<Self> {
106        Self::new(range, Severity::Info, header, text)
107    }
108
109    /// Create a new [`Severity::Warning`] annotation
110    pub fn warning(
111        range: Range<usize>,
112        header: impl AnnotationText,
113        text: impl AnnotationText,
114    ) -> Result<Self> {
115        Self::new(range, Severity::Warning, header, text)
116    }
117
118    /// Create a new [`Severity::Error`] annotation
119    pub fn error(
120        range: Range<usize>,
121        header: impl AnnotationText,
122        text: impl AnnotationText,
123    ) -> Result<Self> {
124        Self::new(range, Severity::Error, header, text)
125    }
126
127    /// Get annotations range
128    pub fn range(&self) -> &Range<usize> {
129        &self.range
130    }
131}