oro_package_spec/
error.rs1use miette::Diagnostic;
2use node_semver::SemverError;
3use nom::error::{ContextError, ErrorKind, FromExternalError, ParseError};
4use thiserror::Error;
5use url::ParseError as UrlParseError;
6
7#[derive(Debug, Error, Diagnostic)]
12#[error("Error parsing package spec. {kind}")]
13#[diagnostic(
14 code(package_spec::no_parse),
15 url(docsrs),
16 help("The syntax for package specifiers is documented here: https://orogene.dev/book/guide/node_modules.html#specifier-syntax")
17)]
18pub struct PackageSpecError {
19 pub input: String,
20 pub offset: usize,
21 pub kind: SpecErrorKind,
22}
23
24impl PackageSpecError {
25 pub fn location(&self) -> (usize, usize) {
26 let prefix = &self.input.as_bytes()[..self.offset];
28
29 let line_number = bytecount::count(prefix, b'\n');
31
32 let line_begin = prefix
35 .iter()
36 .rev()
37 .position(|&b| b == b'\n')
38 .map(|pos| self.offset - pos)
39 .unwrap_or(0);
40
41 let line = self.input[line_begin..]
43 .lines()
44 .next()
45 .unwrap_or(&self.input[line_begin..])
46 .trim_end();
47
48 let column_number = self.input[self.offset..].as_ptr() as usize - line.as_ptr() as usize;
50
51 (line_number, column_number)
52 }
53}
54
55#[derive(Debug, Error)]
56pub enum SpecErrorKind {
57 #[error("Found invalid characters: `{0}`")]
58 InvalidCharacters(String),
59 #[error("Drive letters on Windows can only be alphabetical. Got `{0}`.")]
60 InvalidDriveLetter(char),
61 #[error("Invalid git host `{0}`. Only github:, gitlab:, gist:, and bitbucket: are supported in shorthands.")]
62 InvalidGitHost(String),
63 #[error(transparent)]
64 SemverParseError(SemverError),
65 #[error(transparent)]
66 UrlParseError(UrlParseError),
67 #[error(transparent)]
68 GitHostParseError(Box<PackageSpecError>),
69 #[error("Failed to parse {0} component of semver string.")]
70 Context(&'static str),
71 #[error("Incomplete input to semver parser.")]
72 IncompleteInput,
73 #[error("An unspecified error occurred.")]
74 Other,
75}
76
77#[derive(Debug)]
78pub(crate) struct SpecParseError<I> {
79 pub(crate) input: I,
80 pub(crate) context: Option<&'static str>,
81 pub(crate) kind: Option<SpecErrorKind>,
82}
83
84impl<I> ParseError<I> for SpecParseError<I> {
85 fn from_error_kind(input: I, _kind: nom::error::ErrorKind) -> Self {
86 Self {
87 input,
88 context: None,
89 kind: None,
90 }
91 }
92
93 fn append(_input: I, _kind: nom::error::ErrorKind, other: Self) -> Self {
94 other
95 }
96}
97
98impl<I> ContextError<I> for SpecParseError<I> {
99 fn add_context(_input: I, ctx: &'static str, mut other: Self) -> Self {
100 other.context = Some(ctx);
101 other
102 }
103}
104
105impl<'a> FromExternalError<&'a str, SpecParseError<&'a str>> for SpecParseError<&'a str> {
109 fn from_external_error(_input: &'a str, _kind: ErrorKind, e: SpecParseError<&'a str>) -> Self {
110 e
111 }
112}
113
114impl<'a> FromExternalError<&'a str, SemverError> for SpecParseError<&'a str> {
115 fn from_external_error(input: &'a str, _kind: ErrorKind, e: SemverError) -> Self {
116 SpecParseError {
117 input,
118 context: None,
119 kind: Some(SpecErrorKind::SemverParseError(e)),
120 }
121 }
122}
123
124impl<'a> FromExternalError<&'a str, UrlParseError> for SpecParseError<&'a str> {
125 fn from_external_error(input: &'a str, _kind: ErrorKind, e: UrlParseError) -> Self {
126 SpecParseError {
127 input,
128 context: None,
129 kind: Some(SpecErrorKind::UrlParseError(e)),
130 }
131 }
132}
133
134impl<'a> FromExternalError<&'a str, PackageSpecError> for SpecParseError<&'a str> {
135 fn from_external_error(input: &'a str, _kind: ErrorKind, e: PackageSpecError) -> Self {
136 SpecParseError {
137 input,
138 context: None,
139 kind: Some(SpecErrorKind::GitHostParseError(Box::new(e))),
140 }
141 }
142}