Move File module and tests to the shell project

Signed-off-by: Ava Affine <ava@sunnypup.io>
This commit is contained in:
Ava Apples Affine 2024-07-12 21:07:04 -07:00
parent 6d2925984f
commit 74f73fb493
4 changed files with 87 additions and 82 deletions

252
shell/src/file.rs Normal file
View file

@ -0,0 +1,252 @@
/* Flesh: Flexible Shell
* Copyright (C) 2021 Ava 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 flesh::ast::{
start_trace, Ctr, Seg, Type, SymTable,
Symbol, ValueType, Args, Traceback,
};
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()
}
);
}
#[cfg(test)]
mod tests {
use flesh::ast::{eval, lex, SymTable};
use flesh::stdlib::static_stdlib;
use crate::stl::static_stdlib_overwrites;
#[test]
fn test_fexists() {
let document = "(exists? \"/tmp\")";
let result = "true";
let mut syms = SymTable::new();
static_stdlib(&mut syms);
static_stdlib_overwrites(&mut syms);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_fexists_doesnt() {
let document = "(exists? \"cargo.timtam\")";
let result = "false";
let mut syms = SymTable::new();
static_stdlib(&mut syms);
static_stdlib_overwrites(&mut syms);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_write_file() {
let document = "
(let ((s \"test\")
(t \"/tmp/flesh-lib-test-file-1\"))
(write-file t s)
(echo (read-file t))
(eq? (read-file t) s))";
let result = "true";
let mut syms = SymTable::new();
static_stdlib(&mut syms);
static_stdlib_overwrites(&mut syms);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
#[test]
fn test_append_file() {
let document = "
(let ((s \"test\")
(t \"/tmp/flesh-lib-test-file-2\"))
(write-file t s)
(append-file t s)
(eq? (read-file t)
(concat s s)))";
let result = "true";
let mut syms = SymTable::new();
static_stdlib(&mut syms);
static_stdlib_overwrites(&mut syms);
assert_eq!(
*eval(&lex(&document.to_string()).unwrap(), &mut syms)
.unwrap()
.to_string(),
result.to_string(),
);
}
}