Files
orchid/src/libs/directfs/commands.rs
Lawrence Bethlenfalvy c279301583 Massive feature drop
- pattern matching seems to be correct
- dynamic dispatch works with the to_string example
- template strings as a last-minute addition
- interpreter revamp, virtual stack for abort safety
2024-01-29 18:26:56 +00:00

192 lines
6.3 KiB
Rust

use std::ffi::OsString;
use std::fs::File;
use std::path::{Path, PathBuf};
use super::osstring::os_string_lib;
use crate::facade::system::{IntoSystem, System};
use crate::foreign::atom::Atomic;
use crate::foreign::cps_box::CPSBox;
use crate::foreign::error::ExternResult;
use crate::foreign::inert::{Inert, InertPayload};
use crate::foreign::process::Unstable;
use crate::foreign::to_clause::ToClause;
use crate::gen::tpl;
use crate::gen::traits::Gen;
use crate::gen::tree::{atom_ent, xfn_ent, ConstTree};
use crate::interpreter::gen_nort::nort_gen;
use crate::interpreter::handler::HandlerTable;
use crate::interpreter::nort::{Clause, Expr};
use crate::libs::io::instances::io_error_handler;
use crate::libs::io::{Sink, Source};
use crate::libs::scheduler::system::{SeqScheduler, SharedHandle};
use crate::libs::std::runtime_error::RuntimeError;
use crate::utils::combine::Combine;
use crate::virt_fs::DeclTree;
#[derive(Debug, Clone)]
struct ReadFileCmd(OsString);
impl InertPayload for ReadFileCmd {
const TYPE_STR: &'static str = "readfile command";
}
#[derive(Debug, Clone)]
struct ReadDirCmd(OsString);
impl InertPayload for ReadDirCmd {
const TYPE_STR: &'static str = "readdir command";
}
#[derive(Debug, Clone)]
struct WriteFile {
name: OsString,
append: bool,
}
impl InertPayload for WriteFile {
const TYPE_STR: &'static str = "writefile command";
}
#[must_use]
fn read_file(sched: &SeqScheduler, cmd: &CPSBox<ReadFileCmd>) -> Expr {
let (ReadFileCmd(name), succ, fail, cont) = cmd.unpack3();
let name = name.clone();
let cancel = sched.run_orphan(
move |_| File::open(name),
|file, _| match file {
Err(e) => vec![io_error_handler(e, fail)],
Ok(f) => {
let source_handle = SharedHandle::wrap(Source::new(Box::new(f)));
let tpl = tpl::A(tpl::Slot, tpl::V(Inert(source_handle)));
vec![tpl.template(nort_gen(succ.location()), [succ])]
},
},
);
let tpl = tpl::A(tpl::Slot, tpl::V(CPSBox::new(1, cancel)));
tpl.template(nort_gen(cont.location()), [cont])
}
#[must_use]
fn read_dir(sched: &SeqScheduler, cmd: &CPSBox<ReadDirCmd>) -> Expr {
let (ReadDirCmd(name), succ, fail, cont) = cmd.unpack3();
let name = name.clone();
let cancel = sched.run_orphan(
move |_| {
Path::new(&name)
.read_dir()?
.map(|r| r.and_then(|e| Ok((e.file_name(), e.file_type()?.is_dir()))))
.collect()
},
|items: std::io::Result<Vec<(OsString, bool)>>, _| match items {
Err(e) => vec![io_error_handler(e, fail)],
Ok(os_namev) => {
let converted = (os_namev.into_iter())
.map(|(n, d)| {
Ok((Inert(n).atom_expr(succ.location()), Inert(d).atom_expr(succ.location())))
})
.collect::<Result<Vec<_>, Clause>>();
match converted {
Err(e) => {
let e = e.to_expr(fail.location());
let tpl = tpl::A(tpl::Slot, tpl::Slot);
vec![tpl.template(nort_gen(fail.location()), [fail, e])]
},
Ok(names) => {
let names = names.to_expr(succ.location());
let tpl = tpl::A(tpl::Slot, tpl::Slot);
vec![tpl.template(nort_gen(succ.location()), [succ, names])]
},
}
},
},
);
let tpl = tpl::A(tpl::Slot, tpl::V(CPSBox::new(1, cancel)));
tpl.template(nort_gen(cont.location()), [cont])
}
#[must_use]
fn write_file(sched: &SeqScheduler, cmd: &CPSBox<WriteFile>) -> Expr {
let (cmd, succ, fail, cont) = cmd.unpack3();
let cmd = cmd.clone();
let cancel = sched.run_orphan(
move |_| File::options().write(true).append(cmd.append).open(&cmd.name),
|file, _| match file {
Err(e) => vec![io_error_handler(e, fail)],
Ok(f) => {
let sink_handle = SharedHandle::wrap(Box::new(f) as Sink);
let tpl = tpl::A(tpl::Slot, tpl::V(Inert(sink_handle)));
vec![tpl.template(nort_gen(succ.location()), [succ])]
},
},
);
let tpl = tpl::A(tpl::Slot, tpl::V(CPSBox::new(1, cancel)));
tpl.template(nort_gen(cont.location()), [cont])
}
fn open_file_read_cmd(name: OsString) -> CPSBox<ReadFileCmd> { CPSBox::new(3, ReadFileCmd(name)) }
fn read_dir_cmd(name: OsString) -> CPSBox<ReadDirCmd> { CPSBox::new(3, ReadDirCmd(name)) }
fn open_file_write_cmd(name: OsString) -> CPSBox<WriteFile> {
CPSBox::new(3, WriteFile { name, append: false })
}
fn open_file_append_cmd(name: OsString) -> CPSBox<WriteFile> {
CPSBox::new(3, WriteFile { name, append: true })
}
fn join_paths(root: OsString, sub: OsString) -> OsString {
let mut path = PathBuf::from(root);
path.push(sub);
path.into_os_string()
}
fn pop_path(path: Inert<OsString>) -> Option<(Inert<OsString>, Inert<OsString>)> {
let mut path = PathBuf::from(path.0);
let sub = path.file_name()?.to_owned();
debug_assert!(path.pop(), "file_name above returned Some");
Some((Inert(path.into_os_string()), Inert(sub)))
}
/// A rudimentary system to read and write files.
#[derive(Clone)]
pub struct DirectFS {
scheduler: SeqScheduler,
}
impl DirectFS {
/// Create a new instance of the system.
pub fn new(scheduler: SeqScheduler) -> Self { Self { scheduler } }
}
impl IntoSystem<'static> for DirectFS {
fn into_system(self) -> System<'static> {
let mut handlers = HandlerTable::new();
let sched = self.scheduler.clone();
handlers.register(move |cmd| read_file(&sched, cmd));
let sched = self.scheduler.clone();
handlers.register(move |cmd| read_dir(&sched, cmd));
let sched = self.scheduler;
handlers.register(move |cmd| write_file(&sched, cmd));
System {
name: "system::directfs",
code: DeclTree::empty(),
prelude: Vec::new(),
lexer_plugins: vec![],
line_parsers: vec![],
constants: ConstTree::ns("system::fs", [ConstTree::tree([
xfn_ent("read_file", [open_file_read_cmd]),
xfn_ent("read_dir", [read_dir_cmd]),
xfn_ent("write_file", [open_file_write_cmd]),
xfn_ent("append_file", [open_file_append_cmd]),
xfn_ent("join_paths", [join_paths]),
xfn_ent("pop_path", [pop_path]),
atom_ent("cwd", [Unstable::new(|_| -> ExternResult<_> {
let path =
std::env::current_dir().map_err(|e| RuntimeError::ext(e.to_string(), "reading CWD"))?;
Ok(Inert(path.into_os_string()))
})]),
])])
.combine(os_string_lib())
.expect("os_string library and directfs conflict"),
handlers,
}
}
}