Last active
March 26, 2020 18:37
-
-
Save eddyb/822c658190ccf18058db to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use async::fs::File; | |
use async::io::Async; | |
fn copy(src: &Path, dst: &Path) -> impl Async<()> { | |
let src = try!(File::open(src)); | |
let dst = try!(File::create(dst)); | |
// Copy a kiB at a time. | |
let mut buffer = Vec::with_capacity(1024); | |
loop { | |
buffer = await!(src.read(buffer)); | |
if buffer.is_empty() { break; } | |
buffer = await!(dst.write(buffer)); | |
buffer.clear(); | |
let s_meta = await!(src.metadata()); | |
let d_meta = await!(dst.metadata()); | |
println!("{}B copied out of {}B", d_meta.size, s_meta.size); | |
} | |
Ok(()) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fn copy(src: &Path, dst: &Path) -> impl Async<()> { | |
let src = try!(File::open(src)); | |
let dst = try!(File::create(dst)); | |
// Copy a kiB at a time. | |
let mut buffer = Vec::with_capacity(1024); | |
loop { | |
buffer = try!(yield+ src.read(buffer)); | |
if buffer.is_empty() { break; } | |
buffer = try!(yield+ dst.write(buffer)); | |
buffer.clear(); | |
let s_meta = try!(yield+ src.metadata()); | |
let d_meta = try!(yield+ dst.metadata()); | |
println!("{}B copied out of {}B", d_meta.size, s_meta.size); | |
} | |
Ok(()) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fn copy(src: &Path, dst: &Path) -> impl Async<()> { | |
let src = try!(File::open(src)); | |
let dst = try!(File::create(dst)); | |
// Copy a kiB at a time. | |
let mut buffer = Vec::with_capacity(1024); | |
loop { | |
buffer = try!(match src.read(buffer).generate() { | |
Yield::Value((mut action, mut gen)) => { | |
loop { | |
// NOTE not the case here, but this yield could need | |
// to do polymorphic pass-through to `gen`. | |
match gen.next(yield action) { | |
Yield::Value(next) => action = next, | |
// NOTE cheating via break with value. | |
Yield::Return(res) => break res | |
} | |
} | |
} | |
Yield::Return(res) => res | |
}); | |
if buffer.is_empty() { break; } | |
buffer = try!(match dst.write(buffer).generate() { | |
Yield::Value((mut action, mut gen)) => { | |
loop { | |
match gen.next(yield action) { | |
Yield::Value(next) => action = next, | |
Yield::Return(res) => break res | |
} | |
} | |
} | |
Yield::Return(res) => res | |
}); | |
buffer.clear(); | |
let s_meta = try!(match src.metadata().generate() { | |
Yield::Value((mut action, mut gen)) => { | |
loop { | |
match gen.next(yield action) { | |
Yield::Value(next) => action = next, | |
Yield::Return(res) => break res | |
} | |
} | |
} | |
Yield::Return(res) => res | |
}); | |
let d_meta = try!(match dst.metadata().generate() { | |
Yield::Value((mut action, mut gen)) => { | |
loop { | |
match gen.next(yield action) { | |
Yield::Value(next) => action = next, | |
Yield::Return(res) => break res | |
} | |
} | |
} | |
Yield::Return(res) => res | |
}); | |
println!("{}B copied out of {}B", d_meta.size, s_meta.size); | |
} | |
Ok(()) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fn copy(src: &Path, dst: &Path) -> impl Async<()> { | |
let src = match File::open(src) { | |
Ok(file) => file, | |
Err(e) => return Err(e) | |
}; | |
let dst = match File::create(dst) { | |
Ok(file) => file, | |
Err(e) => return Err(e) | |
}; | |
// Copy a kiB at a time. | |
let mut buffer = Vec::with_capacity(1024); | |
loop { | |
let res = match src.read(buffer).generate() { | |
Yield::Value((mut action, mut gen)) => { | |
loop { | |
// NOTE not the case here, but this yield could need | |
// to do polymorphic pass-through to `gen`. | |
match gen.next(yield action) { | |
Yield::Value(next) => action = next, | |
// NOTE cheating via break with value. | |
Yield::Return(res) => break res | |
} | |
} | |
} | |
Yield::Return(res) => res | |
}; | |
match res { | |
Ok(b) => buffer = b, | |
Err(e) => return Err(e) | |
} | |
if buffer.is_empty() { break; } | |
let res = match dst.write(buffer).generate() { | |
Yield::Value((mut action, mut gen)) => { | |
loop { | |
match gen.next(yield action) { | |
Yield::Value(next) => action = next, | |
Yield::Return(res) => break res | |
} | |
} | |
} | |
Yield::Return(res) => res | |
}; | |
match res { | |
Ok(b) => buffer = b, | |
Err(e) => return Err(e) | |
} | |
buffer.clear(); | |
let res = match src.metadata().generate() { | |
Yield::Value((mut action, mut gen)) => { | |
loop { | |
match gen.next(yield action) { | |
Yield::Value(next) => action = next, | |
Yield::Return(res) => break res | |
} | |
} | |
} | |
Yield::Return(res) => res | |
}; | |
let s_meta = match res { | |
Ok(x) => x, | |
Err(e) => return Err(e) | |
}; | |
let res = match dst.metadata().generate() { | |
Yield::Value((mut action, mut gen)) => { | |
loop { | |
match gen.next(yield action) { | |
Yield::Value(next) => action = next, | |
Yield::Return(res) => break res | |
} | |
} | |
} | |
Yield::Return(res) => res | |
}; | |
let d_meta = match res { | |
Ok(x) => x, | |
Err(e) => return Err(e) | |
}; | |
println!("{}B copied out of {}B", d_meta.size, s_meta.size); | |
} | |
Ok(()) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fn copy(src: &Path, dst: &Path) -> impl Async<()> { | |
let src = match File::open(src) { | |
Ok(file) => file, | |
Err(e) => return Yield::Return(Err(e)) | |
}; | |
let dst = match File::create(dst) { | |
Ok(file) => file, | |
Err(e) => return Yield::Return(Err(e)) | |
}; | |
// Copy a kiB at a time. | |
let mut buffer = Vec::with_capacity(1024); | |
loop { | |
let res = match src.read(buffer).generate() { | |
Yield::Value((mut action, mut gen)) => { | |
loop { | |
return Yield::Value(action); | |
// NOTE input into the generator is denoted with in(T). | |
match gen.next(in(io::Result<Vec<u8>>)) { | |
Yield::Value(next) => action = next, | |
// NOTE cheating via break with value. | |
Yield::Return(res) => break res | |
} | |
} | |
} | |
Yield::Return(res) => res | |
}; | |
match res { | |
Ok(b) => buffer = b, | |
Err(e) => return Yield::Return(Err(e)) | |
} | |
if buffer.is_empty() { return Yield::Return(Ok(())); } | |
let res = match dst.write(buffer).generate() { | |
Yield::Value((mut action, mut gen)) => { | |
loop { | |
return Yield::Value(action); | |
match gen.next(in(io::Result<Vec<u8>>)) { | |
Yield::Value(next) => action = next, | |
Yield::Return(res) => break res | |
} | |
} | |
} | |
Yield::Return(res) => res | |
}; | |
match res { | |
Ok(b) => buffer = b, | |
Err(e) => return Yield::Return(Err(e)) | |
} | |
buffer.clear(); | |
let res = match src.metadata().generate() { | |
Yield::Value((mut action, mut gen)) => { | |
loop { | |
return Yield::Value(action); | |
match gen.next(in(io::Result<fs::Metadata>)) { | |
Yield::Value(next) => action = next, | |
Yield::Return(res) => break res | |
} | |
} | |
} | |
Yield::Return(res) => res | |
}; | |
let s_meta = match res { | |
Ok(x) => x, | |
Err(e) => return Yield::Return(Err(e)) | |
}; | |
let res = match dst.metadata().generate() { | |
Yield::Value((mut action, mut gen)) => { | |
loop { | |
return Yield::Value(action); | |
match gen.next(in(io::Result<fs::Metadata>)) { | |
Yield::Value(next) => action = next, | |
Yield::Return(res) => break res | |
} | |
} | |
} | |
Yield::Return(res) => res | |
}; | |
let d_meta = match res { | |
Ok(x) => x, | |
Err(e) => return Yield::Return(Err(e)) | |
}; | |
println!("{}B copied out of {}B", d_meta.size, s_meta.size); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use gen::{Generate, Generator, GeneratorBase, Yield, Mirror}; | |
use fs::File; | |
use io::Async; | |
use std::mem; | |
use std::path::Path; | |
struct Copy<'a, 'b> { src: &'a Path, dst: &'b Path } | |
fn copy<'a, 'b>(src: &'a Path, dst: &'b Path) -> Copy<'a, 'b> { | |
Copy { src: src, dst: dst } | |
} | |
impl<'a, 'b> Generate for Copy<'a, 'b> { | |
type G = S; | |
// NOTE due to how generators can return immediatelly, | |
// it's theoretically possible to have `copy` finish | |
// without yielding any io::Action (e.g. a sync mock-up). | |
// However, inlining the src.read(buffer).generate() call | |
// from S::advance should kill the rest of that loop here. | |
fn generate(self) -> Yield<(io::Action, S), io::Result<()>> { | |
let Copy { src, dst } = self; | |
let src = match File::open(src) { | |
Ok(file) => file, | |
Err(e) => return Yield::Return(Err(e)) | |
}; | |
let dst = match File::create(dst) { | |
Ok(file) => file, | |
Err(e) => return Yield::Return(Err(e)) | |
}; | |
// Copy a kiB at a time. | |
let buffer = Vec::with_capacity(1024); | |
let mut gen = S::Read { src: src, dst: dst, buffer: buffer }; | |
match gen.advance() { | |
Yield::Value(x) => Yield::Value((x, gen)), | |
Yield::Return(r) => Yield::Return(r) | |
} | |
} | |
} | |
enum S { | |
Read { src: File, dst: File, buffer: Vec<u8> }, | |
ReadY { src: File, dst: File, gen: Mirror<io::Action, io::Result<Vec<u8>>> }, | |
Write { src: File, dst: File, buffer: Vec<u8> }, | |
WriteY{ src: File, dst: File, gen: Mirror<io::Action, io::Result<Vec<u8>>> }, | |
SMeta { src: File, dst: File, buffer: Vec<u8> }, | |
SMetaY{ src: File, dst: File, buffer: Vec<u8>, gen: Mirror<io::Action, io::Result<fs::Metadata>> }, | |
DMeta { src: File, dst: File, buffer: Vec<u8>, s_meta: fs::Metadata }, | |
DMetaY{ src: File, dst: File, buffer: Vec<u8>, s_meta: fs::Metadata, gen: Mirror<io::Action, io::Result<fs::Metadata>> }, | |
Print { src: File, dst: File, buffer: Vec<u8>, s_meta: fs::Metadata, d_meta: fs::Metadata }, | |
Completed | |
} | |
impl GeneratorBase for S { | |
type Value = io::Action; | |
type Return = io::Result<()>; | |
} | |
impl S { | |
// Continue execution until the next yield or return. | |
fn advance(&mut self) -> Yield<io::Action, io::Result<()>> { | |
loop { | |
match mem::replace(self, S::Completed) { | |
S::Read { src, dst, buffer } => { | |
match src.read(buffer).generate() { | |
Yield::Value((action, gen)) => { | |
*self = S::ReadY { src: src, dst: dst, gen: gen }; | |
return Yield::Value(action); | |
} | |
Yield::Return(Err(e)) => return Yield::Return(Err(e)), | |
Yield::Return(Ok(res)) => { | |
*self = S::Write { src: src, dst: dst, buffer: res }; | |
} | |
} | |
} | |
S::Write { src, dst, buffer } => { | |
if buffer.is_empty() { return Yield::Return(Ok(())); } | |
match dst.write(buffer).generate() { | |
Yield::Value((action, gen)) => { | |
*self = S::WriteY { src: src, dst: dst, gen: gen }; | |
return Yield::Value(action); | |
} | |
Yield::Return(Err(e)) => return Yield::Return(Err(e)), | |
Yield::Return(Ok(res)) => { | |
*self = S::SMeta { src: src, dst: dst, buffer: res }; | |
} | |
} | |
} | |
S::SMeta { src, dst, mut buffer } => { | |
buffer.clear(); | |
match src.metadata().generate() { | |
Yield::Value((action, gen)) => { | |
*self = S::SMetaY { src: src, dst: dst, buffer: buffer, gen: gen }; | |
return Yield::Value(action); | |
} | |
Yield::Return(Err(e)) => return Yield::Return(Err(e)), | |
Yield::Return(Ok(res)) => { | |
*self = S::DMeta { src: src, dst: dst, buffer: buffer, s_meta: res }; | |
} | |
} | |
} | |
S::DMeta { src, dst, buffer, s_meta } => { | |
match dst.metadata().generate() { | |
Yield::Value((action, gen)) => { | |
*self = S::DMetaY { src: src, dst: dst, buffer: buffer, s_meta: s_meta, gen: gen }; | |
return Yield::Value(action); | |
} | |
Yield::Return(Err(e)) => return Yield::Return(Err(e)), | |
Yield::Return(Ok(res)) => { | |
*self = S::Print { src: src, dst: dst, buffer: buffer, s_meta: s_meta, d_meta: res }; | |
} | |
} | |
} | |
S::Print { src, dst, buffer, s_meta, d_meta } => { | |
println!("{}B copied out of {}B", d_meta.len(), s_meta.len()); | |
*self = S::Read { src: src, dst: dst, buffer: buffer }; | |
} | |
S::ReadY {..} | S::WriteY {..} | S::SMetaY {..} | S::DMetaY {..} => { | |
unreachable!("generator needs input at yield point") | |
} | |
S::Completed => unreachable!("generator has already completed") | |
} | |
} | |
} | |
} | |
impl Generator<io::Result<Vec<u8>>> for S { | |
fn next(&mut self, input: io::Result<Vec<u8>>) -> Yield<io::Action, io::Result<()>> { | |
match mem::replace(self, S::Completed) { | |
S::ReadY { src, dst, mut gen } => { | |
match gen.next(input) { | |
Yield::Value(next) => { | |
*self = S::ReadY { src: src, dst: dst, gen: gen }; | |
return Yield::Value(next); | |
} | |
Yield::Return(Err(e)) => return Yield::Return(Err(e)), | |
Yield::Return(Ok(res)) => { | |
*self = S::Write { src: src, dst: dst, buffer: res }; | |
} | |
} | |
} | |
S::WriteY { src, dst, mut gen } => { | |
match gen.next(input) { | |
Yield::Value(next) => { | |
*self = S::WriteY { src: src, dst: dst, gen: gen }; | |
return Yield::Value(next); | |
} | |
Yield::Return(Err(e)) => return Yield::Return(Err(e)), | |
Yield::Return(Ok(res)) => { | |
*self = S::SMeta { src: src, dst: dst, buffer: res }; | |
} | |
} | |
} | |
_ => unreachable!("generator state does not accept Vec<u8> as input") | |
} | |
self.advance() | |
} | |
} | |
impl Generator<io::Result<fs::Metadata>> for S { | |
fn next(&mut self, input: io::Result<fs::Metadata>) -> Yield<io::Action, io::Result<()>> { | |
match mem::replace(self, S::Completed) { | |
S::SMetaY { src, dst, buffer, mut gen } => { | |
match gen.next(input) { | |
Yield::Value(next) => { | |
*self = S::SMetaY { src: src, dst: dst, buffer: buffer, gen: gen }; | |
return Yield::Value(next); | |
} | |
Yield::Return(Err(e)) => return Yield::Return(Err(e)), | |
Yield::Return(Ok(res)) => { | |
*self = S::DMeta { src: src, dst: dst, buffer: buffer, s_meta: res }; | |
} | |
} | |
} | |
S::DMetaY { src, dst, buffer, s_meta, mut gen } => { | |
match gen.next(input) { | |
Yield::Value(next) => { | |
*self = S::DMetaY { src: src, dst: dst, buffer: buffer, s_meta: s_meta, gen: gen }; | |
return Yield::Value(next); | |
} | |
Yield::Return(Err(e)) => return Yield::Return(Err(e)), | |
Yield::Return(Ok(res)) => { | |
*self = S::Print { src: src, dst: dst, buffer: buffer, s_meta: s_meta, d_meta: res }; | |
} | |
} | |
} | |
_ => unreachable!("generator state does not accept fs::Metadata as input") | |
} | |
self.advance() | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pub mod gen { | |
use std::marker::PhantomData; | |
#[cfg_attr(std, lang = "yield")] | |
pub enum Yield<T, R> { | |
Value(T), | |
Return(R) | |
} | |
#[cfg_attr(std, lang = "generator_base")] | |
pub trait GeneratorBase { | |
type Value; | |
type Return; | |
} | |
fn type_name<T: ?Sized>() -> &'static str { | |
"<unknown>" | |
/*unsafe { | |
::std::intrinsics::type_name::<T>() | |
}*/ | |
} | |
#[cfg_attr(std, lang = "generator")] | |
pub trait Generator<Input>: GeneratorBase { | |
fn next(&mut self, _: Input) -> Yield<Self::Value, Self::Return> { | |
panic!("generator `{}` does not take `{}` as input", | |
type_name::<Self>(), type_name::<Input>()) | |
} | |
} | |
#[cfg(std)] | |
impl<G: Generator<(), Return=()>> Iterator for G { | |
type Item = G::Value; | |
fn next(&mut self) -> Option<G::Value> { | |
match self.next(()) { | |
Yield::Value(x) => Some(x), | |
Yield::Return(_) => None | |
} | |
} | |
} | |
pub trait Generate { | |
type G: GeneratorBase; | |
/// Run up to the first yield, return that and the generator. | |
fn generate(self) -> Yield<(<Self::G as GeneratorBase>::Value, Self::G), | |
<Self::G as GeneratorBase>::Return>; | |
} | |
pub struct YieldAnd<V, G>(pub V, pub G); | |
impl<V, G: GeneratorBase<Value = V>> Generate for YieldAnd<V, G> { | |
type G = G; | |
fn generate(self) -> Yield<(V, G), G::Return> { | |
Yield::Value((self.0, self.1)) | |
} | |
} | |
pub struct Mirror<V, R> { | |
_data: PhantomData<(V, R)> | |
} | |
impl<V, R> Mirror<V, R> { | |
pub fn new() -> Mirror<V, R> { | |
Mirror { | |
_data: PhantomData | |
} | |
} | |
} | |
impl<V, R> GeneratorBase for Mirror<V, R> { | |
type Value = V; | |
type Return = R; | |
} | |
impl<V, R> Generator<R> for Mirror<V, R> { | |
fn next(&mut self, value: R) -> Yield<V, R> { | |
Yield::Return(value) | |
} | |
} | |
pub type YieldAndMirror<V, R> = YieldAnd<V, Mirror<V, R>>; | |
} | |
pub mod io { | |
use gen::{Generate, GeneratorBase, Yield}; | |
use fs; | |
pub use std::io::{Error, Result}; | |
pub enum Action { | |
File(fs::File, fs::Action) | |
} | |
pub trait Async<R>: Generate | |
where Self::G: GeneratorBase<Value=Action, Return=Result<R>> | |
+ fs::Async { | |
fn run_sync(self) -> Result<R> where Self: Sized { | |
match self.generate() { | |
Yield::Value((mut action, mut gen)) => { | |
loop { | |
match Self::do_io(&mut gen, action) { | |
Yield::Value(next) => action = next, | |
Yield::Return(res) => return res | |
}; | |
} | |
} | |
Yield::Return(res) => res | |
} | |
} | |
fn do_io(gen: &mut Self::G, action: Action) -> Yield<Action, Result<R>> { | |
match action { | |
Action::File(file, action) => fs::Async::do_file(gen, file, action) | |
} | |
} | |
} | |
impl<T: Generate, R> Async<R> for T | |
where T::G: GeneratorBase<Value=Action, Return=Result<R>> | |
+ fs::Async {} | |
macro_rules! await { | |
($x:expr) => (try!(yield+ $x)) | |
} | |
} | |
pub mod fs { | |
use gen::{Generator, Mirror, Yield, YieldAnd, YieldAndMirror}; | |
use io; | |
use std::fs as sync; | |
use std::io::prelude::*; | |
use std::os::unix::prelude::*; | |
use std::path::Path; | |
pub struct File { sync: sync::File } | |
pub use std::fs::Metadata; | |
impl File { | |
pub fn open<P: AsRef<Path>>(path: P) -> io::Result<File> { | |
sync::File::open(path).map(|file| File { sync: file }) | |
} | |
pub fn create<P: AsRef<Path>>(path: P) -> io::Result<File> { | |
sync::File::create(path).map(|file| File { sync: file }) | |
} | |
} | |
impl Clone for File { | |
fn clone(&self) -> File { | |
extern "C" { | |
fn dup(fd: i32) -> i32; | |
} | |
File { | |
sync: unsafe { | |
sync::File::from_raw_fd(dup(self.sync.as_raw_fd())) | |
} | |
} | |
} | |
} | |
pub enum Action { | |
Read(Vec<u8>), | |
Write(Vec<u8>), | |
GetMetadata | |
} | |
pub trait Async: Generator<io::Result<Vec<u8>>> | |
+ Generator<io::Result<sync::Metadata>> { | |
fn do_file(&mut self, mut file: File, action: Action) | |
-> Yield<Self::Value, Self::Return> { | |
match action { | |
Action::Read(mut buffer) => { | |
// Nasty zero-fill because read takes &mut [u8]. | |
let extra = buffer.capacity() - buffer.len(); | |
buffer.extend((0..extra).map(|_| 0)); | |
self.next(file.sync.read(&mut buffer).map(|bytes| { | |
buffer.truncate(bytes); | |
buffer | |
})) | |
} | |
Action::Write(buffer) => { | |
self.next(file.sync.write(&buffer).map(|_| buffer)) | |
} | |
Action::GetMetadata => { | |
self.next(file.sync.metadata()) | |
} | |
} | |
} | |
} | |
impl<G> Async for G | |
where G: Generator<io::Result<Vec<u8>>> | |
+ Generator<io::Result<Metadata>> {} | |
macro_rules! action { | |
($fun:ident => $act:ident -> $ret:ty) => { | |
pub fn $fun(&self) -> YieldAndMirror<io::Action, io::Result<$ret>> { | |
YieldAnd(io::Action::File(self.clone(), Action::$act), Mirror::new()) | |
} | |
}; | |
($fun:ident => $act:ident($($arg:ident: $ty:ty),+) -> $ret:ty) => { | |
pub fn $fun(&self, $($arg: $ty),+) -> YieldAndMirror<io::Action, io::Result<$ret>> { | |
YieldAnd(io::Action::File(self.clone(), Action::$act($($arg),+)), Mirror::new()) | |
} | |
} | |
} | |
impl File { | |
action!(read => Read(buffer: Vec<u8>) -> Vec<u8>); | |
action!(write => Write(buffer: Vec<u8>) -> Vec<u8>); | |
action!(metadata => GetMetadata -> Metadata); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://play.rust-lang.org/?gist=a5c2d92666cd3b39dac9&run=1