hyperware_process_lib/vfs/
mod.rs1use crate::Request;
2use serde::{Deserialize, Serialize};
3use thiserror::Error;
4
5pub mod directory;
6pub mod file;
7
8pub use directory::*;
9pub use file::*;
10
11#[derive(Debug, Serialize, Deserialize)]
13pub struct VfsRequest {
14 pub path: String,
17 pub action: VfsAction,
18}
19
20#[derive(Debug, Serialize, Deserialize)]
21pub enum VfsAction {
22 CreateDrive,
23 CreateDir,
24 CreateDirAll,
25 CreateFile,
26 OpenFile { create: bool },
27 CloseFile,
28 Write,
29 WriteAll,
30 Append,
31 SyncAll,
32 Read,
33 ReadDir,
34 ReadToEnd,
35 ReadExact { length: u64 },
36 ReadToString,
37 Seek(SeekFrom),
38 RemoveFile,
39 RemoveDir,
40 RemoveDirAll,
41 Rename { new_path: String },
42 Metadata,
43 AddZip,
44 CopyFile { new_path: String },
45 Len,
46 SetLen(u64),
47 Hash,
48}
49
50#[derive(Debug, Serialize, Deserialize)]
51pub enum SeekFrom {
52 Start(u64),
53 End(i64),
54 Current(i64),
55}
56
57#[derive(Debug, Serialize, Deserialize, PartialEq)]
58pub enum FileType {
59 File,
60 Directory,
61 Symlink,
62 Other,
63}
64
65#[derive(Debug, Serialize, Deserialize)]
66pub struct FileMetadata {
67 pub file_type: FileType,
68 pub len: u64,
69}
70
71#[derive(Debug, Serialize, Deserialize, PartialEq)]
72pub struct DirEntry {
73 pub path: String,
74 pub file_type: FileType,
75}
76
77#[derive(Debug, Serialize, Deserialize)]
78pub enum VfsResponse {
79 Ok,
80 Err(VfsError),
81 Read,
82 SeekFrom { new_offset: u64 },
83 ReadDir(Vec<DirEntry>),
84 ReadToString(String),
85 Metadata(FileMetadata),
86 Len(u64),
87 Hash([u8; 32]),
88}
89
90#[derive(Clone, Debug, Error, Serialize, Deserialize)]
91pub enum VfsError {
92 #[error("no write capability for requested drive")]
93 NoWriteCap,
94 #[error("no read capability for requested drive")]
95 NoReadCap,
96 #[error("failed to generate capability for new drive")]
97 AddCapFailed,
98 #[error("request could not be deserialized to valid VfsRequest")]
99 MalformedRequest,
100 #[error("request type used requires a blob")]
101 NoBlob,
102 #[error("error parsing path: {path}: {error}")]
103 ParseError { error: String, path: String },
104 #[error("IO error: {0}")]
105 IOError(String),
106 #[error("non-file non-dir in zip")]
107 UnzipError,
108 #[error("SendError")]
110 SendError(crate::SendErrorKind),
111}
112
113pub fn vfs_request<T>(path: T, action: VfsAction) -> Request
114where
115 T: Into<String>,
116{
117 Request::new().target(("our", "vfs", "distro", "sys")).body(
118 serde_json::to_vec(&VfsRequest {
119 path: path.into(),
120 action,
121 })
122 .expect("failed to serialize VfsRequest"),
123 )
124}
125
126pub fn metadata(path: &str, timeout: Option<u64>) -> Result<FileMetadata, VfsError> {
128 let timeout = timeout.unwrap_or(5);
129
130 let message = vfs_request(path, VfsAction::Metadata)
131 .send_and_await_response(timeout)
132 .unwrap()
133 .map_err(|e| VfsError::SendError(e.kind))?;
134
135 match parse_response(message.body())? {
136 VfsResponse::Metadata(metadata) => Ok(metadata),
137 VfsResponse::Err(e) => Err(e),
138 _ => Err(VfsError::ParseError {
139 error: "unexpected response".to_string(),
140 path: path.to_string(),
141 }),
142 }
143}
144
145pub fn remove_path(path: &str, timeout: Option<u64>) -> Result<(), VfsError> {
147 let meta = metadata(path, timeout)?;
148
149 match meta.file_type {
150 FileType::Directory => remove_dir(path, timeout),
151 FileType::File => remove_file(path, timeout),
152 _ => Err(VfsError::ParseError {
153 error: "path is not a file or directory".to_string(),
154 path: path.to_string(),
155 }),
156 }
157}
158
159pub fn parse_response(body: &[u8]) -> Result<VfsResponse, VfsError> {
160 serde_json::from_slice::<VfsResponse>(body).map_err(|_| VfsError::MalformedRequest)
161}