typeshare_engine/
lib.rs

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