specta_typescript/
error.rs

1use core::fmt;
2use std::borrow::Cow;
3
4use specta_serde::SerdeError;
5use thiserror::Error;
6
7use specta::ImplLocation;
8
9use super::ExportPath;
10
11/// Describes where an error occurred.
12#[derive(Error, Debug, PartialEq)]
13pub enum NamedLocation {
14    Type,
15    Field,
16    Variant,
17}
18
19impl fmt::Display for NamedLocation {
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        match self {
22            Self::Type => write!(f, "type"),
23            Self::Field => write!(f, "field"),
24            Self::Variant => write!(f, "variant"),
25        }
26    }
27}
28
29/// The error type for the TypeScript exporter.
30#[derive(Error, Debug)]
31#[non_exhaustive]
32pub enum ExportError {
33    #[error("Attempted to export '{0}' but Specta configuration forbids exporting BigInt types (i64, u64, i128, u128) because we don't know if your se/deserializer supports it. You can change this behavior by editing your `ExportConfiguration`!")]
34    BigIntForbidden(ExportPath),
35    #[error("Serde error: {0}")]
36    Serde(#[from] SerdeError),
37    // #[error("Attempted to export '{0}' but was unable to export a tagged type which is unnamed")]
38    // UnableToTagUnnamedType(ExportPath),
39    #[error("Attempted to export '{1}' but was unable to due to {0} name '{2}' conflicting with a reserved keyword in Typescript. Try renaming it or using `#[specta(rename = \"new name\")]`")]
40    ForbiddenName(NamedLocation, ExportPath, &'static str),
41    #[error("Attempted to export '{1}' but was unable to due to {0} name '{2}' containing an invalid character")]
42    InvalidName(NamedLocation, ExportPath, String),
43    #[error("Attempted to export '{0}' with tagging but the type is not tagged.")]
44    InvalidTagging(ExportPath),
45    #[error("Attempted to export '{0}' with internal tagging but the variant is a tuple struct.")]
46    InvalidTaggedVariantContainingTupleStruct(ExportPath),
47    #[error("Unable to export type named '{0}' from locations '{:?}' '{:?}'", .1.as_str(), .2.as_str())]
48    DuplicateTypeName(Cow<'static, str>, ImplLocation, ImplLocation),
49    #[error("IO error: {0}")]
50    Io(#[from] std::io::Error),
51    #[error("fmt error: {0}")]
52    Fmt(#[from] std::fmt::Error),
53    #[error("Failed to export '{0}' due to error: {1}")]
54    Other(ExportPath, String),
55}
56
57// TODO: This `impl` is cringe
58impl PartialEq for ExportError {
59    fn eq(&self, other: &Self) -> bool {
60        match (self, other) {
61            (Self::BigIntForbidden(l0), Self::BigIntForbidden(r0)) => l0 == r0,
62            (Self::Serde(l0), Self::Serde(r0)) => l0 == r0,
63            // (Self::UnableToTagUnnamedType(l0), Self::UnableToTagUnnamedType(r0)) => l0 == r0,
64            (Self::ForbiddenName(l0, l1, l2), Self::ForbiddenName(r0, r1, r2)) => {
65                l0 == r0 && l1 == r1 && l2 == r2
66            }
67            (Self::InvalidName(l0, l1, l2), Self::InvalidName(r0, r1, r2)) => {
68                l0 == r0 && l1 == r1 && l2 == r2
69            }
70            (Self::InvalidTagging(l0), Self::InvalidTagging(r0)) => l0 == r0,
71            (
72                Self::InvalidTaggedVariantContainingTupleStruct(l0),
73                Self::InvalidTaggedVariantContainingTupleStruct(r0),
74            ) => l0 == r0,
75            (Self::DuplicateTypeName(l0, l1, l2), Self::DuplicateTypeName(r0, r1, r2)) => {
76                l0 == r0 && l1 == r1 && l2 == r2
77            }
78            (Self::Io(l0), Self::Io(r0)) => l0.to_string() == r0.to_string(), // This is a bit hacky but it will be fine for usage in unit tests!
79            (Self::Other(l0, l1), Self::Other(r0, r1)) => l0 == r0 && l1 == r1,
80            _ => false,
81        }
82    }
83}