Skip to main content

xtask_todo_lib/devshell/workspace/
io.rs

1//! Shared workspace file reads: Mode P γ ([`GuestFsOps`]) vs Mode S ([`Vfs`]).
2//!
3//! Used by [`crate::devshell::command::dispatch`] and script / REPL `source` loading (design §9).
4
5use std::cell::RefCell;
6use std::rc::Rc;
7
8use crate::devshell::vfs::{Vfs, VfsError};
9#[cfg(any(unix, feature = "beta-vm"))]
10use crate::devshell::vm::GuestFsOps;
11use crate::devshell::vm::{GuestFsError, SessionHolder};
12
13#[cfg(unix)]
14use crate::devshell::vm::GammaSession;
15#[cfg(any(unix, feature = "beta-vm"))]
16use crate::devshell::workspace::logical_path_to_guest;
17use crate::devshell::workspace::WorkspaceBackendError;
18
19/// Failure reading a logical path from the active workspace (guest or VFS).
20#[derive(Debug)]
21pub enum WorkspaceReadError {
22    Vfs(VfsError),
23    Guest(GuestFsError),
24    PathOutsideWorkspace,
25    Backend(WorkspaceBackendError),
26}
27
28impl std::fmt::Display for WorkspaceReadError {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        match self {
31            Self::Vfs(e) => write!(f, "{e}"),
32            Self::Guest(e) => write!(f, "{e}"),
33            Self::PathOutsideWorkspace => f.write_str("path outside workspace"),
34            Self::Backend(e) => write!(f, "{e}"),
35        }
36    }
37}
38
39impl std::error::Error for WorkspaceReadError {
40    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
41        match self {
42            Self::Vfs(e) => Some(e),
43            Self::Guest(e) => Some(e),
44            Self::Backend(e) => Some(e),
45            Self::PathOutsideWorkspace => None,
46        }
47    }
48}
49
50/// Map a logical path to a guest absolute path (γ guest-primary).
51#[cfg(unix)]
52///
53/// # Errors
54/// Returns [`WorkspaceBackendError`] when logical path mapping fails.
55pub fn logical_to_guest_abs(
56    vfs: &Vfs,
57    g: &GammaSession,
58    logical_path: &str,
59) -> Result<String, WorkspaceBackendError> {
60    logical_path_to_guest(g.guest_mount(), vfs.cwd(), logical_path)
61}
62
63/// Read file bytes for a logical path: guest-primary (γ or β) first, else in-memory [`Vfs`].
64///
65/// # Errors
66/// Returns [`WorkspaceReadError`] when guest/VFS reads fail or the path is outside workspace.
67pub fn read_logical_file_bytes(
68    vfs: &mut Vfs,
69    vm_session: &mut SessionHolder,
70    path: &str,
71) -> Result<Vec<u8>, WorkspaceReadError> {
72    #[cfg(any(unix, feature = "beta-vm"))]
73    {
74        if let Some((ops, mount)) = vm_session.guest_primary_fs_ops_mut() {
75            let gp = match logical_path_to_guest(&mount, vfs.cwd(), path) {
76                Ok(p) => p,
77                Err(WorkspaceBackendError::PathOutsideWorkspace) => {
78                    return Err(WorkspaceReadError::PathOutsideWorkspace);
79                }
80                Err(e) => return Err(WorkspaceReadError::Backend(e)),
81            };
82            return GuestFsOps::read_file(ops, &gp).map_err(WorkspaceReadError::Guest);
83        }
84    }
85    #[cfg(not(any(unix, feature = "beta-vm")))]
86    let _ = vm_session;
87    vfs.read_file(path).map_err(WorkspaceReadError::Vfs)
88}
89
90/// Same as [`read_logical_file_bytes`] with shared [`Rc`] handles (script / REPL).
91///
92/// # Errors
93/// Returns [`WorkspaceReadError`] propagated from [`read_logical_file_bytes`].
94pub fn read_logical_file_bytes_rc(
95    vfs: &Rc<RefCell<Vfs>>,
96    vm_session: &Rc<RefCell<SessionHolder>>,
97    path: &str,
98) -> Result<Vec<u8>, WorkspaceReadError> {
99    read_logical_file_bytes(&mut vfs.borrow_mut(), &mut vm_session.borrow_mut(), path)
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105    use crate::devshell::vfs::Vfs;
106
107    #[test]
108    fn read_host_session_uses_vfs() {
109        let vfs = Rc::new(RefCell::new(Vfs::new()));
110        vfs.borrow_mut().mkdir("/a").unwrap();
111        vfs.borrow_mut().write_file("/a/x", b"hi").unwrap();
112        let vm = Rc::new(RefCell::new(SessionHolder::new_host()));
113        let got = read_logical_file_bytes_rc(&vfs, &vm, "/a/x").unwrap();
114        assert_eq!(got, b"hi");
115    }
116}