refactor: rename read_only to writable
Positive-form field name avoids double-negative confusion.
Mounts default to read-only — users opt in with writable #true.

Assisted-by: Claude Opus 4.6 via pi
change ssnynwrwtptqpzskqoszvvmtlnsuqyxt
commit b458ae0cb21bbf5a3181f7a01edf0c8fecae4d8a
author Alpha Chen <alpha@kejadlen.dev>
date
parent spnmrylz
diff --git a/src/config.rs b/src/config.rs
index 596e795..09d7090 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -13,7 +13,7 @@ pub struct Mount {
     pub source: String,
     pub target: Option<String>,
     #[serde(default)]
-    pub read_only: bool,
+    pub writable: bool,
 }
 
 /// A mount with tilde-expanded paths ready for Docker.
@@ -21,7 +21,7 @@ pub struct Mount {
 pub struct ResolvedMount {
     pub source: PathBuf,
     pub target: String,
-    pub read_only: bool,
+    pub writable: bool,
 }
 
 impl Default for Config {
@@ -58,17 +58,17 @@ impl Config {
                 Mount {
                     source: "~/.config/git".into(),
                     target: None,
-                    read_only: true,
+                    writable: false,
                 },
                 Mount {
                     source: "~/.config/jj".into(),
                     target: None,
-                    read_only: true,
+                    writable: false,
                 },
                 Mount {
                     source: "~/.local/share/ranger".into(),
                     target: None,
-                    read_only: false,
+                    writable: true,
                 },
             ],
         }
@@ -100,7 +100,7 @@ impl Mount {
         Some(ResolvedMount {
             source: expanded,
             target,
-            read_only: self.read_only,
+            writable: self.writable,
         })
     }
 }
@@ -108,19 +108,19 @@ impl Mount {
 impl ResolvedMount {
     /// Format as a Docker volume mount string (`source:target` or `source:target:ro`).
     pub fn to_volume_string(&self) -> String {
-        if self.read_only {
-            format!("{}:{}:ro", self.source.display(), self.target)
-        } else {
+        if self.writable {
             format!("{}:{}", self.source.display(), self.target)
+        } else {
+            format!("{}:{}:ro", self.source.display(), self.target)
         }
     }
 
     /// Label for display in `config` output (target, with ` (ro)` suffix when read-only).
     pub fn display_target(&self) -> String {
-        if self.read_only {
-            format!("{} (ro)", self.target)
-        } else {
+        if self.writable {
             self.target.clone()
+        } else {
+            format!("{} (ro)", self.target)
         }
     }
 }
@@ -151,15 +151,13 @@ mod tests {
         let kdl_content = r#"
             mounts{
             source "~/.config/git"
-            read_only #true
             }
             mounts{
             source "~/.config/jj"  
-            read_only #true
             }
             mounts{
             source "~/.local/share/ranger"
-            read_only #false
+            writable #true
             }
             mounts{
             source "~/Downloads"
@@ -170,8 +168,11 @@ mod tests {
         let parsed: Config = serde_kdl2::from_str(kdl_content).unwrap();
         assert_eq!(parsed.mounts.len(), 4);
         assert_eq!(parsed.mounts[0].source, "~/.config/git");
-        assert!(parsed.mounts[0].read_only);
+        assert!(!parsed.mounts[0].writable);
+        assert!(parsed.mounts[2].writable);
         assert_eq!(parsed.mounts[3].target, Some("/root/downloads".into()));
+        // writable defaults to false when omitted.
+        assert!(!parsed.mounts[3].writable);
     }
 
     #[test]
@@ -201,7 +202,7 @@ mod tests {
         let mount = Mount {
             source: "/nonexistent/path/that/does/not/exist".into(),
             target: None,
-            read_only: false,
+            writable: false,
         };
         assert!(mount.resolve().is_none());
     }
@@ -211,12 +212,12 @@ mod tests {
         let mount = Mount {
             source: "/tmp".into(),
             target: Some("/container/tmp".into()),
-            read_only: true,
+            writable: false,
         };
         let resolved = mount.resolve().unwrap();
         assert_eq!(resolved.source, PathBuf::from("/tmp"));
         assert_eq!(resolved.target, "/container/tmp");
-        assert!(resolved.read_only);
+        assert!(!resolved.writable);
     }
 
     #[test]
@@ -224,7 +225,7 @@ mod tests {
         let mount = Mount {
             source: "/tmp".into(),
             target: None,
-            read_only: false,
+            writable: true,
         };
         let resolved = mount.resolve().unwrap();
         assert_eq!(resolved.target, "/tmp");
@@ -235,7 +236,7 @@ mod tests {
         let m = ResolvedMount {
             source: PathBuf::from("/home/user/.config/git"),
             target: "/root/.config/git".into(),
-            read_only: true,
+            writable: false,
         };
         assert_eq!(
             m.to_volume_string(),
@@ -248,7 +249,7 @@ mod tests {
         let m = ResolvedMount {
             source: PathBuf::from("/home/user/.local/share/ranger"),
             target: "/root/.local/share/ranger".into(),
-            read_only: false,
+            writable: true,
         };
         assert_eq!(
             m.to_volume_string(),
@@ -261,7 +262,7 @@ mod tests {
         let m = ResolvedMount {
             source: PathBuf::from("/x"),
             target: "/root/.config/git".into(),
-            read_only: true,
+            writable: false,
         };
         assert_eq!(m.display_target(), "/root/.config/git (ro)");
     }
@@ -271,7 +272,7 @@ mod tests {
         let m = ResolvedMount {
             source: PathBuf::from("/x"),
             target: "/root/.local/share/ranger".into(),
-            read_only: false,
+            writable: true,
         };
         assert_eq!(m.display_target(), "/root/.local/share/ranger");
     }
@@ -283,12 +284,12 @@ mod tests {
                 Mount {
                     source: "/tmp".into(),
                     target: Some("/container/tmp".into()),
-                    read_only: false,
+                    writable: true,
                 },
                 Mount {
                     source: "/nonexistent".into(),
                     target: None,
-                    read_only: false,
+                    writable: false,
                 },
             ],
         };
diff --git a/src/main.rs b/src/main.rs
index 8cf529e..ec693e1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -134,7 +134,7 @@ impl Ramekin {
             .map(|(source, target)| config::ResolvedMount {
                 source: source.clone(),
                 target: target.into(),
-                read_only: false,
+                writable: true,
             })
             .collect();