File operations
* read * write * append * exists? * tests for each
This commit is contained in:
parent
25db2aac47
commit
64be70b3af
5 changed files with 261 additions and 6 deletions
172
src/stl/file.rs
Normal file
172
src/stl/file.rs
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
/* relish: versatile lisp shell
|
||||
* Copyright (C) 2021 Aidan Hahn
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use crate::segment::{Ctr, Seg, Type};
|
||||
use crate::sym::{SymTable, Symbol, ValueType, Args};
|
||||
use crate::error::{Traceback, start_trace};
|
||||
use std::io::Write;
|
||||
use std::fs::{File, read_to_string, OpenOptions};
|
||||
use std::rc::Rc;
|
||||
use std::path::Path;
|
||||
|
||||
|
||||
const READ_TO_STRING_DOCSTRING: &str = "Takes one input (filename).
|
||||
If file exists, returns a string containing file contents.
|
||||
If the file does not exist returns error.";
|
||||
fn read_to_string_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
|
||||
if let Ctr::String(ref filename) = *ast.car {
|
||||
let res = read_to_string(filename);
|
||||
if let Ok(s) = res {
|
||||
Ok(Ctr::String(s))
|
||||
} else {
|
||||
Err(start_trace(
|
||||
("read-file", res.err().unwrap().to_string())
|
||||
.into()))
|
||||
}
|
||||
} else {
|
||||
Err(start_trace(("read-file", "impossible arg").into()))
|
||||
}
|
||||
}
|
||||
|
||||
const WRITE_TO_FILE_DOCSTRING: &str = "Takes two inputs: a filename and a string of content.
|
||||
Writes contents to the file and returns None.";
|
||||
fn write_to_file_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
|
||||
if let Ctr::String(ref filename) = *ast.car {
|
||||
if let Ctr::Seg(ref next) = *ast.cdr {
|
||||
if let Ctr::String(ref body) = *next.car {
|
||||
let fres = OpenOptions::new()
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.write(true)
|
||||
.open(filename);
|
||||
if fres.is_err() {
|
||||
Err(start_trace(
|
||||
("write-file",
|
||||
format!("couldn't open file: {}", fres.err().unwrap().to_string()))
|
||||
.into()))
|
||||
} else {
|
||||
if let Err(e) = write!(&mut fres.unwrap(), "{}", body) {
|
||||
Err(start_trace(
|
||||
("write-file", format!("failed to write to file: {}", e))
|
||||
.into()))
|
||||
} else {
|
||||
Ok(Ctr::None)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(start_trace(("write-file", "impossible arg").into()))
|
||||
}
|
||||
} else {
|
||||
Err(start_trace(("write-file", "not enough args").into()))
|
||||
}
|
||||
} else {
|
||||
Err(start_trace(("write-file", "impossible arg").into()))
|
||||
}
|
||||
}
|
||||
|
||||
const APPEND_TO_FILE_DOCSTRING: &str = "Takes two inputs: a filename and a string of content.
|
||||
Appends content to the end of the file and returns None";
|
||||
fn append_to_file_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
|
||||
if let Ctr::String(ref filename) = *ast.car {
|
||||
if let Ctr::Seg(ref next) = *ast.cdr {
|
||||
if let Ctr::String(ref body) = *next.car {
|
||||
let fres = File::options().append(true).open(filename);
|
||||
if fres.is_err() {
|
||||
Err(start_trace(
|
||||
("append-file",
|
||||
format!("couldn't open file: {}", fres.err().unwrap().to_string()))
|
||||
.into()))
|
||||
} else {
|
||||
if let Err(e) = write!(&mut fres.unwrap(), "{}", body) {
|
||||
Err(start_trace(
|
||||
("append-file", format!("failed to write to file: {}", e))
|
||||
.into()))
|
||||
} else {
|
||||
Ok(Ctr::None)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(start_trace(("append-file", "impossible arg").into()))
|
||||
}
|
||||
} else {
|
||||
Err(start_trace(("append-file", "not enough args").into()))
|
||||
}
|
||||
} else {
|
||||
Err(start_trace(("append-file", "impossible arg").into()))
|
||||
}
|
||||
}
|
||||
|
||||
const IS_FILE_EXISTS_DOCSTRING: &str = "Takes one input: a filename.
|
||||
Returns true or false depending on if the file exists.";
|
||||
fn is_file_exists_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
|
||||
if let Ctr::String(ref filename) = *ast.car {
|
||||
Ok(Ctr::Bool(Path::new(&filename).exists()))
|
||||
} else {
|
||||
Err(Traceback::new().with_trace(("exists?", "impossible arg").into()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_file_lib(syms: &mut SymTable) {
|
||||
syms.insert(
|
||||
"read-file".to_string(),
|
||||
Symbol {
|
||||
name: String::from("read-file"),
|
||||
args: Args::Strict(vec![Type::String]),
|
||||
conditional_branches: false,
|
||||
docs: READ_TO_STRING_DOCSTRING.to_string(),
|
||||
value: ValueType::Internal(Rc::new(read_to_string_callback)),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
syms.insert(
|
||||
"write-file".to_string(),
|
||||
Symbol {
|
||||
name: String::from("write-file"),
|
||||
args: Args::Strict(vec![Type::String, Type::String]),
|
||||
conditional_branches: false,
|
||||
docs: WRITE_TO_FILE_DOCSTRING.to_string(),
|
||||
value: ValueType::Internal(Rc::new(write_to_file_callback)),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
syms.insert(
|
||||
"append-file".to_string(),
|
||||
Symbol {
|
||||
name: String::from("append-file"),
|
||||
args: Args::Strict(vec![Type::String, Type::String]),
|
||||
conditional_branches: false,
|
||||
docs: APPEND_TO_FILE_DOCSTRING.to_string(),
|
||||
value: ValueType::Internal(Rc::new(append_to_file_callback)),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
syms.insert(
|
||||
"exists?".to_string(),
|
||||
Symbol {
|
||||
name: String::from("exists?"),
|
||||
args: Args::Strict(vec![Type::String]),
|
||||
conditional_branches: false,
|
||||
docs: IS_FILE_EXISTS_DOCSTRING.to_string(),
|
||||
value: ValueType::Internal(Rc::new(is_file_exists_callback)),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue