Skip to content

Commit fc69249

Browse files
jamesadevineCopilot
andcommitted
refactor(compile): make AwfMount.mode non-optional
Always store an explicit AwfMountMode instead of Option<AwfMountMode>. Parsing 'host:container' without a mode suffix now defaults to ReadOnly (secure default). Display/Serialize always emit the mode suffix so generated AWF flags are self-documenting. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 1e85efc commit fc69249

3 files changed

Lines changed: 17 additions & 21 deletions

File tree

src/compile/extensions/mod.rs

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -365,18 +365,18 @@ pub struct AwfMount {
365365
pub host_path: String,
366366
/// Corresponding path inside the container.
367367
pub container_path: String,
368-
/// Optional mount access mode. When absent the Docker default applies
369-
/// (read-write).
370-
pub mode: Option<AwfMountMode>,
368+
/// Mount access mode. Defaults to [`AwfMountMode::ReadOnly`] when not
369+
/// specified in the input — the secure default for AWF chroot mounts.
370+
pub mode: AwfMountMode,
371371
}
372372

373373
impl AwfMount {
374374
/// Creates an `AwfMount` with the given host path, container path, and
375-
/// optional access mode.
375+
/// access mode.
376376
pub fn new(
377377
host_path: impl Into<String>,
378378
container_path: impl Into<String>,
379-
mode: Option<AwfMountMode>,
379+
mode: AwfMountMode,
380380
) -> Self {
381381
Self {
382382
host_path: host_path.into(),
@@ -388,11 +388,7 @@ impl AwfMount {
388388

389389
impl fmt::Display for AwfMount {
390390
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
391-
if let Some(mode) = &self.mode {
392-
write!(f, "{}:{}:{}", self.host_path, self.container_path, mode)
393-
} else {
394-
write!(f, "{}:{}", self.host_path, self.container_path)
395-
}
391+
write!(f, "{}:{}:{}", self.host_path, self.container_path, self.mode)
396392
}
397393
}
398394

@@ -405,12 +401,12 @@ impl FromStr for AwfMount {
405401
[host, container] => Ok(Self {
406402
host_path: (*host).to_string(),
407403
container_path: (*container).to_string(),
408-
mode: None,
404+
mode: AwfMountMode::ReadOnly,
409405
}),
410406
[host, container, mode_str] => Ok(Self {
411407
host_path: (*host).to_string(),
412408
container_path: (*container).to_string(),
413-
mode: Some(mode_str.parse()?),
409+
mode: mode_str.parse()?,
414410
}),
415411
_ => anyhow::bail!(
416412
"Invalid AWF mount spec '{}': expected 'host:container[:mode]'",

src/compile/extensions/tests.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,36 +29,36 @@ fn test_awf_mount_mode_parse() {
2929

3030
#[test]
3131
fn test_awf_mount_display_with_mode() {
32-
let m = AwfMount::new("$HOME/.elan", "$HOME/.elan", Some(AwfMountMode::ReadOnly));
32+
let m = AwfMount::new("$HOME/.elan", "$HOME/.elan", AwfMountMode::ReadOnly);
3333
assert_eq!(m.to_string(), "$HOME/.elan:$HOME/.elan:ro");
3434
}
3535

3636
#[test]
3737
fn test_awf_mount_display_no_mode() {
38-
let m = AwfMount::new("/tmp/foo", "/tmp/foo", None);
39-
assert_eq!(m.to_string(), "/tmp/foo:/tmp/foo");
38+
let m = AwfMount::new("/tmp/foo", "/tmp/foo", AwfMountMode::ReadOnly);
39+
assert_eq!(m.to_string(), "/tmp/foo:/tmp/foo:ro");
4040
}
4141

4242
#[test]
4343
fn test_awf_mount_parse_with_mode() {
4444
let m: AwfMount = "$HOME/.elan:$HOME/.elan:ro".parse().unwrap();
4545
assert_eq!(m.host_path, "$HOME/.elan");
4646
assert_eq!(m.container_path, "$HOME/.elan");
47-
assert_eq!(m.mode, Some(AwfMountMode::ReadOnly));
47+
assert_eq!(m.mode, AwfMountMode::ReadOnly);
4848
}
4949

5050
#[test]
5151
fn test_awf_mount_parse_rw_mode() {
5252
let m: AwfMount = "/tmp/work:/tmp/work:rw".parse().unwrap();
53-
assert_eq!(m.mode, Some(AwfMountMode::ReadWrite));
53+
assert_eq!(m.mode, AwfMountMode::ReadWrite);
5454
}
5555

5656
#[test]
5757
fn test_awf_mount_parse_no_mode() {
5858
let m: AwfMount = "/tmp/foo:/tmp/foo".parse().unwrap();
5959
assert_eq!(m.host_path, "/tmp/foo");
6060
assert_eq!(m.container_path, "/tmp/foo");
61-
assert!(m.mode.is_none());
61+
assert_eq!(m.mode, AwfMountMode::ReadOnly);
6262
}
6363

6464
#[test]
@@ -69,7 +69,7 @@ fn test_awf_mount_parse_invalid_mode_errors() {
6969

7070
#[test]
7171
fn test_awf_mount_serde_roundtrip() {
72-
let m = AwfMount::new("$HOME/.elan", "$HOME/.elan", Some(AwfMountMode::ReadOnly));
72+
let m = AwfMount::new("$HOME/.elan", "$HOME/.elan", AwfMountMode::ReadOnly);
7373
let json = serde_json::to_string(&m).unwrap();
7474
assert_eq!(json, r#""$HOME/.elan:$HOME/.elan:ro""#);
7575
let parsed: AwfMount = serde_json::from_str(&json).unwrap();
@@ -212,7 +212,7 @@ fn test_lean_required_awf_mounts() {
212212
assert_eq!(mounts.len(), 1);
213213
assert_eq!(mounts[0].host_path, "$HOME/.elan");
214214
assert_eq!(mounts[0].container_path, "$HOME/.elan");
215-
assert_eq!(mounts[0].mode, Some(AwfMountMode::ReadOnly));
215+
assert_eq!(mounts[0].mode, AwfMountMode::ReadOnly);
216216
// Round-trips to Docker format string
217217
assert_eq!(mounts[0].to_string(), "$HOME/.elan:$HOME/.elan:ro");
218218
}

src/runtimes/lean/extension.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ the toolchain. Lean files use the `.lean` extension.\n"
5757
}
5858

5959
fn required_awf_mounts(&self) -> Vec<AwfMount> {
60-
vec![AwfMount::new("$HOME/.elan", "$HOME/.elan", Some(AwfMountMode::ReadOnly))]
60+
vec![AwfMount::new("$HOME/.elan", "$HOME/.elan", AwfMountMode::ReadOnly)]
6161
}
6262

6363
fn validate(&self, ctx: &CompileContext) -> Result<Vec<String>> {

0 commit comments

Comments
 (0)