Skip to main content

monty_proto/convert/
os_call.rs

1//! Conversions for `OsCall` suspensions: the typed wire arms of
2//! `monty.v1.OsCall` and [`OsFunctionCall`] map 1:1, so payloads (write data,
3//! paths) *move* between the wire and the call — never clone.
4
5use monty_types::{
6    GetenvArgs, MkdirCallArgs, MontyPath, MontyTimeZone, OpenCallArgs, OsFunctionCall, PathBytesDataArgs,
7    PathStringDataArgs, RenameCallArgs,
8};
9
10use crate::{
11    convert::ProtoConvertError,
12    pb::{TimeZone, Unit, os_call},
13};
14
15impl From<OsFunctionCall> for os_call::Call {
16    fn from(call: OsFunctionCall) -> Self {
17        match call {
18            OsFunctionCall::Exists(p) => Self::Exists(p.into_string()),
19            OsFunctionCall::IsFile(p) => Self::IsFile(p.into_string()),
20            OsFunctionCall::IsDir(p) => Self::IsDir(p.into_string()),
21            OsFunctionCall::IsSymlink(p) => Self::IsSymlink(p.into_string()),
22            OsFunctionCall::ReadText(p) => Self::ReadText(p.into_string()),
23            OsFunctionCall::ReadBytes(p) => Self::ReadBytes(p.into_string()),
24            OsFunctionCall::Stat(p) => Self::Stat(p.into_string()),
25            OsFunctionCall::Iterdir(p) => Self::Iterdir(p.into_string()),
26            OsFunctionCall::Resolve(p) => Self::Resolve(p.into_string()),
27            OsFunctionCall::Absolute(p) => Self::Absolute(p.into_string()),
28            OsFunctionCall::Unlink(p) => Self::Unlink(p.into_string()),
29            OsFunctionCall::Rmdir(p) => Self::Rmdir(p.into_string()),
30            OsFunctionCall::WriteText(a) => Self::WriteText(text_write(a)),
31            OsFunctionCall::AppendText(a) => Self::AppendText(text_write(a)),
32            OsFunctionCall::WriteBytes(a) => Self::WriteBytes(bytes_write(a)),
33            OsFunctionCall::AppendBytes(a) => Self::AppendBytes(bytes_write(a)),
34            OsFunctionCall::Open(a) => Self::Open(os_call::Open {
35                path: a.path.into_string(),
36                mode: a.mode.as_str().to_owned(),
37            }),
38            OsFunctionCall::Mkdir(a) => Self::Mkdir(os_call::Mkdir {
39                path: a.path.into_string(),
40                parents: a.parents,
41                exist_ok: a.exist_ok,
42            }),
43            OsFunctionCall::Rename(a) => Self::Rename(os_call::Rename {
44                src: a.src.into_string(),
45                dst: a.dst.into_string(),
46            }),
47            OsFunctionCall::Getenv(a) => Self::Getenv(os_call::Getenv {
48                key: a.key,
49                default: Some(a.default.into()),
50            }),
51            OsFunctionCall::GetEnviron => Self::GetEnviron(Unit {}),
52            OsFunctionCall::DateToday => Self::DateToday(Unit {}),
53            OsFunctionCall::DateTimeNow(tz) => Self::DateTimeNow(os_call::DateTimeNow {
54                tz: tz.map(|tz| TimeZone {
55                    offset_seconds: tz.offset_seconds,
56                    name: tz.name,
57                }),
58            }),
59        }
60    }
61}
62
63impl TryFrom<os_call::Call> for OsFunctionCall {
64    type Error = ProtoConvertError;
65
66    fn try_from(call: os_call::Call) -> Result<Self, ProtoConvertError> {
67        Ok(match call {
68            os_call::Call::Exists(p) => Self::Exists(MontyPath::new(p)),
69            os_call::Call::IsFile(p) => Self::IsFile(MontyPath::new(p)),
70            os_call::Call::IsDir(p) => Self::IsDir(MontyPath::new(p)),
71            os_call::Call::IsSymlink(p) => Self::IsSymlink(MontyPath::new(p)),
72            os_call::Call::ReadText(p) => Self::ReadText(MontyPath::new(p)),
73            os_call::Call::ReadBytes(p) => Self::ReadBytes(MontyPath::new(p)),
74            os_call::Call::Stat(p) => Self::Stat(MontyPath::new(p)),
75            os_call::Call::Iterdir(p) => Self::Iterdir(MontyPath::new(p)),
76            os_call::Call::Resolve(p) => Self::Resolve(MontyPath::new(p)),
77            os_call::Call::Absolute(p) => Self::Absolute(MontyPath::new(p)),
78            os_call::Call::Unlink(p) => Self::Unlink(MontyPath::new(p)),
79            os_call::Call::Rmdir(p) => Self::Rmdir(MontyPath::new(p)),
80            os_call::Call::WriteText(a) => Self::WriteText(text_args(a)),
81            os_call::Call::AppendText(a) => Self::AppendText(text_args(a)),
82            os_call::Call::WriteBytes(a) => Self::WriteBytes(bytes_args(a)),
83            os_call::Call::AppendBytes(a) => Self::AppendBytes(bytes_args(a)),
84            os_call::Call::Open(o) => Self::Open(OpenCallArgs {
85                mode: o.mode.parse().map_err(|_| ProtoConvertError::InvalidFileMode(o.mode))?,
86                path: MontyPath::new(o.path),
87            }),
88            os_call::Call::Mkdir(m) => Self::Mkdir(MkdirCallArgs {
89                path: MontyPath::new(m.path),
90                parents: m.parents,
91                exist_ok: m.exist_ok,
92            }),
93            os_call::Call::Rename(r) => Self::Rename(RenameCallArgs {
94                src: MontyPath::new(r.src),
95                dst: MontyPath::new(r.dst),
96            }),
97            os_call::Call::Getenv(g) => Self::Getenv(GetenvArgs {
98                key: g.key,
99                default: g
100                    .default
101                    .ok_or(ProtoConvertError::MissingField("Getenv.default"))?
102                    .into_object()?,
103            }),
104            os_call::Call::GetEnviron(_) => Self::GetEnviron,
105            os_call::Call::DateToday(_) => Self::DateToday,
106            // typed arm: the wire cannot express anything but an optional
107            // timezone here, mirroring the VM's validation of `datetime.now`
108            os_call::Call::DateTimeNow(now) => Self::DateTimeNow(now.tz.map(|tz| MontyTimeZone {
109                offset_seconds: tz.offset_seconds,
110                name: tz.name,
111            })),
112        })
113    }
114}
115
116/// `PathStringDataArgs` → wire `TextWrite`, moving the text payload.
117fn text_write(args: PathStringDataArgs) -> os_call::TextWrite {
118    os_call::TextWrite {
119        path: args.path.into_string(),
120        data: args.data,
121    }
122}
123
124/// `PathBytesDataArgs` → wire `BytesWrite`, moving the bytes payload.
125fn bytes_write(args: PathBytesDataArgs) -> os_call::BytesWrite {
126    os_call::BytesWrite {
127        path: args.path.into_string(),
128        data: args.data,
129    }
130}
131
132/// Wire `TextWrite` → `PathStringDataArgs`, moving the text payload.
133fn text_args(wire: os_call::TextWrite) -> PathStringDataArgs {
134    PathStringDataArgs {
135        path: MontyPath::new(wire.path),
136        data: wire.data,
137    }
138}
139
140/// Wire `BytesWrite` → `PathBytesDataArgs`, moving the bytes payload.
141fn bytes_args(wire: os_call::BytesWrite) -> PathBytesDataArgs {
142    PathBytesDataArgs {
143        path: MontyPath::new(wire.path),
144        data: wire.data,
145    }
146}