@@ -2,6 +2,9 @@ use crate::environment::environment::Environment;
22use crate :: ir:: ast:: { Statement , Expression } ;
33use std:: collections:: HashMap ;
44use std:: sync:: OnceLock ;
5+ use std:: fs:: File ;
6+ use std:: io:: Read ;
7+ use std:: io:: Write ;
58
69pub type MetaBuiltinStmt = fn ( & mut Environment < Expression > ) -> Statement ;
710
@@ -13,6 +16,7 @@ pub fn get_metabuiltins_table() -> &'static HashMap<String, MetaBuiltinStmt> {
1316 let mut table = HashMap :: new ( ) ;
1417 table. insert ( "input" . to_string ( ) , input_builtin as MetaBuiltinStmt ) ;
1518 table. insert ( "print" . to_string ( ) , print_builtin as MetaBuiltinStmt ) ;
19+ table. insert ( "open" . to_string ( ) , open_builtin as MetaBuiltinStmt ) ;
1620 table
1721 } )
1822}
@@ -44,6 +48,88 @@ pub fn print_builtin(env: &mut Environment<Expression>) -> Statement {
4448 Statement :: Return ( Box :: new ( Expression :: CVoid ) )
4549}
4650
51+ pub fn open_builtin ( env : & mut Environment < Expression > ) -> Statement {
52+ let path = match env. lookup ( & "path" . to_string ( ) ) {
53+ Some ( ( _, Expression :: CString ( p) ) ) => p. clone ( ) ,
54+ _ => {
55+ return Statement :: Return ( Box :: new ( Expression :: CString (
56+ "open: first argument must be a string with the file path" . to_string ( ) ) ) ) ;
57+ }
58+ } ;
59+
60+ let mode = match env. lookup ( & "mode" . to_string ( ) ) {
61+ Some ( ( _, Expression :: CString ( m) ) ) => m. clone ( ) ,
62+ _ => "r" . to_string ( ) ,
63+ } ;
64+
65+ match mode. as_str ( ) {
66+ "r" => {
67+ let mut file = match File :: open ( & path) {
68+ Ok ( f) => f,
69+ Err ( e) => {
70+ return Statement :: Return ( Box :: new ( Expression :: CString (
71+ format ! ( "open: could not open '{}' for reading: {}" , path, e) ) ) ) ;
72+ }
73+ } ;
74+ let mut contents = String :: new ( ) ;
75+ if let Err ( e) = file. read_to_string ( & mut contents) {
76+ return Statement :: Return ( Box :: new ( Expression :: CString (
77+ format ! ( "open: error reading '{}': {}" , path, e) ) ) ) ;
78+ }
79+
80+ Statement :: Return ( Box :: new ( Expression :: CString ( contents) ) )
81+ }
82+
83+ "w" => {
84+ let content = match env. lookup ( & "content" . to_string ( ) ) {
85+ Some ( ( _, Expression :: CString ( c) ) ) => c. clone ( ) ,
86+ _ => {
87+ return Statement :: Return ( Box :: new ( Expression :: CString (
88+ "open: when using mode 'w', a third argument with the content to write is required" . to_string ( ) ) ) ) ;
89+ }
90+ } ;
91+
92+ match std:: fs:: write ( & path, content) {
93+ Ok ( _) => Statement :: Return ( Box :: new ( Expression :: CVoid ) ) ,
94+ Err ( e) => Statement :: Return ( Box :: new ( Expression :: CString (
95+ format ! ( "open: could not write to '{}': {}" , path, e) ) ) ) ,
96+ }
97+ }
98+
99+ "a" => {
100+ let content = match env. lookup ( & "content" . to_string ( ) ) {
101+ Some ( ( _, Expression :: CString ( c) ) ) => c. clone ( ) ,
102+ _ => {
103+ return Statement :: Return ( Box :: new ( Expression :: CString (
104+ "open: when using mode 'a', a third argument with the content to append is required" . to_string ( ) ) ) ) ;
105+ }
106+ } ;
107+
108+ match std:: fs:: OpenOptions :: new ( )
109+ . append ( true )
110+ . create ( true )
111+ . open ( & path) {
112+ Ok ( mut file) => {
113+ if let Err ( e) = writeln ! ( file, "{}" , content) {
114+ return Statement :: Return ( Box :: new ( Expression :: CString (
115+ format ! ( "open: could not append to '{}': {}" , path, e) ) ) ) ;
116+ }
117+ Statement :: Return ( Box :: new ( Expression :: CVoid ) )
118+ } ,
119+ Err ( e) => Statement :: Return ( Box :: new ( Expression :: CString (
120+ format ! ( "open: could not open '{}' for appending: {}" , path, e) ) ) ) ,
121+ }
122+
123+ }
124+
125+ m => {
126+ Statement :: Return ( Box :: new ( Expression :: CString (
127+ format ! ( "open: unsupported mode '{}'." , m) ) ) )
128+ }
129+ }
130+
131+ }
132+
47133#[ cfg( test) ]
48134mod tests {
49135 use super :: * ;
@@ -80,7 +166,7 @@ mod tests {
80166 #[ test]
81167 fn test_meta_stmt_table_has_correct_size ( ) {
82168 let table = get_metabuiltins_table ( ) ;
83- assert_eq ! ( table. len( ) , 2 , "The table must contain exactly 2 functions" ) ;
169+ assert_eq ! ( table. len( ) , 3 , "The table must contain exactly 3 functions" ) ;
84170 }
85171
86172 #[ test]
@@ -90,7 +176,8 @@ mod tests {
90176
91177 assert ! ( keys. contains( &&"input" . to_string( ) ) , "The table must contain the key 'input'" ) ;
92178 assert ! ( keys. contains( &&"print" . to_string( ) ) , "The table must contain the key 'print'" ) ;
93- assert_eq ! ( keys. len( ) , 2 , "The table must contain only 2 keys" ) ;
179+ assert ! ( keys. contains( &&"open" . to_string( ) ) , "The table must contain the key 'open'" ) ;
180+ assert_eq ! ( keys. len( ) , 3 , "The table must contain only 3 keys" ) ;
94181 }
95182
96183 #[ test]
@@ -133,4 +220,31 @@ mod tests {
133220 panic ! ( "The 'print' function must be in the table" ) ;
134221 }
135222 }
223+
224+ #[ test]
225+ fn test_open_builtin_read_mode_success ( ) {
226+ use std:: fs;
227+ let test_file_path = "test_read_file.txt" ;
228+ let test_content = "Hello, World!\n This is a test file." ;
229+ fs:: write ( test_file_path, test_content) . unwrap ( ) ;
230+
231+ let mut env: Environment < Expression > = Environment :: new ( ) ;
232+ env. map_variable ( "path" . to_string ( ) , false , Expression :: CString ( test_file_path. to_string ( ) ) ) ;
233+ env. map_variable ( "mode" . to_string ( ) , false , Expression :: CString ( "r" . to_string ( ) ) ) ;
234+
235+ let result = open_builtin ( & mut env) ;
236+
237+ match result {
238+ Statement :: Return ( expr) => {
239+ if let Expression :: CString ( content) = * expr {
240+ assert_eq ! ( content, test_content, "File content should match" ) ;
241+ } else {
242+ panic ! ( "Expected CString with file content" ) ;
243+ }
244+ }
245+ _ => panic ! ( "Expected Statement::Return" ) ,
246+ }
247+
248+ fs:: remove_file ( test_file_path) . unwrap ( ) ;
249+ }
136250}
0 commit comments