mirror of
https://github.com/typst/typst
synced 2025-07-27 06:17:53 +08:00
Move parts of the messages to hints
This commit is contained in:
parent
3b84ce91e3
commit
26f0a1358c
@ -29,8 +29,9 @@ pub fn init(command: &InitCommand) -> StrResult<()> {
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Find or download the package.
|
// Find or download the package.
|
||||||
let package_path =
|
let package_path = package_storage
|
||||||
package_storage.prepare_package(&spec, &mut PrintDownload(&spec))?;
|
.prepare_package(&spec, &mut PrintDownload(&spec))
|
||||||
|
.map_err(|e| eco_format!("{e}"))?;
|
||||||
|
|
||||||
// Parse the manifest.
|
// Parse the manifest.
|
||||||
let manifest = parse_manifest(&package_path)?;
|
let manifest = parse_manifest(&package_path)?;
|
||||||
|
@ -121,7 +121,7 @@ impl PackageStorage {
|
|||||||
let namespace_dir = packages_dir.join(format!("{}", spec.namespace));
|
let namespace_dir = packages_dir.join(format!("{}", spec.namespace));
|
||||||
if !namespace_dir.exists() {
|
if !namespace_dir.exists() {
|
||||||
return not_found(eco_format!(
|
return not_found(eco_format!(
|
||||||
"namespace @{} should be located at {}",
|
"the namespace @{} should be located at {}",
|
||||||
spec.namespace,
|
spec.namespace,
|
||||||
namespace_dir.display()
|
namespace_dir.display()
|
||||||
));
|
));
|
||||||
@ -129,7 +129,7 @@ impl PackageStorage {
|
|||||||
let package_dir = namespace_dir.join(format!("{}", spec.name));
|
let package_dir = namespace_dir.join(format!("{}", spec.name));
|
||||||
if !package_dir.exists() {
|
if !package_dir.exists() {
|
||||||
return not_found(eco_format!(
|
return not_found(eco_format!(
|
||||||
"{} does not have package '{}'",
|
"the registry at {} does not have package '{}'",
|
||||||
namespace_dir.display(),
|
namespace_dir.display(),
|
||||||
spec.name
|
spec.name
|
||||||
));
|
));
|
||||||
@ -220,7 +220,7 @@ impl PackageStorage {
|
|||||||
return Err(PackageError::NotFound(
|
return Err(PackageError::NotFound(
|
||||||
spec.clone(),
|
spec.clone(),
|
||||||
eco_format!(
|
eco_format!(
|
||||||
"{namespace_url} does not have package '{}'",
|
"the registry at {namespace_url} does not have package '{}'",
|
||||||
spec.name
|
spec.name
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
@ -315,6 +315,24 @@ impl<T> Trace<T> for SourceResult<T> {
|
|||||||
/// A result type with a string error message.
|
/// A result type with a string error message.
|
||||||
pub type StrResult<T> = Result<T, EcoString>;
|
pub type StrResult<T> = Result<T, EcoString>;
|
||||||
|
|
||||||
|
/// A common trait to add a span to any error
|
||||||
|
pub trait ErrAt {
|
||||||
|
/// Attach a span to a more specialized error and turn it into
|
||||||
|
/// a generic error, optionally with hints.
|
||||||
|
fn err_at(self, span: Span) -> SourceDiagnostic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Blanket implementation for anything that doesn't implement its own
|
||||||
|
/// convertion to a SourceDiagnostic.
|
||||||
|
impl<S> ErrAt for S
|
||||||
|
where
|
||||||
|
S: Into<EcoString>,
|
||||||
|
{
|
||||||
|
fn err_at(self, span: Span) -> SourceDiagnostic {
|
||||||
|
SourceDiagnostic::error(span, self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert a [`StrResult`] or [`HintedStrResult`] to a [`SourceResult`] by
|
/// Convert a [`StrResult`] or [`HintedStrResult`] to a [`SourceResult`] by
|
||||||
/// adding span information.
|
/// adding span information.
|
||||||
pub trait At<T> {
|
pub trait At<T> {
|
||||||
@ -324,18 +342,10 @@ pub trait At<T> {
|
|||||||
|
|
||||||
impl<T, S> At<T> for Result<T, S>
|
impl<T, S> At<T> for Result<T, S>
|
||||||
where
|
where
|
||||||
S: Into<EcoString>,
|
S: ErrAt,
|
||||||
{
|
{
|
||||||
fn at(self, span: Span) -> SourceResult<T> {
|
fn at(self, span: Span) -> SourceResult<T> {
|
||||||
self.map_err(|message| {
|
self.map_err(|e| eco_vec![e.err_at(span)])
|
||||||
let mut diagnostic = SourceDiagnostic::error(span, message);
|
|
||||||
if diagnostic.message.contains("(access denied)") {
|
|
||||||
diagnostic.hint("cannot read file outside of project root");
|
|
||||||
diagnostic
|
|
||||||
.hint("you can adjust the project root with the --root argument");
|
|
||||||
}
|
|
||||||
eco_vec![diagnostic]
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -469,6 +479,29 @@ impl FileError {
|
|||||||
_ => Self::Other(Some(eco_format!("{err}"))),
|
_ => Self::Other(Some(eco_format!("{err}"))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_hints(&self) -> EcoVec<EcoString> {
|
||||||
|
match self {
|
||||||
|
Self::NotFound(_) => eco_vec![],
|
||||||
|
Self::AccessDenied(_) => {
|
||||||
|
eco_vec![
|
||||||
|
eco_format!("cannot read file outside of project root"),
|
||||||
|
eco_format!(
|
||||||
|
"you can adjust the project root with the --root argument"
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
Self::IsDirectory(_) => eco_vec![],
|
||||||
|
Self::NotSource(_) => eco_vec![],
|
||||||
|
Self::InvalidUtf8(Some(path)) => {
|
||||||
|
eco_vec![eco_format!("tried to read {}", path.display())]
|
||||||
|
}
|
||||||
|
Self::InvalidUtf8(None) => eco_vec![],
|
||||||
|
Self::Package(error) => error.write_hints(),
|
||||||
|
Self::Other(Some(err)) => eco_vec![eco_format!("{err}")],
|
||||||
|
Self::Other(None) => eco_vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for FileError {}
|
impl std::error::Error for FileError {}
|
||||||
@ -479,26 +512,31 @@ impl Display for FileError {
|
|||||||
Self::NotFound(path) => {
|
Self::NotFound(path) => {
|
||||||
write!(f, "file not found (searched at {})", path.display())
|
write!(f, "file not found (searched at {})", path.display())
|
||||||
}
|
}
|
||||||
Self::AccessDenied(path) => {
|
|
||||||
write!(f, "failed to load file {} (access denied)", path.display())
|
|
||||||
}
|
|
||||||
Self::IsDirectory(path) => {
|
|
||||||
write!(f, "failed to load file {} (is a directory)", path.display())
|
|
||||||
}
|
|
||||||
Self::NotSource(path) => {
|
Self::NotSource(path) => {
|
||||||
write!(f, "{} is not a typst source file", path.display())
|
write!(f, "{} is not a typst source file", path.display())
|
||||||
}
|
}
|
||||||
Self::InvalidUtf8(Some(path)) => {
|
Self::InvalidUtf8(_) => write!(f, "file is not valid utf-8"),
|
||||||
write!(f, "file {} is not valid utf-8", path.display())
|
Self::IsDirectory(path) => {
|
||||||
|
write!(f, "failed to load file {} (is a directory)", path.display())
|
||||||
}
|
}
|
||||||
Self::InvalidUtf8(None) => f.pad("file is not valid utf-8"),
|
Self::AccessDenied(path) => {
|
||||||
Self::Package(error) => error.fmt(f),
|
write!(f, "failed to load file {} (access denied)", path.display())
|
||||||
Self::Other(Some(err)) => write!(f, "failed to load file ({err})"),
|
}
|
||||||
Self::Other(None) => f.pad("failed to load file"),
|
Self::Other(_) => {
|
||||||
|
write!(f, "failed to load file")
|
||||||
|
}
|
||||||
|
Self::Package(error) => write!(f, "{error}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ErrAt for FileError {
|
||||||
|
fn err_at(self, span: Span) -> SourceDiagnostic {
|
||||||
|
let hints = self.write_hints();
|
||||||
|
SourceDiagnostic::error(span, eco_format!("{}", self)).with_hints(hints)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Utf8Error> for FileError {
|
impl From<Utf8Error> for FileError {
|
||||||
fn from(_: Utf8Error) -> Self {
|
fn from(_: Utf8Error) -> Self {
|
||||||
Self::InvalidUtf8(None)
|
Self::InvalidUtf8(None)
|
||||||
@ -517,12 +555,6 @@ impl From<PackageError> for FileError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<FileError> for EcoString {
|
|
||||||
fn from(err: FileError) -> Self {
|
|
||||||
eco_format!("{err}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A result type with a package-related error.
|
/// A result type with a package-related error.
|
||||||
pub type PackageResult<T> = Result<T, PackageError>;
|
pub type PackageResult<T> = Result<T, PackageError>;
|
||||||
|
|
||||||
@ -545,48 +577,58 @@ pub enum PackageError {
|
|||||||
Other(Option<EcoString>),
|
Other(Option<EcoString>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PackageError {
|
||||||
|
fn write_hints(&self) -> EcoVec<EcoString> {
|
||||||
|
match self {
|
||||||
|
Self::NotFound(_, detail) => {
|
||||||
|
eco_vec![eco_format!("{detail}")]
|
||||||
|
}
|
||||||
|
Self::VersionNotFound(spec, latest, registry) => {
|
||||||
|
if let Some(version) = latest {
|
||||||
|
eco_vec![
|
||||||
|
eco_format!(
|
||||||
|
"the package {} exists, but not with version {}",
|
||||||
|
spec.name,
|
||||||
|
spec.version
|
||||||
|
),
|
||||||
|
eco_format!(
|
||||||
|
"the registry at {registry} provides up to version {version}"
|
||||||
|
),
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
eco_vec![eco_format!(
|
||||||
|
"the registry at {registry} contains no versions for this package"
|
||||||
|
)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::NetworkFailed(Some(err)) => eco_vec![eco_format!("{err}")],
|
||||||
|
Self::NetworkFailed(None) => eco_vec![],
|
||||||
|
Self::MalformedArchive(Some(err)) => eco_vec![eco_format!("{err}")],
|
||||||
|
Self::MalformedArchive(None) => {
|
||||||
|
eco_vec![eco_format!("the archive is malformed")]
|
||||||
|
}
|
||||||
|
Self::Other(Some(err)) => eco_vec![eco_format!("{err}")],
|
||||||
|
Self::Other(None) => eco_vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::error::Error for PackageError {}
|
impl std::error::Error for PackageError {}
|
||||||
|
|
||||||
impl Display for PackageError {
|
impl Display for PackageError {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::NotFound(spec, detail) => {
|
Self::NotFound(spec, _) => write!(f, "package {spec} not found"),
|
||||||
write!(f, "package not found: {detail} (searching for {spec})",)
|
Self::VersionNotFound(spec, _, _) => {
|
||||||
|
write!(f, "package version {spec} not found")
|
||||||
}
|
}
|
||||||
Self::VersionNotFound(spec, latest, registry) => {
|
Self::NetworkFailed(_) => write!(f, "failed to download package"),
|
||||||
write!(
|
Self::MalformedArchive(_) => write!(f, "failed to decompress package"),
|
||||||
f,
|
Self::Other(_) => f.pad("failed to load package"),
|
||||||
"package '{}' found, but version {} does not exist",
|
|
||||||
spec.name, spec.version
|
|
||||||
)?;
|
|
||||||
if let Some(version) = latest {
|
|
||||||
write!(f, " (latest version provided by {registry} is {version})")
|
|
||||||
} else {
|
|
||||||
write!(f, " ({registry} contains no versions for this package)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::NetworkFailed(Some(err)) => {
|
|
||||||
write!(f, "failed to download package ({err})")
|
|
||||||
}
|
|
||||||
Self::NetworkFailed(None) => f.pad("failed to download package"),
|
|
||||||
Self::MalformedArchive(Some(err)) => {
|
|
||||||
write!(f, "failed to decompress package ({err})")
|
|
||||||
}
|
|
||||||
Self::MalformedArchive(None) => {
|
|
||||||
f.pad("failed to decompress package (archive malformed)")
|
|
||||||
}
|
|
||||||
Self::Other(Some(err)) => write!(f, "failed to load package ({err})"),
|
|
||||||
Self::Other(None) => f.pad("failed to load package"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<PackageError> for EcoString {
|
|
||||||
fn from(err: PackageError) -> Self {
|
|
||||||
eco_format!("{err}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A result type with a data-loading-related error.
|
/// A result type with a data-loading-related error.
|
||||||
pub type LoadResult<T> = Result<T, LoadError>;
|
pub type LoadResult<T> = Result<T, LoadError>;
|
||||||
|
|
||||||
|
@ -41,9 +41,9 @@ pub use typst_utils as utils;
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use comemo::{Track, Tracked, Validate};
|
use comemo::{Track, Tracked, Validate};
|
||||||
use ecow::{eco_format, eco_vec, EcoString, EcoVec};
|
use ecow::{eco_format, eco_vec, EcoVec};
|
||||||
use typst_library::diag::{
|
use typst_library::diag::{
|
||||||
bail, warning, FileError, SourceDiagnostic, SourceResult, Warned,
|
bail, warning, ErrAt, FileError, SourceDiagnostic, SourceResult, Warned,
|
||||||
};
|
};
|
||||||
use typst_library::engine::{Engine, Route, Sink, Traced};
|
use typst_library::engine::{Engine, Route, Sink, Traced};
|
||||||
use typst_library::foundations::{StyleChain, Styles, Value};
|
use typst_library::foundations::{StyleChain, Styles, Value};
|
||||||
@ -191,8 +191,7 @@ fn hint_invalid_main_file(
|
|||||||
input: FileId,
|
input: FileId,
|
||||||
) -> EcoVec<SourceDiagnostic> {
|
) -> EcoVec<SourceDiagnostic> {
|
||||||
let is_utf8_error = matches!(file_error, FileError::InvalidUtf8(_));
|
let is_utf8_error = matches!(file_error, FileError::InvalidUtf8(_));
|
||||||
let mut diagnostic =
|
let mut diagnostic = file_error.err_at(Span::detached());
|
||||||
SourceDiagnostic::error(Span::detached(), EcoString::from(file_error));
|
|
||||||
|
|
||||||
// Attempt to provide helpful hints for UTF-8 errors. Perhaps the user
|
// Attempt to provide helpful hints for UTF-8 errors. Perhaps the user
|
||||||
// mistyped the filename. For example, they could have written "file.pdf"
|
// mistyped the filename. For example, they could have written "file.pdf"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user