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);
    }
}