Report path on a FileError

This commit is contained in:
Neven Villani 2025-06-12 15:22:51 +02:00
parent d148f3ebe6
commit 3846955ce6
5 changed files with 31 additions and 19 deletions

View File

@ -404,7 +404,9 @@ fn system_path(
// Join the path to the root. If it tries to escape, deny // Join the path to the root. If it tries to escape, deny
// access. Note: It can still escape via symlinks. // access. Note: It can still escape via symlinks.
id.vpath().resolve(root).ok_or(FileError::AccessDenied) id.vpath()
.resolve(root)
.ok_or_else(|| FileError::AccessDenied(id.vpath().as_rootless_path().into()))
} }
/// Reads a file from a `FileId`. /// Reads a file from a `FileId`.
@ -427,7 +429,7 @@ fn read(
fn read_from_disk(path: &Path) -> FileResult<Vec<u8>> { fn read_from_disk(path: &Path) -> FileResult<Vec<u8>> {
let f = |e| FileError::from_io(e, path); let f = |e| FileError::from_io(e, path);
if fs::metadata(path).map_err(f)?.is_dir() { if fs::metadata(path).map_err(f)?.is_dir() {
Err(FileError::IsDirectory) Err(FileError::IsDirectory(path.into()))
} else { } else {
fs::read(path).map_err(f) fs::read(path).map_err(f)
} }

View File

@ -143,7 +143,6 @@ impl PackageStorage {
} }
/// Tries to determine the latest version of a package. /// Tries to determine the latest version of a package.
/// TODO improve here too
pub fn determine_latest_version( pub fn determine_latest_version(
&self, &self,
spec: &VersionlessPackageSpec, spec: &VersionlessPackageSpec,

View File

@ -440,13 +440,13 @@ pub enum FileError {
/// A file was not found at this path. /// A file was not found at this path.
NotFound(PathBuf), NotFound(PathBuf),
/// A file could not be accessed. /// A file could not be accessed.
AccessDenied, AccessDenied(PathBuf),
/// A directory was found, but a file was expected. /// A directory was found, but a file was expected.
IsDirectory, IsDirectory(PathBuf),
/// The file is not a Typst source file, but should have been. /// The file is not a Typst source file, but should have been.
NotSource, NotSource(PathBuf),
/// The file was not valid UTF-8, but should have been. /// The file was not valid UTF-8, but should have been.
InvalidUtf8, InvalidUtf8(Option<PathBuf>),
/// The package the file is part of could not be loaded. /// The package the file is part of could not be loaded.
Package(PackageError), Package(PackageError),
/// Another error. /// Another error.
@ -460,11 +460,11 @@ impl FileError {
pub fn from_io(err: io::Error, path: &Path) -> Self { pub fn from_io(err: io::Error, path: &Path) -> Self {
match err.kind() { match err.kind() {
io::ErrorKind::NotFound => Self::NotFound(path.into()), io::ErrorKind::NotFound => Self::NotFound(path.into()),
io::ErrorKind::PermissionDenied => Self::AccessDenied, io::ErrorKind::PermissionDenied => Self::AccessDenied(path.into()),
io::ErrorKind::InvalidData io::ErrorKind::InvalidData
if err.to_string().contains("stream did not contain valid UTF-8") => if err.to_string().contains("stream did not contain valid UTF-8") =>
{ {
Self::InvalidUtf8 Self::InvalidUtf8(Some(path.into()))
} }
_ => Self::Other(Some(eco_format!("{err}"))), _ => Self::Other(Some(eco_format!("{err}"))),
} }
@ -479,10 +479,19 @@ 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 => f.pad("failed to load file (access denied)"), Self::AccessDenied(path) => {
Self::IsDirectory => f.pad("failed to load file (is a directory)"), write!(f, "failed to load file {} (access denied)", path.display())
Self::NotSource => f.pad("not a typst source file"), }
Self::InvalidUtf8 => f.pad("file is not valid utf-8"), Self::IsDirectory(path) => {
write!(f, "failed to load file {} (is a directory)", path.display())
}
Self::NotSource(path) => {
write!(f, "{} is not a typst source file", path.display())
}
Self::InvalidUtf8(Some(path)) => {
write!(f, "file {} is not valid utf-8", path.display())
}
Self::InvalidUtf8(None) => f.pad("file is not valid utf-8"),
Self::Package(error) => error.fmt(f), Self::Package(error) => error.fmt(f),
Self::Other(Some(err)) => write!(f, "failed to load file ({err})"), Self::Other(Some(err)) => write!(f, "failed to load file ({err})"),
Self::Other(None) => f.pad("failed to load file"), Self::Other(None) => f.pad("failed to load file"),
@ -492,13 +501,13 @@ impl Display for FileError {
impl From<Utf8Error> for FileError { impl From<Utf8Error> for FileError {
fn from(_: Utf8Error) -> Self { fn from(_: Utf8Error) -> Self {
Self::InvalidUtf8 Self::InvalidUtf8(None)
} }
} }
impl From<FromUtf8Error> for FileError { impl From<FromUtf8Error> for FileError {
fn from(_: FromUtf8Error) -> Self { fn from(_: FromUtf8Error) -> Self {
Self::InvalidUtf8 Self::InvalidUtf8(None)
} }
} }
@ -545,7 +554,11 @@ impl Display for PackageError {
write!(f, "package not found: {detail} (searching for {spec})",) write!(f, "package not found: {detail} (searching for {spec})",)
} }
Self::VersionNotFound(spec, latest, registry) => { Self::VersionNotFound(spec, latest, registry) => {
write!(f, "package found, but version {} does not exist", spec.version,)?; write!(
f,
"package '{}' found, but version {} does not exist",
spec.name, spec.version
)?;
if let Some(version) = latest { if let Some(version) = latest {
write!(f, " (latest version provided by {registry} is {version})") write!(f, " (latest version provided by {registry} is {version})")
} else { } else {

View File

@ -190,7 +190,7 @@ fn hint_invalid_main_file(
file_error: FileError, file_error: FileError,
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 =
SourceDiagnostic::error(Span::detached(), EcoString::from(file_error)); SourceDiagnostic::error(Span::detached(), EcoString::from(file_error));

View File

@ -1,2 +0,0 @@
alias testit="cargo test --workspace --test tests --"