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}