Skip to main content

strict_path/path/virtual_path/
fs.rs

1//! Filesystem I/O methods for `VirtualPath`.
2//!
3//! All methods delegate to the inner `StrictPath`, which holds the real system path. The
4//! virtual path component is not used for I/O — it exists only for user-visible display.
5use super::VirtualPath;
6
7impl<Marker> VirtualPath<Marker> {
8    /// Write bytes to the underlying system path. Accepts `&str`, `String`, `&[u8]`, `Vec<u8]`, etc.
9    #[inline]
10    pub fn write<C: AsRef<[u8]>>(&self, contents: C) -> std::io::Result<()> {
11        self.inner.write(contents)
12    }
13
14    /// Append bytes to the underlying system path (create if missing). Accepts `&str`, `&[u8]`, etc.
15    ///
16    /// # Errors
17    ///
18    /// - `std::io::Error`: Propagates OS errors when the file cannot be opened or written.
19    ///
20    /// # Examples
21    ///
22    /// ```rust
23    /// # use strict_path::VirtualRoot;
24    /// # let root = std::env::temp_dir().join("strict-path-vpath-append");
25    /// # std::fs::create_dir_all(&root)?;
26    /// # let vroot: VirtualRoot = VirtualRoot::try_new(&root)?;
27    /// // Untrusted input from request/CLI/config/etc.
28    /// let log_file = "logs/activity.log";
29    /// let vpath = vroot.virtual_join(log_file)?;
30    /// vpath.create_parent_dir_all()?;
31    /// vpath.append("[2025-01-01] Operation A\n")?;
32    /// vpath.append("[2025-01-01] Operation B\n")?;
33    /// let contents = vpath.read_to_string()?;
34    /// assert!(contents.contains("Operation A"));
35    /// assert!(contents.contains("Operation B"));
36    /// # std::fs::remove_dir_all(&root)?;
37    /// # Ok::<_, Box<dyn std::error::Error>>(())
38    /// ```
39    #[inline]
40    pub fn append<C: AsRef<[u8]>>(&self, data: C) -> std::io::Result<()> {
41        self.inner.append(data)
42    }
43
44    /// Create or truncate the file at this virtual path and return a writable handle.
45    ///
46    /// # Errors
47    ///
48    /// - `std::io::Error`: Propagates operating-system errors when the parent directory is missing or file creation fails.
49    ///
50    /// # Examples
51    ///
52    /// ```rust
53    /// # use strict_path::VirtualRoot;
54    /// # use std::io::Write;
55    /// # let root = std::env::temp_dir().join("strict-path-virtual-create-file");
56    /// # std::fs::create_dir_all(&root)?;
57    /// # let vroot: VirtualRoot = VirtualRoot::try_new(&root)?;
58    /// let report = vroot.virtual_join("reports/summary.txt")?;
59    /// report.create_parent_dir_all()?;
60    /// let mut file = report.create_file()?;
61    /// file.write_all(b"summary")?;
62    /// # std::fs::remove_dir_all(&root)?;
63    /// # Ok::<_, Box<dyn std::error::Error>>(())
64    /// ```
65    #[inline]
66    pub fn create_file(&self) -> std::io::Result<std::fs::File> {
67        self.inner.create_file()
68    }
69
70    /// Open the file at this virtual path in read-only mode.
71    ///
72    /// # Errors
73    ///
74    /// - `std::io::Error`: Propagates operating-system errors when the file is missing or inaccessible.
75    ///
76    /// # Examples
77    ///
78    /// ```rust
79    /// # use strict_path::VirtualRoot;
80    /// # use std::io::{Read, Write};
81    /// # let root = std::env::temp_dir().join("strict-path-virtual-open-file");
82    /// # std::fs::create_dir_all(&root)?;
83    /// # let vroot: VirtualRoot = VirtualRoot::try_new(&root)?;
84    /// let report = vroot.virtual_join("reports/summary.txt")?;
85    /// report.create_parent_dir_all()?;
86    /// report.write("summary")?;
87    /// let mut file = report.open_file()?;
88    /// let mut contents = String::new();
89    /// file.read_to_string(&mut contents)?;
90    /// assert_eq!(contents, "summary");
91    /// # std::fs::remove_dir_all(&root)?;
92    /// # Ok::<_, Box<dyn std::error::Error>>(())
93    /// ```
94    #[inline]
95    pub fn open_file(&self) -> std::io::Result<std::fs::File> {
96        self.inner.open_file()
97    }
98
99    /// Return an options builder for advanced file opening (read+write, append, exclusive create, etc.).
100    ///
101    /// # Examples
102    ///
103    /// ```rust
104    /// # use strict_path::VirtualRoot;
105    /// # use std::io::{Read, Write, Seek, SeekFrom};
106    /// # let root = std::env::temp_dir().join("vpath-open-with-example");
107    /// # std::fs::create_dir_all(&root)?;
108    /// # let vroot: VirtualRoot = VirtualRoot::try_new(&root)?;
109    /// // Untrusted input from request/CLI/config/etc.
110    /// let data_file = "cache/state.bin";
111    /// let cache_path = vroot.virtual_join(data_file)?;
112    /// cache_path.create_parent_dir_all()?;
113    ///
114    /// // Open with read+write access, create if missing
115    /// let mut file = cache_path.open_with()
116    ///     .read(true)
117    ///     .write(true)
118    ///     .create(true)
119    ///     .open()?;
120    /// file.write_all(b"state")?;
121    /// file.seek(SeekFrom::Start(0))?;
122    /// let mut buf = [0u8; 5];
123    /// file.read_exact(&mut buf)?;
124    /// assert_eq!(&buf, b"state");
125    /// # std::fs::remove_dir_all(&root)?;
126    /// # Ok::<_, Box<dyn std::error::Error>>(())
127    /// ```
128    #[must_use = "open_with() returns a builder — chain .read(), .write(), .create(), .open() to use it"]
129    #[inline]
130    pub fn open_with(&self) -> crate::path::strict_path::StrictOpenOptions<'_, Marker> {
131        self.inner.open_with()
132    }
133
134    /// Create all directories in the underlying system path if missing.
135    #[inline]
136    pub fn create_dir_all(&self) -> std::io::Result<()> {
137        self.inner.create_dir_all()
138    }
139
140    /// Create the directory at this virtual location (non‑recursive). Fails if parent missing.
141    #[inline]
142    pub fn create_dir(&self) -> std::io::Result<()> {
143        self.inner.create_dir()
144    }
145
146    /// Create only the immediate parent of this virtual path (non‑recursive). `Ok(())` at virtual root.
147    #[inline]
148    pub fn create_parent_dir(&self) -> std::io::Result<()> {
149        match self.virtualpath_parent() {
150            Ok(Some(parent)) => parent.create_dir(),
151            Ok(None) => Ok(()),
152            Err(crate::StrictPathError::PathEscapesBoundary { .. }) => Ok(()),
153            Err(e) => Err(std::io::Error::other(e)),
154        }
155    }
156
157    /// Recursively create all missing directories up to the immediate parent. `Ok(())` at virtual root.
158    #[inline]
159    pub fn create_parent_dir_all(&self) -> std::io::Result<()> {
160        match self.virtualpath_parent() {
161            Ok(Some(parent)) => parent.create_dir_all(),
162            Ok(None) => Ok(()),
163            Err(crate::StrictPathError::PathEscapesBoundary { .. }) => Ok(()),
164            Err(e) => Err(std::io::Error::other(e)),
165        }
166    }
167
168    /// Remove the file at the underlying system path.
169    #[inline]
170    pub fn remove_file(&self) -> std::io::Result<()> {
171        self.inner.remove_file()
172    }
173
174    /// Remove the directory at the underlying system path.
175    #[inline]
176    pub fn remove_dir(&self) -> std::io::Result<()> {
177        self.inner.remove_dir()
178    }
179
180    /// Recursively remove the directory and its contents at the underlying system path.
181    #[inline]
182    pub fn remove_dir_all(&self) -> std::io::Result<()> {
183        self.inner.remove_dir_all()
184    }
185}