typeshare_engine/
lib.rs

1pub mod args;
2pub mod config;
3pub mod driver;
4pub mod parser;
5mod rename;
6mod serde;
7mod topsort;
8mod type_parser;
9mod visitors;
10pub mod writer;
11
12use std::{
13    fmt::{self, Display, Write},
14    io,
15    path::PathBuf,
16};
17
18use indent_write::fmt::IndentWriter;
19use proc_macro2::LineColumn;
20use syn::spanned::Spanned;
21use thiserror::Error;
22use typeshare_model::prelude::{CrateName, TypeName};
23
24// Re-export this for the driver crate to use
25pub use typeshare_model::prelude::FilesMode;
26
27#[derive(Debug, Error)]
28pub struct FileParseErrors {
29    pub path: PathBuf,
30    pub crate_name: Option<CrateName>,
31    pub kind: FileErrorKind,
32}
33
34impl FileParseErrors {
35    pub fn new(path: PathBuf, crate_name: Option<CrateName>, kind: FileErrorKind) -> Self {
36        Self {
37            path,
38            crate_name,
39            kind,
40        }
41    }
42}
43
44impl Display for FileParseErrors {
45    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46        write!(
47            f,
48            "in {path}, {error}",
49            path = self.path.display(),
50            error = self.kind
51        )
52    }
53}
54
55#[derive(Debug)]
56pub enum FileErrorKind {
57    UnknownCrate,
58    ParseErrors(ParseErrorSet),
59    ReadError(io::Error),
60}
61
62impl Display for FileErrorKind {
63    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64        match self {
65            FileErrorKind::UnknownCrate => f.write_str("unknown crate in mutli-file mode"),
66            FileErrorKind::ParseErrors(parse_error_set) => parse_error_set.fmt(f),
67            FileErrorKind::ReadError(error) => write!(f, "i/o error: {error}"),
68        }
69    }
70}
71/// A group of parse errors from a single file. Guaranteed to be non-emtpy.
72#[derive(Debug)]
73pub struct ParseErrorSet {
74    errors: Vec<ParseError>,
75}
76
77impl ParseErrorSet {
78    pub fn collect(errors: impl IntoIterator<Item = ParseError>) -> Result<(), Self> {
79        let mut errors = errors.into_iter().peekable();
80
81        match errors.peek() {
82            Some(_) => Err(Self {
83                errors: errors.collect(),
84            }),
85            None => Ok(()),
86        }
87    }
88}
89
90impl From<ParseError> for ParseErrorSet {
91    fn from(error: ParseError) -> Self {
92        Self {
93            errors: Vec::from([error]),
94        }
95    }
96}
97
98impl Display for ParseErrorSet {
99    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100        match self.errors.as_slice() {
101            [] => Ok(()),
102            [error] => write!(f, "{error}"),
103            errors => {
104                writeln!(f, "multiple errors:")?;
105                let mut f = IndentWriter::new("  ", f);
106                errors.iter().try_for_each(|error| write!(f, "{error}"))
107            }
108        }
109    }
110}
111
112#[derive(Debug, Error)]
113#[error("at {}:{}..{}:{}: {kind}",
114    .start.line,
115    .start.column,
116    .end.line,
117    .end.column,
118)]
119pub struct ParseError {
120    start: LineColumn,
121    end: LineColumn,
122    kind: ParseErrorKind,
123}
124
125impl ParseError {
126    pub fn new(span: &impl Spanned, kind: ParseErrorKind) -> Self {
127        let span = span.span();
128        Self {
129            start: span.start(),
130            end: span.end(),
131            kind,
132        }
133    }
134}
135
136/// Errors that can occur while parsing Rust source input.
137#[derive(Debug, Error)]
138#[allow(missing_docs)]
139pub enum ParseErrorKind {
140    #[error("{0}")]
141    SynError(#[from] syn::Error),
142    #[error("failed to parse a rust type: {0}")]
143    RustTypeParseError(#[from] RustTypeParseError),
144    #[error("unsupported language encountered: {0}")]
145    UnsupportedLanguage(String),
146    #[error("unsupported type encountered: {0}")]
147    UnsupportedType(String),
148    #[error("tuple structs with more than one field are currently unsupported")]
149    ComplexTupleStruct,
150    #[error("multiple unnamed associated types are not currently supported")]
151    MultipleUnnamedAssociatedTypes,
152    #[error("the serde tag attribute is not supported for non-algebraic enums: {enum_ident}")]
153    SerdeTagNotAllowed { enum_ident: TypeName },
154    #[error("the serde content attribute is not supported for non-algebraic enums: {enum_ident}")]
155    SerdeContentNotAllowed { enum_ident: TypeName },
156    #[error("serde tag attribute needs to be specified for algebraic enum {enum_ident}. e.g. #[serde(tag = \"type\", content = \"content\")]")]
157    SerdeTagRequired { enum_ident: TypeName },
158    #[error("serde content attribute needs to be specified for algebraic enum {enum_ident}. e.g. #[serde(tag = \"type\", content = \"content\")]")]
159    SerdeContentRequired { enum_ident: TypeName },
160    #[error("the serde flatten attribute is not currently supported")]
161    SerdeFlattenNotAllowed,
162    #[error("the expression assigned to this constant variable is not a numeric literal")]
163    RustConstExprInvalid,
164    #[error("you cannot use typeshare on a constant that is not a numeric literal")]
165    RustConstTypeInvalid,
166}
167
168#[derive(Debug, Error)]
169#[allow(missing_docs)]
170pub enum RustTypeParseError {
171    #[error("{0:?}")]
172    UnsupportedType(Vec<String>),
173    #[error("Unexpected token when parsing type: `{0}`. This is an internal error, please ping a typeshare developer to resolve this problem.")]
174    UnexpectedToken(String),
175    #[error("Tuples are not allowed in typeshare types")]
176    UnexpectedParameterizedTuple,
177    #[error("Could not parse numeric literal")]
178    NumericLiteral(syn::parse::Error),
179}