shape_runtime/stdlib/
file.rs1use crate::marshal::{register_typed_fn_1, register_typed_fn_2};
21use crate::module_exports::ModuleExports;
22use crate::stdlib::runtime_policy::{FileSystemProvider, RealFileSystem};
23use crate::typed_module_exports::{ConcreteReturn, ConcreteType, TypedReturn};
24use std::path::Path;
25use std::sync::Arc;
26
27pub fn create_file_module_with_provider(fs: Arc<dyn FileSystemProvider>) -> ModuleExports {
31 let mut module = ModuleExports::new("std::core::file");
32 module.description = "High-level filesystem operations".to_string();
33
34 {
36 let fs = Arc::clone(&fs);
37 register_typed_fn_1::<_, Arc<String>>(
38 &mut module,
39 "read_text",
40 "Read the entire contents of a file as a UTF-8 string",
41 "path",
42 "string",
43 ConcreteType::Result(Box::new(ConcreteType::String)),
44 move |path_str, ctx| {
45 crate::module_exports::check_fs_permission(
46 ctx,
47 shape_abi_v1::Permission::FsRead,
48 path_str.as_str(),
49 )?;
50 let bytes = fs
51 .read(Path::new(path_str.as_str()))
52 .map_err(|e| format!("file.read_text() failed: {}", e))?;
53 let text = String::from_utf8(bytes)
54 .map_err(|e| format!("file.read_text() invalid UTF-8: {}", e))?;
55 Ok(TypedReturn::Ok(ConcreteReturn::String(text)))
56 },
57 );
58 }
59
60 {
62 let fs = Arc::clone(&fs);
63 register_typed_fn_2::<_, Arc<String>, Arc<String>>(
64 &mut module,
65 "write_text",
66 "Write a string to a file, creating or truncating it",
67 [("path", "string"), ("content", "string")],
68 ConcreteType::Result(Box::new(ConcreteType::Unit)),
69 move |path_str, content, ctx| {
70 crate::module_exports::check_fs_permission(
71 ctx,
72 shape_abi_v1::Permission::FsWrite,
73 path_str.as_str(),
74 )?;
75 fs.write(Path::new(path_str.as_str()), content.as_bytes())
76 .map_err(|e| format!("file.write_text() failed: {}", e))?;
77 Ok(TypedReturn::Ok(ConcreteReturn::Unit))
78 },
79 );
80 }
81
82 {
84 let fs = Arc::clone(&fs);
85 register_typed_fn_1::<_, Arc<String>>(
86 &mut module,
87 "read_lines",
88 "Read a file and return its lines as an array of strings",
89 "path",
90 "string",
91 ConcreteType::Result(Box::new(ConcreteType::ArrayString)),
92 move |path_str, ctx| {
93 crate::module_exports::check_fs_permission(
94 ctx,
95 shape_abi_v1::Permission::FsRead,
96 path_str.as_str(),
97 )?;
98 let bytes = fs
99 .read(Path::new(path_str.as_str()))
100 .map_err(|e| format!("file.read_lines() failed: {}", e))?;
101 let text = String::from_utf8(bytes)
102 .map_err(|e| format!("file.read_lines() invalid UTF-8: {}", e))?;
103 let lines: Vec<String> = text.lines().map(|l| l.to_string()).collect();
104 Ok(TypedReturn::Ok(ConcreteReturn::ArrayString(lines)))
105 },
106 );
107 }
108
109 {
111 let fs = Arc::clone(&fs);
112 register_typed_fn_2::<_, Arc<String>, Arc<String>>(
113 &mut module,
114 "append",
115 "Append a string to a file, creating it if it does not exist",
116 [("path", "string"), ("content", "string")],
117 ConcreteType::Result(Box::new(ConcreteType::Unit)),
118 move |path_str, content, ctx| {
119 crate::module_exports::check_fs_permission(
120 ctx,
121 shape_abi_v1::Permission::FsWrite,
122 path_str.as_str(),
123 )?;
124 fs.append(Path::new(path_str.as_str()), content.as_bytes())
125 .map_err(|e| format!("file.append() failed: {}", e))?;
126 Ok(TypedReturn::Ok(ConcreteReturn::Unit))
127 },
128 );
129 }
130
131 module
132}
133
134pub fn create_file_module() -> ModuleExports {
136 create_file_module_with_provider(Arc::new(RealFileSystem))
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn test_file_module_creation() {
145 let module = create_file_module();
146 assert_eq!(module.name, "std::core::file");
147 assert!(module.has_export("read_text"));
148 assert!(module.has_export("write_text"));
149 assert!(module.has_export("read_lines"));
150 assert!(module.has_export("append"));
151 }
152
153 #[test]
154 fn test_file_schemas() {
155 let module = create_file_module();
156 let read_schema = module.get_schema("read_text").unwrap();
157 assert_eq!(read_schema.params.len(), 1);
158 assert_eq!(read_schema.return_type.as_deref(), Some("Result<string>"));
159
160 let write_schema = module.get_schema("write_text").unwrap();
161 assert_eq!(write_schema.params.len(), 2);
162 }
163
164 }