Skip to main content

harn_vm/value/
io_error.rs

1use super::{DictMap, ErrorCategory, VmDictExt, VmError, VmValue};
2
3/// Canonical, stable string key for an [`std::io::ErrorKind`].
4///
5/// These keys are a script-facing contract. Keep the mapping centralized so
6/// filesystem and process boundaries never drift into different spellings.
7pub fn io_error_kind_str(error: &std::io::Error) -> &'static str {
8    use std::io::ErrorKind;
9    match error.kind() {
10        ErrorKind::NotFound => "not_found",
11        ErrorKind::PermissionDenied => "permission_denied",
12        ErrorKind::AlreadyExists => "already_exists",
13        ErrorKind::StorageFull => "storage_full",
14        ErrorKind::QuotaExceeded => "quota_exceeded",
15        ErrorKind::FileTooLarge => "file_too_large",
16        ErrorKind::ReadOnlyFilesystem => "read_only_filesystem",
17        ErrorKind::NotADirectory => "not_a_directory",
18        ErrorKind::IsADirectory => "is_a_directory",
19        ErrorKind::DirectoryNotEmpty => "directory_not_empty",
20        ErrorKind::CrossesDevices => "crosses_devices",
21        ErrorKind::TooManyLinks => "too_many_links",
22        ErrorKind::InvalidInput => "invalid_input",
23        ErrorKind::InvalidData => "invalid_data",
24        ErrorKind::TimedOut => "timed_out",
25        ErrorKind::Interrupted => "interrupted",
26        ErrorKind::UnexpectedEof => "unexpected_eof",
27        ErrorKind::WouldBlock => "would_block",
28        ErrorKind::OutOfMemory => "out_of_memory",
29        ErrorKind::ResourceBusy => "resource_busy",
30        ErrorKind::ExecutableFileBusy => "executable_file_busy",
31        // `ErrorKind` is non-exhaustive. Unknown platform kinds deliberately
32        // collapse to one stable value instead of leaking a debug spelling.
33        _ => "other",
34    }
35}
36
37/// Lower an OS I/O failure to the stable script-facing value.
38pub fn io_error_value(
39    error: &std::io::Error,
40    message: impl AsRef<str>,
41    category: Option<ErrorCategory>,
42) -> VmValue {
43    let mut failure = DictMap::new();
44    failure.put_str("error", "io_error");
45    failure.put_str("kind", io_error_kind_str(error));
46    failure.put_str("message", message);
47    if let Some(category) = category {
48        failure.put_str("category", category.as_str());
49    }
50    VmValue::dict(failure)
51}
52
53/// Wrap [`io_error_value`] for builtins that throw instead of returning it.
54pub fn io_error_thrown(
55    error: &std::io::Error,
56    message: impl AsRef<str>,
57    category: Option<ErrorCategory>,
58) -> VmError {
59    VmError::Thrown(io_error_value(error, message, category))
60}
61
62/// Throw an I/O failure classified as an environmental runtime condition.
63pub fn environment_io_error_thrown(error: &std::io::Error, message: impl AsRef<str>) -> VmError {
64    io_error_thrown(error, message, Some(ErrorCategory::Environment))
65}