summaryrefslogtreecommitdiff
path: root/src/diag.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-10-04 20:22:11 +0200
committerLaurenz <laurmaedje@gmail.com>2020-10-04 20:22:11 +0200
commitef8aa763faa59fd62c90c6d6245e8d2c5eece35e (patch)
treed36192af0c770b076a5004ba8bcae3c2df728c75 /src/diag.rs
parenta41d7ab47dda1e30465bdf91fd02bca0e634a38d (diff)
Shorten some names ↔
Diffstat (limited to 'src/diag.rs')
-rw-r--r--src/diag.rs102
1 files changed, 102 insertions, 0 deletions
diff --git a/src/diag.rs b/src/diag.rs
new file mode 100644
index 00000000..38f3d0a2
--- /dev/null
+++ b/src/diag.rs
@@ -0,0 +1,102 @@
+//! Diagnostics for source code.
+//!
+//! There are no fatal errors. The document will always compile and yield a
+//! layout on a best effort process, but diagnostics are nevertheless generated
+//! for incorrect things.
+
+use std::fmt::{self, Display, Formatter};
+
+/// A diagnostic that arose in parsing or layouting.
+#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
+#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
+pub struct Diag {
+ /// How severe / important the diagnostic is.
+ pub level: Level,
+ /// A message describing the diagnostic.
+ pub message: String,
+}
+
+/// How severe / important a diagnostic is.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
+#[cfg_attr(feature = "serialize", serde(rename_all = "camelCase"))]
+pub enum Level {
+ Warning,
+ Error,
+}
+
+impl Diag {
+ /// Create a new diagnostic from message and level.
+ pub fn new(level: Level, message: impl Into<String>) -> Self {
+ Self { level, message: message.into() }
+ }
+}
+
+impl Display for Level {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ f.pad(match self {
+ Self::Warning => "warning",
+ Self::Error => "error",
+ })
+ }
+}
+
+/// Construct a diagnostic with [`Error`] level.
+///
+/// ```
+/// # use typstc::error;
+/// # use typstc::syntax::Span;
+/// # use typstc::Feedback;
+/// # let span = Span::ZERO;
+/// # let mut feedback = Feedback::new();
+/// # let name = "";
+/// // Create formatted error values.
+/// let error = error!("expected {}", name);
+///
+/// // Create spanned errors.
+/// let spanned = error!(span, "there is an error here");
+///
+/// // Create an error and directly add it to existing feedback.
+/// error!(@feedback, span, "oh no!");
+/// ```
+///
+/// [`Error`]: diagnostic/enum.Level.html#variant.Error
+#[macro_export]
+macro_rules! error {
+ ($($tts:tt)*) => {
+ $crate::__impl_diagnostic!($crate::diag::Level::Error; $($tts)*)
+ };
+}
+
+/// Construct a diagnostic with [`Warning`] level.
+///
+/// This works exactly like `error!`. See its documentation for more
+/// information.
+///
+/// [`Warning`]: diagnostic/enum.Level.html#variant.Warning
+#[macro_export]
+macro_rules! warning {
+ ($($tts:tt)*) => {
+ $crate::__impl_diagnostic!($crate::diag::Level::Warning; $($tts)*)
+ };
+}
+
+/// Backs the `error!` and `warning!` macros.
+#[macro_export]
+#[doc(hidden)]
+macro_rules! __impl_diagnostic {
+ ($level:expr; @$feedback:expr, $($tts:tt)*) => {
+ $feedback.diags.push($crate::__impl_diagnostic!($level; $($tts)*));
+ };
+
+ ($level:expr; $fmt:literal $($tts:tt)*) => {
+ $crate::diag::Diag::new($level, format!($fmt $($tts)*))
+ };
+
+ ($level:expr; $span:expr, $fmt:literal $($tts:tt)*) => {
+ $crate::syntax::Spanned::new(
+ $crate::__impl_diagnostic!($level; $fmt $($tts)*),
+ $span,
+ )
+ };
+}