diff --git a/.cargo/config.toml b/.cargo/config.toml index d4f4905..5cecad4 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -6,9 +6,9 @@ orcxdb = "xtask orcxdb" [env] CARGO_WORKSPACE_DIR = { value = "", relative = true } ORCHID_EXTENSIONS = "target/debug/orchid-std" -ORCHID_DEFAULT_SYSTEMS = "orchid::std" +ORCHID_DEFAULT_SYSTEMS = "orchid::std;orchid::macros" ORCHID_LOG_BUFFERS = "true" -RUSTBACKTRACE = "1" +RUST_BACKTRACE = "1" [build] # rustflags = ["-Znext-solver"] diff --git a/Cargo.lock b/Cargo.lock index 8bda1fa..85ca37c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,24 +23,11 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom 0.2.15", + "getrandom", "once_cell", "version_check", ] -[[package]] -name = "ahash" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" -dependencies = [ - "cfg-if", - "getrandom 0.3.3", - "once_cell", - "version_check", - "zerocopy 0.8.26", -] - [[package]] name = "aho-corasick" version = "1.1.3" @@ -111,17 +98,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" -[[package]] -name = "async-channel" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" -dependencies = [ - "concurrent-queue", - "event-listener 2.5.3", - "futures-core", -] - [[package]] name = "async-channel" version = "2.3.1" @@ -135,31 +111,11 @@ dependencies = [ ] [[package]] -name = "async-executor" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +name = "async-fn-stream" +version = "0.1.0" dependencies = [ - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "slab", -] - -[[package]] -name = "async-global-executor" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" -dependencies = [ - "async-channel 2.3.1", - "async-executor", - "async-io", - "async-lock", - "blocking", - "futures-lite", - "once_cell", + "futures", + "test_executors", ] [[package]] @@ -187,7 +143,7 @@ version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" dependencies = [ - "event-listener 5.4.0", + "event-listener", "event-listener-strategy", "pin-project-lite", ] @@ -204,14 +160,14 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65daa13722ad51e6ab1a1b9c01299142bc75135b337923cfa10e79bbbd669f00" dependencies = [ - "async-channel 2.3.1", + "async-channel", "async-io", "async-lock", "async-signal", "async-task", "blocking", "cfg-if", - "event-listener 5.4.0", + "event-listener", "futures-lite", "rustix 1.0.8", ] @@ -234,54 +190,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "async-std" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" -dependencies = [ - "async-channel 1.9.0", - "async-global-executor", - "async-io", - "async-lock", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite", - "gloo-timers", - "kv-log-macro", - "log", - "memchr", - "once_cell", - "pin-project-lite", - "pin-utils", - "slab", - "wasm-bindgen-futures", -] - -[[package]] -name = "async-stream" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" -dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", -] - [[package]] name = "async-task" version = "4.7.1" @@ -348,7 +256,7 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" dependencies = [ - "async-channel 2.3.1", + "async-channel", "async-task", "futures-io", "futures-lite", @@ -562,41 +470,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "darling" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2 1.0.101", - "quote 1.0.40", - "strsim", - "syn 2.0.106", -] - -[[package]] -name = "darling_macro" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" -dependencies = [ - "darling_core", - "quote 1.0.40", - "syn 2.0.106", -] - [[package]] name = "derive_destructure" version = "1.0.0" @@ -646,12 +519,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - [[package]] name = "event-listener" version = "5.4.0" @@ -669,7 +536,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" dependencies = [ - "event-listener 5.4.0", + "event-listener", "pin-project-lite", ] @@ -679,12 +546,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "foldhash" version = "0.2.0" @@ -817,19 +678,7 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "wasi 0.14.3+wasi-0.2.4", + "wasi", ] [[package]] @@ -838,25 +687,13 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" -[[package]] -name = "gloo-timers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash 0.7.8", + "ahash", ] [[package]] @@ -888,12 +725,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - [[package]] name = "include_dir" version = "0.7.4" @@ -982,15 +813,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bf92d396aa2df203577ebef8deaf1efc24d446366ca86be83ec8ac794b157d6" -[[package]] -name = "kv-log-macro" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" -dependencies = [ - "log", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -1030,9 +852,6 @@ name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" -dependencies = [ - "value-bag", -] [[package]] name = "logwise" @@ -1099,7 +918,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys 0.52.0", ] @@ -1160,7 +979,6 @@ dependencies = [ name = "orchid-api-derive" version = "0.1.0" dependencies = [ - "darling", "itertools", "orchid-api-traits", "proc-macro2 1.0.101", @@ -1172,7 +990,7 @@ dependencies = [ name = "orchid-api-traits" version = "0.1.0" dependencies = [ - "async-stream", + "async-fn-stream", "futures", "itertools", "never", @@ -1183,8 +1001,8 @@ dependencies = [ name = "orchid-base" version = "0.1.0" dependencies = [ + "async-fn-stream", "async-once-cell", - "async-stream", "derive_destructure", "dyn-clone", "futures", @@ -1209,10 +1027,9 @@ dependencies = [ name = "orchid-extension" version = "0.1.0" dependencies = [ - "ahash 0.8.12", + "async-fn-stream", "async-lock", "async-once-cell", - "async-stream", "derive_destructure", "dyn-clone", "futures", @@ -1241,10 +1058,10 @@ dependencies = [ name = "orchid-host" version = "0.1.0" dependencies = [ + "async-fn-stream", "async-lock", "async-once-cell", "async-process", - "async-stream", "bound", "derive_destructure", "futures", @@ -1269,7 +1086,6 @@ name = "orchid-std" version = "0.1.0" dependencies = [ "async-once-cell", - "async-stream", "futures", "hashbrown 0.16.0", "itertools", @@ -1291,8 +1107,7 @@ dependencies = [ name = "orcx" version = "0.1.0" dependencies = [ - "async-std", - "async-stream", + "async-fn-stream", "camino", "clap", "ctrlc", @@ -1392,7 +1207,7 @@ version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "zerocopy 0.7.35", + "zerocopy", ] [[package]] @@ -1466,12 +1281,6 @@ dependencies = [ "proc-macro2 1.0.101", ] -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - [[package]] name = "radium" version = "0.7.0" @@ -1505,7 +1314,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom", ] [[package]] @@ -2039,12 +1848,6 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" -[[package]] -name = "value-bag" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef4c4aa54d5d05a279399bfa921ec387b7aba77caf7a682ae8d86785b8fdad2" - [[package]] name = "version_check" version = "0.9.5" @@ -2067,15 +1870,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "wasi" -version = "0.14.3+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" -dependencies = [ - "wit-bindgen", -] - [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -2279,12 +2073,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "wit-bindgen" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" - [[package]] name = "wyz" version = "0.5.1" @@ -2308,16 +2096,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", - "zerocopy-derive 0.7.35", -] - -[[package]] -name = "zerocopy" -version = "0.8.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" -dependencies = [ - "zerocopy-derive 0.8.26", + "zerocopy-derive", ] [[package]] @@ -2330,14 +2109,3 @@ dependencies = [ "quote 1.0.40", "syn 2.0.106", ] - -[[package]] -name = "zerocopy-derive" -version = "0.8.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" -dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", -] diff --git a/Cargo.toml b/Cargo.toml index 8d35da0..a63d212 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,5 +11,5 @@ members = [ "orchid-api-derive", "orchid-api-traits", "stdio-perftest", - "xtask", + "xtask", "async-fn-stream", ] diff --git a/async-fn-stream/Cargo.toml b/async-fn-stream/Cargo.toml new file mode 100644 index 0000000..424e45a --- /dev/null +++ b/async-fn-stream/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "async-fn-stream" +version = "0.1.0" +edition = "2024" + +[dependencies] +futures = { version = "0.3.31", features = ["std"], default-features = false } + +[dev-dependencies] +test_executors = "0.3.5" diff --git a/async-fn-stream/src/lib.rs b/async-fn-stream/src/lib.rs new file mode 100644 index 0000000..2a8af10 --- /dev/null +++ b/async-fn-stream/src/lib.rs @@ -0,0 +1,207 @@ +use std::cell::Cell; +use std::future::poll_fn; +use std::marker::PhantomData; +use std::pin::Pin; +use std::ptr; +use std::task::{Context, Poll}; + +use futures::future::LocalBoxFuture; +use futures::{FutureExt, Stream}; + +type YieldSlot<'a, T> = &'a Cell>; + +/// Handle that allows you to emit values on a stream. If you drop +/// this, the stream will end and you will not be polled again. +pub struct StreamCtx<'a, T>(&'a Cell>, PhantomData<&'a ()>); +impl StreamCtx<'_, T> { + pub fn emit(&mut self, value: T) -> impl Future { + assert!(self.0.replace(Some(value)).is_none(), "Leftover value in stream"); + let mut state = Poll::Pending; + poll_fn(move |_| std::mem::replace(&mut state, Poll::Ready(()))) + } +} + +enum FnOrFut<'a, T, O> { + Fn(Option) -> LocalBoxFuture<'a, O> + 'a>>), + Fut(LocalBoxFuture<'a, O>), +} + +struct AsyncFnStream<'a, T> { + driver: FnOrFut<'a, T, ()>, + output: Cell>, +} +impl<'a, T> Stream for AsyncFnStream<'a, T> { + type Item = T; + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unsafe { + let self_mut = self.get_unchecked_mut(); + let fut = match &mut self_mut.driver { + FnOrFut::Fut(fut) => fut, + FnOrFut::Fn(f) => { + // safety: the cell is held inline in self, which is pinned. + let cell = ptr::from_ref(&self_mut.output).as_ref().unwrap(); + let fut = f.take().unwrap()(cell); + self_mut.driver = FnOrFut::Fut(fut); + return Pin::new_unchecked(self_mut).poll_next(cx); + }, + }; + match fut.as_mut().poll(cx) { + Poll::Ready(()) => Poll::Ready(None), + Poll::Pending => match self_mut.output.replace(None) { + None => Poll::Pending, + Some(t) => Poll::Ready(Some(t)), + }, + } + } + } +} + +struct AsyncFnTryStream<'a, T, E> { + driver: FnOrFut<'a, T, Result, E>>, + output: Cell>, +} +impl<'a, T, E> Stream for AsyncFnTryStream<'a, T, E> { + type Item = Result; + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unsafe { + let self_mut = self.get_unchecked_mut(); + let fut = match &mut self_mut.driver { + FnOrFut::Fut(fut) => fut, + FnOrFut::Fn(f) => { + // safety: the cell is held inline in self, which is pinned. + let cell = ptr::from_ref(&self_mut.output).as_ref().unwrap(); + let fut = f.take().unwrap()(cell); + self_mut.driver = FnOrFut::Fut(fut); + return Pin::new_unchecked(self_mut).poll_next(cx); + }, + }; + match fut.as_mut().poll(cx) { + Poll::Ready(Ok(_)) => Poll::Ready(None), + Poll::Ready(Err(ex)) => Poll::Ready(Some(Err(ex))), + Poll::Pending => match self_mut.output.replace(None) { + None => Poll::Pending, + Some(t) => Poll::Ready(Some(Ok(t))), + }, + } + } + } +} + +/// Create a stream from an async function acting as a coroutine +pub fn stream<'a, T: 'a>( + f: impl for<'b> AsyncFnOnce(StreamCtx<'b, T>) + 'a, +) -> impl Stream + 'a { + AsyncFnStream { + output: Cell::new(None), + driver: FnOrFut::Fn(Some(Box::new(|t| { + async { f(StreamCtx(t, PhantomData)).await }.boxed_local() + }))), + } +} + +/// Create a stream of result from a fallible function. +pub fn try_stream<'a, T: 'a, E: 'a>( + f: impl for<'b> AsyncFnOnce(StreamCtx<'b, T>) -> Result, E> + 'a, +) -> impl Stream> + 'a { + AsyncFnTryStream { + output: Cell::new(None), + driver: FnOrFut::Fn(Some(Box::new(|t| { + async { f(StreamCtx(t, PhantomData)).await }.boxed_local() + }))), + } +} + +#[cfg(test)] +mod test { + use std::task::Poll; + use std::{future, pin}; + + use futures::channel::mpsc::channel; + use futures::{Stream, StreamExt, TryStreamExt}; + use test_executors::spin_on; + + use crate::{stream, try_stream}; + + #[test] + fn sync() { + spin_on(async { + let v = stream(async |mut cx| { + for i in 0..5 { + cx.emit(i).await + } + }) + .collect::>() + .await; + assert_eq!(v, [0, 1, 2, 3, 4]) + }) + } + + #[test] + /// The exact behaviour of the poll function under blocked use + fn with_delay() { + spin_on(async { + let (mut send, mut recv) = channel(0); + let mut s = pin::pin!(stream(async |mut cx| { + for i in 0..2 { + cx.emit(i).await + } + recv.next().await; + for i in 2..5 { + cx.emit(i).await + } + })); + let mut log = String::new(); + let log = future::poll_fn(|cx| { + match s.as_mut().poll_next(cx) { + Poll::Ready(Some(r)) => log += &format!("Found {r}\n"), + Poll::Ready(None) => return Poll::Ready(format!("{log}Ended")), + Poll::Pending => match send.try_send(()) { + Ok(()) => log += "Unblocked\n", + Err(err) => return Poll::Ready(format!("{log}Unblock err: {err}")), + }, + } + Poll::Pending + }) + .await; + const EXPECTED: &str = "\ + Found 0\n\ + Found 1\n\ + Unblocked\n\ + Found 2\n\ + Found 3\n\ + Found 4\n\ + Ended"; + assert_eq!(log, EXPECTED) + }) + } + + #[test] + fn sync_try_all_ok() { + spin_on(async { + let v = try_stream::<_, ()>(async |mut cx| { + for i in 0..5 { + cx.emit(i).await + } + Ok(cx) + }) + .try_collect::>() + .await; + assert_eq!(v, Ok(vec![0, 1, 2, 3, 4])) + }) + } + + #[test] + fn sync_try_err() { + spin_on(async { + let v = try_stream::<_, ()>(async |mut cx| { + for i in 0..5 { + cx.emit(i).await + } + Err(()) + }) + .try_collect::>() + .await; + assert_eq!(v, Err(())) + }) + } +} diff --git a/orchid-api-derive/Cargo.toml b/orchid-api-derive/Cargo.toml index 133fa5f..2b513fc 100644 --- a/orchid-api-derive/Cargo.toml +++ b/orchid-api-derive/Cargo.toml @@ -13,5 +13,4 @@ quote = "1.0.40" syn = { version = "2.0.106" } orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" } proc-macro2 = "1.0.101" -darling = "0.21.3" itertools = "0.14.0" diff --git a/orchid-api-traits/Cargo.toml b/orchid-api-traits/Cargo.toml index 2763ffd..f4947f6 100644 --- a/orchid-api-traits/Cargo.toml +++ b/orchid-api-traits/Cargo.toml @@ -6,8 +6,8 @@ edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -async-stream = "0.3.6" -futures = { version = "0.3.31", features = ["std"] } +async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" } +futures = { version = "0.3.31", features = ["std"], default-features = false } itertools = "0.14.0" never = "0.1.0" ordered-float = "5.0.0" diff --git a/orchid-api-traits/src/coding.rs b/orchid-api-traits/src/coding.rs index b8757a7..8e6445d 100644 --- a/orchid-api-traits/src/coding.rs +++ b/orchid-api-traits/src/coding.rs @@ -7,7 +7,7 @@ use std::pin::Pin; use std::rc::Rc; use std::sync::Arc; -use async_stream::stream; +use async_fn_stream::stream; use futures::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, StreamExt}; use never::Never; use ordered_float::NotNan; @@ -133,7 +133,13 @@ impl Encode for str { impl Decode for Vec { async fn decode(mut read: Pin<&mut R>) -> Self { let len = u64::decode(read.as_mut()).await.try_into().unwrap(); - stream! { loop { yield T::decode(read.as_mut()).await } }.take(len).collect().await + stream(async |mut cx| { + for _ in 0..len { + cx.emit(T::decode(read.as_mut()).await).await + } + }) + .collect() + .await } } impl Encode for Vec { @@ -186,7 +192,13 @@ impl Encode for Result { impl Decode for HashMap { async fn decode(mut read: Pin<&mut R>) -> Self { let len = u64::decode(read.as_mut()).await.try_into().unwrap(); - stream! { loop { yield <(K, V)>::decode(read.as_mut()).await } }.take(len).collect().await + stream(async |mut cx| { + for _ in 0..len { + cx.emit(<(K, V)>::decode(read.as_mut()).await).await + } + }) + .collect() + .await } } impl Encode for HashMap { @@ -259,8 +271,13 @@ impl Encode for bool { impl Decode for [T; N] { async fn decode(mut read: Pin<&mut R>) -> Self { // TODO: figure out how to do this in safe rust on the stack - let v = - stream! { loop { yield T::decode(read.as_mut()).await } }.take(N).collect::>().await; + let v = stream(async |mut cx| { + for _ in 0..N { + cx.emit(T::decode(read.as_mut()).await).await + } + }) + .collect::>() + .await; v.try_into().unwrap_or_else(|_| unreachable!("The length of this stream is statically known")) } } diff --git a/orchid-api/Cargo.toml b/orchid-api/Cargo.toml index 5f53b41..3b97ed1 100644 --- a/orchid-api/Cargo.toml +++ b/orchid-api/Cargo.toml @@ -9,7 +9,7 @@ edition = "2024" ordered-float = "5.0.0" orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" } orchid-api-derive = { version = "0.1.0", path = "../orchid-api-derive" } -futures = { version = "0.3.31", features = ["std"] } +futures = { version = "0.3.31", features = ["std"], default-features = false } [dev-dependencies] test_executors = "0.3.2" diff --git a/orchid-base/Cargo.toml b/orchid-base/Cargo.toml index e539421..3a49db5 100644 --- a/orchid-base/Cargo.toml +++ b/orchid-base/Cargo.toml @@ -6,11 +6,11 @@ edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" } async-once-cell = "0.5.4" -async-stream = "0.3.6" derive_destructure = "1.0.0" dyn-clone = "1.0.20" -futures = { version = "0.3.31", features = ["std"] } +futures = { version = "0.3.31", features = ["std"], default-features = false } hashbrown = "0.16.0" itertools = "0.14.0" lazy_static = "1.5.0" diff --git a/orchid-base/src/tree.rs b/orchid-base/src/tree.rs index 85557b3..0a62571 100644 --- a/orchid-base/src/tree.rs +++ b/orchid-base/src/tree.rs @@ -4,7 +4,7 @@ use std::future::Future; use std::marker::PhantomData; use std::rc::Rc; -use async_stream::stream; +use async_fn_stream::stream; use futures::future::join_all; use futures::{FutureExt, StreamExt}; use itertools::Itertools; @@ -195,11 +195,11 @@ pub async fn ttv_from_api( src: &Sym, i: &Interner, ) -> Vec> { - stream! { + stream(async |mut cx| { for tok in tokv { - yield TokTree::::from_api(tok.borrow(), hctx, xctx, src, i).boxed_local().await + cx.emit(TokTree::::from_api(tok.borrow(), hctx, xctx, src, i).boxed_local().await).await } - } + }) .collect() .await } @@ -209,11 +209,11 @@ pub async fn ttv_into_api( hctx: &mut H::ToApiCtx<'_>, xctx: &mut X::ToApiCtx<'_>, ) -> Vec { - stream! { + stream(async |mut cx| { for tok in tokv { - yield tok.into_api(hctx, xctx).await + cx.emit(tok.into_api(hctx, xctx).await).await } - } + }) .collect() .await } diff --git a/orchid-extension/Cargo.toml b/orchid-extension/Cargo.toml index d22d897..3ddcfe2 100644 --- a/orchid-extension/Cargo.toml +++ b/orchid-extension/Cargo.toml @@ -6,13 +6,15 @@ edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ahash = "0.8.12" +async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" } async-lock = "3.4.1" async-once-cell = "0.5.4" -async-stream = "0.3.6" derive_destructure = "1.0.0" dyn-clone = "1.0.20" -futures = { version = "0.3.31", features = ["std"] } +futures = { version = "0.3.31", features = [ + "std", + "async-await", +], default-features = false } hashbrown = "0.16.0" include_dir = { version = "0.7.4", optional = true } itertools = "0.14.0" @@ -29,10 +31,11 @@ ordered-float = "5.0.0" pastey = "0.1.1" some_executor = "0.6.1" substack = "1.1.1" -tokio = { version = "1.47.1", optional = true } +tokio = { version = "1.47.1", optional = true, features = [] } tokio-util = { version = "0.7.16", optional = true, features = ["compat"] } trait-set = "0.3.0" [features] tokio = ["dep:tokio", "dep:tokio-util"] +default = ["tokio"] diff --git a/orchid-extension/src/atom.rs b/orchid-extension/src/atom.rs index 69ca5fc..d44f46f 100644 --- a/orchid-extension/src/atom.rs +++ b/orchid-extension/src/atom.rs @@ -1,4 +1,5 @@ use std::any::{Any, TypeId, type_name}; +use std::collections::HashMap; use std::fmt; use std::future::Future; use std::num::NonZeroU32; @@ -6,7 +7,6 @@ use std::ops::Deref; use std::pin::Pin; use std::rc::Rc; -use ahash::HashMap; use dyn_clone::{DynClone, clone_box}; use futures::future::LocalBoxFuture; use futures::{AsyncRead, AsyncWrite, FutureExt, StreamExt, stream}; diff --git a/orchid-extension/src/entrypoint.rs b/orchid-extension/src/entrypoint.rs index 7e93a83..2986490 100644 --- a/orchid-extension/src/entrypoint.rs +++ b/orchid-extension/src/entrypoint.rs @@ -202,8 +202,12 @@ pub fn extension_init( let record = SystemRecord { ctx, lazy_members: lazy_mems.into_inner() }; let systems = systems_weak.upgrade().expect("System constructed during shutdown"); systems.lock().await.insert(new_sys.id, record); - let response = - api::NewSystemResponse { lex_filter, const_root, line_types: vec![], prelude }; + let line_types = join_all( + (cted.inst().dyn_parsers().iter()) + .map(|p| async { i.i(p.line_head()).await.to_api() }), + ) + .await; + let response = api::NewSystemResponse { lex_filter, const_root, line_types, prelude }; hand.handle(&new_sys, &response).await }, api::HostExtReq::GetMember(get_tree @ api::GetMember(sys_id, tree_id)) => { diff --git a/orchid-extension/src/parser.rs b/orchid-extension/src/parser.rs index 4e6ad5b..fa48e7f 100644 --- a/orchid-extension/src/parser.rs +++ b/orchid-extension/src/parser.rs @@ -1,6 +1,6 @@ use std::marker::PhantomData; -use async_stream::stream; +use async_fn_stream::stream; use futures::future::{LocalBoxFuture, join_all}; use futures::{FutureExt, Stream, StreamExt}; use itertools::Itertools; @@ -218,14 +218,15 @@ impl ConstCtx { sys: self.ctx.sys_id(), names: names.into_iter().map(|n| n.to_api()).collect_vec(), }; - stream! { + stream(async |mut cx| { for name_opt in self.ctx.reqnot().request(resolve_names).await { - yield match name_opt { + cx.emit(match name_opt { Err(e) => Err(OrcErrv::from_api(&e, self.ctx.i()).await), - Ok(name) => Ok(Sym::from_api(name, self.ctx.i()).await) - } + Ok(name) => Ok(Sym::from_api(name, self.ctx.i()).await), + }) + .await } - } + }) } pub async fn names_n(&self, names: [&Sym; N]) -> [OrcRes; N] { self.names(names).collect::>().await.try_into().expect("Lengths must match") diff --git a/orchid-extension/src/tokio.rs b/orchid-extension/src/tokio.rs index 37ed5ae..199b3d2 100644 --- a/orchid-extension/src/tokio.rs +++ b/orchid-extension/src/tokio.rs @@ -2,7 +2,7 @@ use crate::entrypoint::ExtensionData; #[cfg(feature = "tokio")] pub async fn tokio_main(data: ExtensionData) { - use std::io::Write; + use std::io::{ErrorKind, Write}; use std::mem; use std::pin::{Pin, pin}; use std::rc::Rc; @@ -14,8 +14,7 @@ pub async fn tokio_main(data: ExtensionData) { use futures::stream::FuturesUnordered; use orchid_api_traits::{Decode, Encode}; use orchid_base::msg::{recv_msg, send_msg}; - use tokio::io; - use tokio::io::Stdout; + use tokio::io::{Stdout, stdin, stdout}; use tokio::task::{LocalSet, spawn_local}; use tokio_util::compat::{Compat, TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt}; @@ -24,7 +23,7 @@ pub async fn tokio_main(data: ExtensionData) { let local_set = LocalSet::new(); local_set.spawn_local(async { - let host_header = api::HostHeader::decode(Pin::new(&mut tokio::io::stdin().compat())).await; + let host_header = api::HostHeader::decode(Pin::new(&mut stdin().compat())).await; let init = Rc::new(extension_init(data, host_header, Rc::new(|fut| mem::drop(spawn_local(fut))))); let mut buf = Vec::new(); @@ -36,10 +35,10 @@ pub async fn tokio_main(data: ExtensionData) { let mut io = FuturesUnordered::>::new(); io.push(Box::pin(async { loop { - match recv_msg(pin!(io::stdin().compat())).await { + match recv_msg(pin!(stdin().compat())).await { Ok(msg) => init.send(&msg[..]).await, - Err(e) if e.kind() == io::ErrorKind::BrokenPipe => break, - Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => break, + Err(e) if e.kind() == ErrorKind::BrokenPipe => break, + Err(e) if e.kind() == ErrorKind::UnexpectedEof => break, Err(e) => panic!("{e}"), } } @@ -47,7 +46,7 @@ pub async fn tokio_main(data: ExtensionData) { io.push(Box::pin(async { while let Some(msg) = init.recv().await { static STDOUT: OnceCell>> = OnceCell::new(); - let stdout_lk = STDOUT.get_or_init(async { Mutex::new(io::stdout().compat_write()) }).await; + let stdout_lk = STDOUT.get_or_init(async { Mutex::new(stdout().compat_write()) }).await; let mut stdout_g = stdout_lk.lock().await; send_msg(pin!(&mut *stdout_g), &msg[..]).await.expect("Parent pipe broken"); } diff --git a/orchid-extension/src/tree.rs b/orchid-extension/src/tree.rs index de71982..73fafaa 100644 --- a/orchid-extension/src/tree.rs +++ b/orchid-extension/src/tree.rs @@ -1,7 +1,7 @@ use std::num::NonZero; use std::rc::Rc; -use async_stream::stream; +use async_fn_stream::stream; use dyn_clone::{DynClone, clone_box}; use futures::future::{LocalBoxFuture, join_all}; use futures::{FutureExt, StreamExt}; @@ -209,8 +209,14 @@ impl MemKind { Self::Lazy(lazy) => api::MemberKind::Lazy(ctx.with_lazy(lazy)), Self::Const(c) => api::MemberKind::Const(c.api_return(ctx.sys(), ctx.req()).await), Self::Mod { members } => api::MemberKind::Module(api::Module { - members: Box::pin(stream! { for m in members { yield m.into_api(ctx).await } }.collect()) - .await, + members: stream(async |mut cx| { + for m in members { + cx.emit(m.into_api(ctx).await).await + } + }) + .collect() + .boxed_local() + .await, }), } } diff --git a/orchid-host/Cargo.toml b/orchid-host/Cargo.toml index 8b92a6e..4616a4a 100644 --- a/orchid-host/Cargo.toml +++ b/orchid-host/Cargo.toml @@ -6,13 +6,13 @@ edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" } async-lock = "3.4.1" async-once-cell = "0.5.4" async-process = "2.4.0" -async-stream = "0.3.6" bound = "0.6.0" derive_destructure = "1.0.0" -futures = { version = "0.3.31", features = ["std"] } +futures = { version = "0.3.31", features = ["std"], default-features = false } hashbrown = "0.16.0" itertools = "0.14.0" lazy_static = "1.5.0" diff --git a/orchid-host/src/extension.rs b/orchid-host/src/extension.rs index e810082..542d09d 100644 --- a/orchid-host/src/extension.rs +++ b/orchid-host/src/extension.rs @@ -5,7 +5,7 @@ use std::num::NonZeroU64; use std::pin::pin; use std::rc::{Rc, Weak}; -use async_stream::stream; +use async_fn_stream::stream; use derive_destructure::destructure; use futures::channel::mpsc::{Sender, channel}; use futures::future::{join, join_all}; @@ -66,7 +66,7 @@ impl Extension { let init = Rc::new(init); let (exiting_snd, exiting_rcv) = channel::<()>(0); (ctx.spawn)(clone!(init, weak, ctx; Box::pin(async move { - let rcv_stream = stream! { loop { yield init.recv().await } }; + let rcv_stream = stream(async |mut cx| loop { cx.emit( init.recv().await).await }); let mut event_stream = pin!(stream::select(exiting_rcv.map(|()| None), rcv_stream)); while let Some(Some(msg)) = event_stream.next().await { if let Some(reqnot) = weak.upgrade().map(|rc| rc.reqnot.clone()) { @@ -207,14 +207,15 @@ impl Extension { let sys = weak_sys.upgrade().expect("ResolveNames after sys drop"); sys.name_resolver(*constid).await }; - let responses = stream! { + let responses = stream(async |mut cx| { for name in names { - yield match resolver(&ctx.i.ex(*name).await[..]).await { + cx.emit(match resolver(&ctx.i.ex(*name).await[..]).await { Ok(abs) => Ok(abs.to_sym(&ctx.i).await.to_api()), Err(e) => Err(e.to_api()), - } + }) + .await } - } + }) .collect() .await; hand.handle(rn, &responses).await diff --git a/orchid-host/src/parse.rs b/orchid-host/src/parse.rs index 6aa4ae0..ac7d9a8 100644 --- a/orchid-host/src/parse.rs +++ b/orchid-host/src/parse.rs @@ -120,7 +120,7 @@ pub async fn parse_exportable_item<'a>( let ext_lines = ctx.systems().flat_map(System::line_types).join(", "); return Err(mk_errv( ctx.i().i("Unrecognized line type").await, - format!("Line types are: const, mod, macro, grammar, {ext_lines}"), + format!("Line types are: mod, {ext_lines}"), [tail.prev().sr()], )); }; diff --git a/orchid-host/src/system.rs b/orchid-host/src/system.rs index 2d7206e..00bf53c 100644 --- a/orchid-host/src/system.rs +++ b/orchid-host/src/system.rs @@ -18,7 +18,7 @@ use orchid_base::location::SrcRange; use orchid_base::name::{NameLike, Sym, VName, VPath}; use orchid_base::parse::Comment; use orchid_base::reqnot::{ReqNot, Requester}; -use orchid_base::tree::ttv_from_api; +use orchid_base::tree::{recur, ttv_from_api}; use ordered_float::NotNan; use substack::{Stackframe, Substack}; @@ -28,7 +28,9 @@ use crate::dealias::walk; use crate::expr::{ExprParseCtx, ExprWillPanic}; use crate::expr_store::ExprStore; use crate::extension::{Extension, WeakExtension}; -use crate::parsed::{Item, ItemKind, ParsTokTree, ParsedMember, ParsedMemberKind, ParsedModule}; +use crate::parsed::{ + Item, ItemKind, ParsTok, ParsTokTree, ParsedMember, ParsedMemberKind, ParsedModule, +}; use crate::tree::Root; #[derive(destructure)] @@ -112,7 +114,13 @@ impl System { let src_path = line.first().expect("cannot be empty").sr.path(); let line = join_all(line.into_iter().map(|t| async { let mut expr_store = self.0.ext.exprs().clone(); - t.into_api(&mut expr_store, &mut ExprWillPanic).await + let without_new_expr = recur(t, &|t, r| { + if let ParsTok::NewExpr(expr) = t.tok { + return ParsTok::Handle(expr).at(t.sr); + } + r(t) + }); + without_new_expr.into_api(&mut expr_store, &mut ExprWillPanic).await })) .await; let comments = comments.iter().map(Comment::to_api).collect_vec(); diff --git a/orchid-std/Cargo.toml b/orchid-std/Cargo.toml index 82b2af7..e2723b9 100644 --- a/orchid-std/Cargo.toml +++ b/orchid-std/Cargo.toml @@ -5,8 +5,7 @@ edition = "2024" [dependencies] async-once-cell = "0.5.4" -async-stream = "0.3.6" -futures = { version = "0.3.31", features = ["std"] } +futures = { version = "0.3.31", features = ["std"], default-features = false } hashbrown = "0.16.0" itertools = "0.14.0" never = "0.1.0" diff --git a/orchid-std/src/macros/macro_system.rs b/orchid-std/src/macros/macro_system.rs index 69985ce..a3b7dd8 100644 --- a/orchid-std/src/macros/macro_system.rs +++ b/orchid-std/src/macros/macro_system.rs @@ -24,7 +24,7 @@ pub struct MacroSystem; impl SystemCtor for MacroSystem { type Deps = StdSystem; type Instance = Self; - const NAME: &'static str = "macros"; + const NAME: &'static str = "orchid::macros"; const VERSION: f64 = 0.00_01; fn inst(_: SystemHandle) -> Self::Instance { Self } } diff --git a/orcx/Cargo.toml b/orcx/Cargo.toml index 7d28610..a53d961 100644 --- a/orcx/Cargo.toml +++ b/orcx/Cargo.toml @@ -6,8 +6,7 @@ edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -async-std = "1.13.0" -async-stream = "0.3.6" +async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" } camino = "1.1.9" clap = { version = "4.5.24", features = ["derive", "env"] } ctrlc = "3.4.5" diff --git a/orcx/src/main.rs b/orcx/src/main.rs index 562b42e..9214410 100644 --- a/orcx/src/main.rs +++ b/orcx/src/main.rs @@ -7,9 +7,7 @@ use std::mem; use std::process::{Command, ExitCode}; use std::rc::Rc; -use async_std::io::stdin; -use async_std::path::PathBuf; -use async_stream::try_stream; +use async_fn_stream::try_stream; use camino::Utf8PathBuf; use clap::{Parser, Subcommand}; use futures::{Stream, TryStreamExt, io}; @@ -32,6 +30,7 @@ use orchid_host::parsed::{Item, ItemKind, ParsTokTree, ParsedMember, ParsedModul use orchid_host::subprocess::ext_command; use orchid_host::system::init_systems; use substack::Substack; +use tokio::io::{AsyncBufReadExt, BufReader, stdin}; use tokio::task::{LocalSet, spawn_local}; use crate::parse_folder::parse_folder; @@ -62,7 +61,7 @@ pub enum Commands { file: Utf8PathBuf, }, Repl, - Execute { + Exec { #[arg(long)] proj: Option, #[arg()] @@ -76,19 +75,16 @@ fn get_all_extensions<'a>( msg_logger: &'a Logger, ctx: &'a Ctx, ) -> impl Stream> + 'a { - try_stream! { + try_stream(async |mut cx| { for ext_path in args.extension.iter() { - let exe = if cfg!(windows) { - ext_path.with_extension("exe") - } else { - ext_path.clone() - }; - let init = ext_command(Command::new(exe.as_os_str()), logger.clone(), msg_logger.clone(), ctx.clone()).await - .unwrap(); - let ext = Extension::new(init, logger.clone(), msg_logger.clone(), ctx.clone())?; - yield ext + let exe = if cfg!(windows) { ext_path.with_extension("exe") } else { ext_path.clone() }; + let init = + ext_command(Command::new(exe.as_os_str()), logger.clone(), msg_logger.clone(), ctx.clone()) + .await?; + cx.emit(Extension::new(init, logger.clone(), msg_logger.clone(), ctx.clone())?).await; } - } + Ok(cx) + }) } #[tokio::main] @@ -153,13 +149,14 @@ async fn main() -> io::Result { let mut counter = 0; let mut imports = Vec::new(); let usercode_path = sym!(usercode; i).await; + let mut stdin = BufReader::new(stdin()); loop { counter += 1; let (mut root, systems) = init_systems(&args.system, &extensions).await.unwrap(); print!("\\.> "); std::io::stdout().flush().unwrap(); let mut prompt = String::new(); - stdin().read_line(&mut prompt).await.unwrap(); + stdin.read_line(&mut prompt).await.unwrap(); let name = i.i(&format!("_{counter}")).await; let path = usercode_path.suffix([name.clone()], i).await; let mut lexemes = @@ -229,13 +226,13 @@ async fn main() -> io::Result { } } }, - Commands::Execute { proj, code } => { + Commands::Exec { proj, code } => { let reporter = Reporter::new(); let path = sym!(usercode::entrypoint; i).await; let prefix_sr = SrcRange::zw(path.clone(), 0); let (mut root, systems) = init_systems(&args.system, &extensions).await.unwrap(); if let Some(proj_path) = proj { - let path = PathBuf::from(proj_path.into_std_path_buf()); + let path = proj_path.into_std_path_buf(); match parse_folder(&root, path, sym!(src; i).await, &reporter, ctx.clone()).await { Ok(r) => root = r, Err(e) => { diff --git a/orcx/src/parse_folder.rs b/orcx/src/parse_folder.rs index 5376c42..799c955 100644 --- a/orcx/src/parse_folder.rs +++ b/orcx/src/parse_folder.rs @@ -1,10 +1,6 @@ use std::ffi::OsStr; +use std::path::{Path, PathBuf}; -use async_std::fs; -use async_std::fs::File; -use async_std::io::ReadExt; -use async_std::path::{Path, PathBuf}; -use async_std::stream::StreamExt; use futures::FutureExt; use itertools::Itertools; use orchid_base::error::{OrcRes, Reporter, async_io_err, mk_errv, os_str_to_string}; @@ -17,6 +13,8 @@ use orchid_host::parse::{HostParseCtxImpl, parse_items}; use orchid_host::parsed::ParsedModule; use orchid_host::tree::Root; use substack::Substack; +use tokio::fs::{self, File}; +use tokio::io::AsyncReadExt; pub async fn parse_folder( root: &Root, @@ -30,7 +28,7 @@ pub async fn parse_folder( return Ok(root.add_parsed(&parsed_module, ns, rep).await); async fn recur(path: &Path, ns: Sym, rep: &Reporter, ctx: Ctx) -> OrcRes> { let sr = SrcRange::new(0..0, &ns); - if path.is_dir().await { + if path.is_dir() { let Some(name_os) = path.file_name() else { return Err(mk_errv( ctx.i.i("Could not read directory name").await, @@ -46,9 +44,10 @@ pub async fn parse_folder( Err(err) => return Err(async_io_err(err, &ctx.i, [sr]).await), Ok(s) => s, }; - while let Some(entry_res) = stream.next().await { - let entry = match entry_res { - Ok(ent) => ent, + loop { + let entry = match stream.next_entry().await { + Ok(Some(ent)) => ent, + Ok(None) => break, Err(err) => { rep.report(async_io_err(err, &ctx.i, [sr.clone()]).await); continue;