use std::num::NonZero; use std::rc::Rc; use itertools::Itertools; use orchid_base::{is, mk_errv}; use orchid_extension::gen_expr::{call, new_atom}; use orchid_extension::std_reqs::ReadLimit; use orchid_extension::tree::{GenMember, comments, fun, prefix}; use orchid_extension::{Expr, ForeignAtom, TAtom, get_arg}; use crate::std::binary::binary_atom::BlobAtom; use crate::std::stream::stream_cmds::{ReadStreamCmd, WriteAction, WriteStreamCmd}; use crate::{Int, OrcString}; pub fn gen_stream_lib() -> Vec { prefix("std", [comments( ["Read from and write to byte streams"], prefix("stream", [ fun(true, "read_bin", async |hand: ForeignAtom, succ: Expr, fail: Expr| { new_atom(ReadStreamCmd { hand, succ, fail, as_str: false, limit: ReadLimit::End }) }), fun(true, "read_str", async |hand: ForeignAtom, succ: Expr, fail: Expr| { new_atom(ReadStreamCmd { hand, succ, fail, as_str: true, limit: ReadLimit::End }) }), fun(true, "read_until", async |hand: ForeignAtom, delim: Int, succ: Expr, fail: Expr| { let Ok(end) = delim.0.try_into() else { return Err(mk_errv( is("Byte out of range").await, format!( "{} doesn't fit into a byte and cannot be used as a delimiter for reading", delim.0 ), [get_arg(1).pos().await], )); }; let limit = ReadLimit::Delimiter(end); Ok(new_atom(ReadStreamCmd { hand, succ, fail, as_str: false, limit })) }), fun(true, "read_bytes", async |hand: ForeignAtom, count: Int, succ: Expr, fail: Expr| { match count.0.try_into().map(NonZero::new) { Ok(Some(nzlen)) => { let limit = ReadLimit::Length(nzlen); Ok(new_atom(ReadStreamCmd { hand, succ, fail, as_str: false, limit })) }, Ok(None) => Ok(call(succ, new_atom(BlobAtom(Rc::default()))).await), Err(_) => Err(mk_errv( is("Length cannot be negative").await, format!("{} is negative and cannot be used as a length", count.0), [get_arg(1).pos().await], )), } }), fun(true, "read_line", async |hand: ForeignAtom, succ: Expr, fail: Expr| { const LIMIT_BR: ReadLimit = ReadLimit::Delimiter(b'\n'); new_atom(ReadStreamCmd { hand, succ, fail, as_str: true, limit: LIMIT_BR }) }), fun(true, "write_str", async |hand: ForeignAtom, str: OrcString, succ: Expr, fail: Expr| { let action = WriteAction::Write(Rc::new(str.get_string().await.bytes().collect_vec())); new_atom(WriteStreamCmd { hand, action, succ, fail }) }), fun( true, "write_bin", async |hand: ForeignAtom, bin: TAtom, succ: Expr, fail: Expr| { let action = WriteAction::Write(bin.own().await.0.clone()); new_atom(WriteStreamCmd { hand, action, succ, fail }) }, ), fun(true, "flush", async |hand: ForeignAtom, succ: Expr, fail: Expr| { new_atom(WriteStreamCmd { hand, action: WriteAction::Flush, succ, fail }) }), fun(true, "close", async |hand: ForeignAtom, succ: Expr, fail: Expr| { new_atom(WriteStreamCmd { hand, action: WriteAction::Close, succ, fail }) }), ]), )]) }