<commit_before>//! Asynchronous I/O
//!
//! This crate contains the `AsyncRead` and `AsyncWrite` traits, the
//! asynchronous analogs to `std::io::{Read, Write}`. The primary difference is
//! that these traits integrate with the asynchronous task system.

#![cfg_attr(not(feature = "std"), no_std)]

#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)]

#![doc(html_root_url = "https://rust-lang-nursery.github.io/futures-api-docs/0.3.0-alpha.13/futures_io")]

#![feature(futures_api)]

#[cfg(feature = "std")]
mod if_std {
    use futures_core::task::{Waker, Poll};
    use std::boxed::Box;
    use std::cmp;
    use std::io as StdIo;
    use std::pin::Pin;
    use std::ptr;

    // Re-export IoVec for convenience
    pub use iovec::IoVec;

    // Re-export io::Error so that users don't have to deal
    // with conflicts when `use`ing `futures::io` and `std::io`.
    pub use self::StdIo::Error as Error;
    pub use self::StdIo::ErrorKind as ErrorKind;
    pub use self::StdIo::Result as Result;

    /// A type used to conditionally initialize buffers passed to `AsyncRead`
    /// methods, modeled after `std`.
    #[derive(Debug)]
    pub struct Initializer(bool);

    impl Initializer {
        /// Returns a new `Initializer` which will zero out buffers.
        #[inline]
        pub fn zeroing() -> Initializer {
            Initializer(true)
        }

        /// Returns a new `Initializer` which will not zero out buffers.
        ///
        /// # Safety
        ///
        /// This method may only be called by `AsyncRead`ers which guarantee
        /// that they will not read from the buffers passed to `AsyncRead`
        /// methods, and that the return value of the method accurately reflects
        /// the number of bytes that have been written to the head of the buffer.
        #[inline]
        pub unsafe fn nop() -> Initializer {
            Initializer(false)
        }

        /// Indicates if a buffer should be initialized.
        #[inline]
        pub fn should_initialize(&self) -> bool {
            self.0
        }

        /// Initializes a buffer if necessary.
        #[inline]
        pub fn initialize(&self, buf: &mut [u8]) {
            if self.should_initialize() {
                unsafe { ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len()) }
            }
        }
    }

    /// Read bytes asynchronously.
    ///
    /// This trait is analogous to the `std::io::Read` trait, but integrates
    /// with the asynchronous task system. In particular, the `poll_read`
    /// method, unlike `Read::read`, will automatically queue the current task
    /// for wakeup and return if data is not yet available, rather than blocking
    /// the calling thread.
    pub trait AsyncRead {
        /// Determines if this `AsyncRead`er can work with buffers of
        /// uninitialized memory.
        ///
        /// The default implementation returns an initializer which will zero
        /// buffers.
        ///
        /// # Safety
        ///
        /// This method is `unsafe` because and `AsyncRead`er could otherwise
        /// return a non-zeroing `Initializer` from another `AsyncRead` type
        /// without an `unsafe` block.
        #[inline]
        unsafe fn initializer(&self) -> Initializer {
            Initializer::zeroing()
        }

        /// Attempt to read from the `AsyncRead` into `buf`.
        ///
        /// On success, returns `Ok(Poll::Ready(num_bytes_read))`.
        ///
        /// If no data is available for reading, the method returns
        /// `Ok(Poll::Pending)` and arranges for the current task (via
        /// `waker.wake()`) to receive a notification when the object becomes
        /// readable or is closed.
        ///
        /// # Implementation
        ///
        /// This function may not return errors of kind `WouldBlock` or
        /// `Interrupted`.  Implementations must convert `WouldBlock` into
        /// `Poll::Pending` and either internally retry or convert
        /// `Interrupted` into another error kind.
        fn poll_read(self: Pin<&mut Self>, waker: &Waker, buf: &mut [u8])
            -> Poll<Result<usize>>;

        /// Attempt to read from the `AsyncRead` into `vec` using vectored
        /// IO operations.
        ///
        /// This method is similar to `poll_read`, but allows data to be read
        /// into multiple buffers using a single operation.
        ///
        /// On success, returns `Ok(Poll::Ready(num_bytes_read))`.
        ///
        /// If no data is available for reading, the method returns
        /// `Ok(Poll::Pending)` and arranges for the current task (via
        /// `waker.wake()`) to receive a notification when the object becomes
        /// readable or is closed.
        /// By default, this method delegates to using `poll_read` on the first
        /// buffer in `vec`. Objects which support vectored IO should override
        /// this method.
        ///
        /// # Implementation
        ///
        /// This function may not return errors of kind `WouldBlock` or
        /// `Interrupted`.  Implementations must convert `WouldBlock` into
        /// `Poll::Pending` and either internally retry or convert
        /// `Interrupted` into another error kind.
        fn poll_vectored_read(self: Pin<&mut Self>, waker: &Waker, vec: &mut [&mut IoVec])
            -> Poll<Result<usize>>
        {
            if let Some(ref mut first_iovec) = vec.get_mut(0) {
                self.poll_read(waker, first_iovec)
            } else {
                // `vec` is empty.
                Poll::Ready(Ok(0))
            }
        }
    }

    /// Write bytes asynchronously.
    ///
    /// This trait is analogous to the `std::io::Write` trait, but integrates
    /// with the asynchronous task system. In particular, the `poll_write`
    /// method, unlike `Write::write`, will automatically queue the current task
    /// for wakeup and return if data is not yet available, rather than blocking
    /// the calling thread.
    pub trait AsyncWrite {
        /// Attempt to write bytes from `buf` into the object.
        ///
        /// On success, returns `Ok(Poll::Ready(num_bytes_written))`.
        ///
        /// If the object is not ready for writing, the method returns
        /// `Ok(Poll::Pending)` and arranges for the current task (via
        /// `waker.wake()`) to receive a notification when the object becomes
        /// readable or is closed.
        ///
        /// # Implementation
        ///
        /// This function may not return errors of kind `WouldBlock` or
        /// `Interrupted`.  Implementations must convert `WouldBlock` into
        /// `Poll::Pending` and either internally retry or convert
        /// `Interrupted` into another error kind.
        fn poll_write(self: Pin<&mut Self>, waker: &Waker, buf: &[u8])
            -> Poll<Result<usize>>;

        /// Attempt to write bytes from `vec` into the object using vectored
        /// IO operations.
        ///
        /// This method is similar to `poll_write`, but allows data from multiple buffers to be written
        /// using a single operation.
        ///
        /// On success, returns `Ok(Poll::Ready(num_bytes_written))`.
        ///
        /// If the object is not ready for writing, the method returns
        /// `Ok(Poll::Pending)` and arranges for the current task (via
        /// `waker.wake()`) to receive a notification when the object becomes
        /// readable or is closed.
        ///
        /// By default, this method delegates to using `poll_write` on the first
        /// buffer in `vec`. Objects which support vectored IO should override
        /// this method.
        ///
        /// # Implementation
        ///
        /// This function may not return errors of kind `WouldBlock` or
        /// `Interrupted`.  Implementations must convert `WouldBlock` into
        /// `Poll::Pending` and either internally retry or convert
        /// `Interrupted` into another error kind.
        fn poll_vectored_write(self: Pin<&mut Self>, waker: &Waker, vec: &[&IoVec])
            -> Poll<Result<usize>>
        {
            if let Some(ref first_iovec) = vec.get(0) {
                self.poll_write(waker, &*first_iovec)
            } else {
                // `vec` is empty.
                Poll::Ready(Ok(0))
            }
        }

        /// Attempt to flush the object, ensuring that any buffered data reach
        /// their destination.
        ///
        /// On success, returns `Ok(Poll::Ready(()))`.
        ///
        /// If flushing cannot immediately complete, this method returns
        /// `Ok(Poll::Pending)` and arranges for the current task (via
        /// `waker.wake()`) to receive a notification when the object can make
        /// progress towards flushing.
        ///
        /// # Implementation
        ///
        /// This function may not return errors of kind `WouldBlock` or
        /// `Interrupted`.  Implementations must convert `WouldBlock` into
        /// `Poll::Pending` and either internally retry or convert
        /// `Interrupted` into another error kind.
        fn poll_flush(self: Pin<&mut Self>, waker: &Waker) -> Poll<Result<()>>;

        /// Attempt to close the object.
        ///
        /// On success, returns `Ok(Poll::Ready(()))`.
        ///
        /// If closing cannot immediately complete, this function returns
        /// `Ok(Poll::Pending)` and arranges for the current task (via
        /// `waker.wake()`) to receive a notification when the object can make
        /// progress towards closing.
        ///
        /// # Implementation
        ///
        /// This function may not return errors of kind `WouldBlock` or
        /// `Interrupted`.  Implementations must convert `WouldBlock` into
        /// `Poll::Pending` and either internally retry or convert
        /// `Interrupted` into another error kind.
        fn poll_close(self: Pin<&mut Self>, waker: &Waker) -> Poll<Result<()>>;
    }

    macro_rules! deref_async_read {
        () => {
            unsafe fn initializer(&self) -> Initializer {
                (**self).initializer()
            }

            fn poll_read(mut self: Pin<&mut Self>, waker: &Waker, buf: &mut [u8])
                -> Poll<Result<usize>>
            {
                Pin::new(&mut **self).poll_read(waker, buf)
            }

            fn poll_vectored_read(mut self: Pin<&mut Self>, waker: &Waker, vec: &mut [&mut IoVec])
                -> Poll<Result<usize>>
            {
                Pin::new(&mut **self).poll_vectored_read(waker, vec)
            }
        }
    }

    impl<T: ?Sized + AsyncRead + Unpin> AsyncRead for Box<T> {
        deref_async_read!();
    }

    impl<'a, T: ?Sized + AsyncRead + Unpin> AsyncRead for &'a mut T {
        deref_async_read!();
    }

    impl<'a, T: ?Sized + AsyncRead> AsyncRead for Pin<&'a mut T> {
        unsafe fn initializer(&self) -> Initializer {
            (**self).initializer()
        }

        fn poll_read(mut self: Pin<&mut Self>, waker: &Waker, buf: &mut [u8])
            -> Poll<Result<usize>>
        {
            T::poll_read((*self).as_mut(), waker, buf)
        }

        fn poll_vectored_read(mut self: Pin<&mut Self>, waker: &Waker, vec: &mut [&mut IoVec])
            -> Poll<Result<usize>>
        {
            T::poll_vectored_read((*self).as_mut(), waker, vec)
        }
    }

    /// `unsafe` because the `StdIo::Read` type must not access the buffer
    /// before reading data into it.
    macro_rules! unsafe_delegate_async_read_to_stdio {
        () => {
            unsafe fn initializer(&self) -> Initializer {
                Initializer::nop()
            }

            fn poll_read(mut self: Pin<&mut Self>, _: &Waker, buf: &mut [u8])
                -> Poll<Result<usize>>
            {
                Poll::Ready(StdIo::Read::read(&mut *self, buf))
            }
        }
    }

    impl<'a> AsyncRead for &'a [u8] {
        unsafe_delegate_async_read_to_stdio!();
    }

    impl AsyncRead for StdIo::Repeat {
        unsafe_delegate_async_read_to_stdio!();
    }

    impl<T: AsRef<[u8]> + Unpin> AsyncRead for StdIo::Cursor<T> {
        unsafe_delegate_async_read_to_stdio!();
    }

    macro_rules! deref_async_write {
        () => {
            fn poll_write(mut self: Pin<&mut Self>, waker: &Waker, buf: &[u8])
                -> Poll<Result<usize>>
            {
                Pin::new(&mut **self).poll_write(waker, buf)
            }

            fn poll_vectored_write(mut self: Pin<&mut Self>, waker: &Waker, vec: &[&IoVec])
                -> Poll<Result<usize>>
            {
                Pin::new(&mut **self).poll_vectored_write(waker, vec)
            }

            fn poll_flush(mut self: Pin<&mut Self>, waker: &Waker) -> Poll<Result<()>> {
                Pin::new(&mut **self).poll_flush(waker)
            }

            fn poll_close(mut self: Pin<&mut Self>, waker: &Waker) -> Poll<Result<()>> {
                Pin::new(&mut **self).poll_close(waker)
            }
        }
    }

    impl<T: ?Sized + AsyncWrite + Unpin> AsyncWrite for Box<T> {
        deref_async_write!();
    }

    impl<'a, T: ?Sized + AsyncWrite + Unpin> AsyncWrite for &'a mut T {
        deref_async_write!();
    }

    impl<'a, T: ?Sized + AsyncWrite> AsyncWrite for Pin<&'a mut T> {
        fn poll_write(mut self: Pin<&mut Self>, waker: &Waker, buf: &[u8])
            -> Poll<Result<usize>>
        {
            T::poll_write((*self).as_mut(), waker, buf)
        }

        fn poll_vectored_write(mut self: Pin<&mut Self>, waker: &Waker, vec: &[&IoVec])
            -> Poll<Result<usize>>
        {
            T::poll_vectored_write((*self).as_mut(), waker, vec)
        }

        fn poll_flush(mut self: Pin<&mut Self>, waker: &Waker) -> Poll<Result<()>> {
            T::poll_flush((*self).as_mut(), waker)
        }

        fn poll_close(mut self: Pin<&mut Self>, waker: &Waker) -> Poll<Result<()>> {
            T::poll_close((*self).as_mut(), waker)
        }
    }

    macro_rules! delegate_async_write_to_stdio {
        () => {
            fn poll_write(mut self: Pin<&mut Self>, _: &Waker, buf: &[u8])
                -> Poll<Result<usize>>
            {
                Poll::Ready(StdIo::Write::write(&mut *self, buf))
            }

            fn poll_flush(mut self: Pin<&mut Self>, _: &Waker) -> Poll<Result<()>> {
                Poll::Ready(StdIo::Write::flush(&mut *self))
            }

            fn poll_close(self: Pin<&mut Self>, waker: &Waker) -> Poll<Result<()>> {
                self.poll_flush(waker)
            }
        }
    }

    impl<T: AsMut<[u8]> + Unpin> AsyncWrite for StdIo::Cursor<T> {
        fn poll_write(
            mut self: Pin<&mut Self>,
            _: &Waker,
            buf: &[u8],
        ) -> Poll<Result<usize>> {
            let position = self.position();
            let result = {
                let out = (&mut *self).get_mut().as_mut();
                let pos = cmp::min(out.len() as u64, position) as usize;
                StdIo::Write::write(&mut &mut out[pos..], buf)
            };
            if let Ok(offset) = result {
                self.get_mut().set_position(position + offset as u64);
            }
            Poll::Ready(result)
        }

        fn poll_flush(self: Pin<&mut Self>, _: &Waker) -> Poll<Result<()>> {
            Poll::Ready(StdIo::Write::flush(&mut self.get_mut().get_mut().as_mut()))
        }

        fn poll_close(self: Pin<&mut Self>, waker: &Waker) -> Poll<Result<()>> {
            self.poll_flush(waker)
        }
    }

    impl AsyncWrite for StdIo::Sink {
        delegate_async_write_to_stdio!();
    }
}

#[cfg(feature = "std")]
pub use self::if_std::*;
<commit_msg>impl AsyncWrite for Vec<u8><commit_after>//! Asynchronous I/O
//!
//! This crate contains the `AsyncRead` and `AsyncWrite` traits, the
//! asynchronous analogs to `std::io::{Read, Write}`. The primary difference is
//! that these traits integrate with the asynchronous task system.

#![cfg_attr(not(feature = "std"), no_std)]

#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)]

#![doc(html_root_url = "https://rust-lang-nursery.github.io/futures-api-docs/0.3.0-alpha.13/futures_io")]

#![feature(futures_api)]

#[cfg(feature = "std")]
mod if_std {
    use futures_core::task::{Waker, Poll};
    use std::boxed::Box;
    use std::cmp;
    use std::io as StdIo;
    use std::pin::Pin;
    use std::ptr;

    // Re-export IoVec for convenience
    pub use iovec::IoVec;

    // Re-export io::Error so that users don't have to deal
    // with conflicts when `use`ing `futures::io` and `std::io`.
    pub use self::StdIo::Error as Error;
    pub use self::StdIo::ErrorKind as ErrorKind;
    pub use self::StdIo::Result as Result;

    /// A type used to conditionally initialize buffers passed to `AsyncRead`
    /// methods, modeled after `std`.
    #[derive(Debug)]
    pub struct Initializer(bool);

    impl Initializer {
        /// Returns a new `Initializer` which will zero out buffers.
        #[inline]
        pub fn zeroing() -> Initializer {
            Initializer(true)
        }

        /// Returns a new `Initializer` which will not zero out buffers.
        ///
        /// # Safety
        ///
        /// This method may only be called by `AsyncRead`ers which guarantee
        /// that they will not read from the buffers passed to `AsyncRead`
        /// methods, and that the return value of the method accurately reflects
        /// the number of bytes that have been written to the head of the buffer.
        #[inline]
        pub unsafe fn nop() -> Initializer {
            Initializer(false)
        }

        /// Indicates if a buffer should be initialized.
        #[inline]
        pub fn should_initialize(&self) -> bool {
            self.0
        }

        /// Initializes a buffer if necessary.
        #[inline]
        pub fn initialize(&self, buf: &mut [u8]) {
            if self.should_initialize() {
                unsafe { ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len()) }
            }
        }
    }

    /// Read bytes asynchronously.
    ///
    /// This trait is analogous to the `std::io::Read` trait, but integrates
    /// with the asynchronous task system. In particular, the `poll_read`
    /// method, unlike `Read::read`, will automatically queue the current task
    /// for wakeup and return if data is not yet available, rather than blocking
    /// the calling thread.
    pub trait AsyncRead {
        /// Determines if this `AsyncRead`er can work with buffers of
        /// uninitialized memory.
        ///
        /// The default implementation returns an initializer which will zero
        /// buffers.
        ///
        /// # Safety
        ///
        /// This method is `unsafe` because and `AsyncRead`er could otherwise
        /// return a non-zeroing `Initializer` from another `AsyncRead` type
        /// without an `unsafe` block.
        #[inline]
        unsafe fn initializer(&self) -> Initializer {
            Initializer::zeroing()
        }

        /// Attempt to read from the `AsyncRead` into `buf`.
        ///
        /// On success, returns `Ok(Poll::Ready(num_bytes_read))`.
        ///
        /// If no data is available for reading, the method returns
        /// `Ok(Poll::Pending)` and arranges for the current task (via
        /// `waker.wake()`) to receive a notification when the object becomes
        /// readable or is closed.
        ///
        /// # Implementation
        ///
        /// This function may not return errors of kind `WouldBlock` or
        /// `Interrupted`.  Implementations must convert `WouldBlock` into
        /// `Poll::Pending` and either internally retry or convert
        /// `Interrupted` into another error kind.
        fn poll_read(self: Pin<&mut Self>, waker: &Waker, buf: &mut [u8])
            -> Poll<Result<usize>>;

        /// Attempt to read from the `AsyncRead` into `vec` using vectored
        /// IO operations.
        ///
        /// This method is similar to `poll_read`, but allows data to be read
        /// into multiple buffers using a single operation.
        ///
        /// On success, returns `Ok(Poll::Ready(num_bytes_read))`.
        ///
        /// If no data is available for reading, the method returns
        /// `Ok(Poll::Pending)` and arranges for the current task (via
        /// `waker.wake()`) to receive a notification when the object becomes
        /// readable or is closed.
        /// By default, this method delegates to using `poll_read` on the first
        /// buffer in `vec`. Objects which support vectored IO should override
        /// this method.
        ///
        /// # Implementation
        ///
        /// This function may not return errors of kind `WouldBlock` or
        /// `Interrupted`.  Implementations must convert `WouldBlock` into
        /// `Poll::Pending` and either internally retry or convert
        /// `Interrupted` into another error kind.
        fn poll_vectored_read(self: Pin<&mut Self>, waker: &Waker, vec: &mut [&mut IoVec])
            -> Poll<Result<usize>>
        {
            if let Some(ref mut first_iovec) = vec.get_mut(0) {
                self.poll_read(waker, first_iovec)
            } else {
                // `vec` is empty.
                Poll::Ready(Ok(0))
            }
        }
    }

    /// Write bytes asynchronously.
    ///
    /// This trait is analogous to the `std::io::Write` trait, but integrates
    /// with the asynchronous task system. In particular, the `poll_write`
    /// method, unlike `Write::write`, will automatically queue the current task
    /// for wakeup and return if data is not yet available, rather than blocking
    /// the calling thread.
    pub trait AsyncWrite {
        /// Attempt to write bytes from `buf` into the object.
        ///
        /// On success, returns `Ok(Poll::Ready(num_bytes_written))`.
        ///
        /// If the object is not ready for writing, the method returns
        /// `Ok(Poll::Pending)` and arranges for the current task (via
        /// `waker.wake()`) to receive a notification when the object becomes
        /// readable or is closed.
        ///
        /// # Implementation
        ///
        /// This function may not return errors of kind `WouldBlock` or
        /// `Interrupted`.  Implementations must convert `WouldBlock` into
        /// `Poll::Pending` and either internally retry or convert
        /// `Interrupted` into another error kind.
        fn poll_write(self: Pin<&mut Self>, waker: &Waker, buf: &[u8])
            -> Poll<Result<usize>>;

        /// Attempt to write bytes from `vec` into the object using vectored
        /// IO operations.
        ///
        /// This method is similar to `poll_write`, but allows data from multiple buffers to be written
        /// using a single operation.
        ///
        /// On success, returns `Ok(Poll::Ready(num_bytes_written))`.
        ///
        /// If the object is not ready for writing, the method returns
        /// `Ok(Poll::Pending)` and arranges for the current task (via
        /// `waker.wake()`) to receive a notification when the object becomes
        /// readable or is closed.
        ///
        /// By default, this method delegates to using `poll_write` on the first
        /// buffer in `vec`. Objects which support vectored IO should override
        /// this method.
        ///
        /// # Implementation
        ///
        /// This function may not return errors of kind `WouldBlock` or
        /// `Interrupted`.  Implementations must convert `WouldBlock` into
        /// `Poll::Pending` and either internally retry or convert
        /// `Interrupted` into another error kind.
        fn poll_vectored_write(self: Pin<&mut Self>, waker: &Waker, vec: &[&IoVec])
            -> Poll<Result<usize>>
        {
            if let Some(ref first_iovec) = vec.get(0) {
                self.poll_write(waker, &*first_iovec)
            } else {
                // `vec` is empty.
                Poll::Ready(Ok(0))
            }
        }

        /// Attempt to flush the object, ensuring that any buffered data reach
        /// their destination.
        ///
        /// On success, returns `Ok(Poll::Ready(()))`.
        ///
        /// If flushing cannot immediately complete, this method returns
        /// `Ok(Poll::Pending)` and arranges for the current task (via
        /// `waker.wake()`) to receive a notification when the object can make
        /// progress towards flushing.
        ///
        /// # Implementation
        ///
        /// This function may not return errors of kind `WouldBlock` or
        /// `Interrupted`.  Implementations must convert `WouldBlock` into
        /// `Poll::Pending` and either internally retry or convert
        /// `Interrupted` into another error kind.
        fn poll_flush(self: Pin<&mut Self>, waker: &Waker) -> Poll<Result<()>>;

        /// Attempt to close the object.
        ///
        /// On success, returns `Ok(Poll::Ready(()))`.
        ///
        /// If closing cannot immediately complete, this function returns
        /// `Ok(Poll::Pending)` and arranges for the current task (via
        /// `waker.wake()`) to receive a notification when the object can make
        /// progress towards closing.
        ///
        /// # Implementation
        ///
        /// This function may not return errors of kind `WouldBlock` or
        /// `Interrupted`.  Implementations must convert `WouldBlock` into
        /// `Poll::Pending` and either internally retry or convert
        /// `Interrupted` into another error kind.
        fn poll_close(self: Pin<&mut Self>, waker: &Waker) -> Poll<Result<()>>;
    }

    macro_rules! deref_async_read {
        () => {
            unsafe fn initializer(&self) -> Initializer {
                (**self).initializer()
            }

            fn poll_read(mut self: Pin<&mut Self>, waker: &Waker, buf: &mut [u8])
                -> Poll<Result<usize>>
            {
                Pin::new(&mut **self).poll_read(waker, buf)
            }

            fn poll_vectored_read(mut self: Pin<&mut Self>, waker: &Waker, vec: &mut [&mut IoVec])
                -> Poll<Result<usize>>
            {
                Pin::new(&mut **self).poll_vectored_read(waker, vec)
            }
        }
    }

    impl<T: ?Sized + AsyncRead + Unpin> AsyncRead for Box<T> {
        deref_async_read!();
    }

    impl<'a, T: ?Sized + AsyncRead + Unpin> AsyncRead for &'a mut T {
        deref_async_read!();
    }

    impl<'a, T: ?Sized + AsyncRead> AsyncRead for Pin<&'a mut T> {
        unsafe fn initializer(&self) -> Initializer {
            (**self).initializer()
        }

        fn poll_read(mut self: Pin<&mut Self>, waker: &Waker, buf: &mut [u8])
            -> Poll<Result<usize>>
        {
            T::poll_read((*self).as_mut(), waker, buf)
        }

        fn poll_vectored_read(mut self: Pin<&mut Self>, waker: &Waker, vec: &mut [&mut IoVec])
            -> Poll<Result<usize>>
        {
            T::poll_vectored_read((*self).as_mut(), waker, vec)
        }
    }

    /// `unsafe` because the `StdIo::Read` type must not access the buffer
    /// before reading data into it.
    macro_rules! unsafe_delegate_async_read_to_stdio {
        () => {
            unsafe fn initializer(&self) -> Initializer {
                Initializer::nop()
            }

            fn poll_read(mut self: Pin<&mut Self>, _: &Waker, buf: &mut [u8])
                -> Poll<Result<usize>>
            {
                Poll::Ready(StdIo::Read::read(&mut *self, buf))
            }
        }
    }

    impl<'a> AsyncRead for &'a [u8] {
        unsafe_delegate_async_read_to_stdio!();
    }

    impl AsyncRead for StdIo::Repeat {
        unsafe_delegate_async_read_to_stdio!();
    }

    impl<T: AsRef<[u8]> + Unpin> AsyncRead for StdIo::Cursor<T> {
        unsafe_delegate_async_read_to_stdio!();
    }

    macro_rules! deref_async_write {
        () => {
            fn poll_write(mut self: Pin<&mut Self>, waker: &Waker, buf: &[u8])
                -> Poll<Result<usize>>
            {
                Pin::new(&mut **self).poll_write(waker, buf)
            }

            fn poll_vectored_write(mut self: Pin<&mut Self>, waker: &Waker, vec: &[&IoVec])
                -> Poll<Result<usize>>
            {
                Pin::new(&mut **self).poll_vectored_write(waker, vec)
            }

            fn poll_flush(mut self: Pin<&mut Self>, waker: &Waker) -> Poll<Result<()>> {
                Pin::new(&mut **self).poll_flush(waker)
            }

            fn poll_close(mut self: Pin<&mut Self>, waker: &Waker) -> Poll<Result<()>> {
                Pin::new(&mut **self).poll_close(waker)
            }
        }
    }

    impl<T: ?Sized + AsyncWrite + Unpin> AsyncWrite for Box<T> {
        deref_async_write!();
    }

    impl<'a, T: ?Sized + AsyncWrite + Unpin> AsyncWrite for &'a mut T {
        deref_async_write!();
    }

    impl<'a, T: ?Sized + AsyncWrite> AsyncWrite for Pin<&'a mut T> {
        fn poll_write(mut self: Pin<&mut Self>, waker: &Waker, buf: &[u8])
            -> Poll<Result<usize>>
        {
            T::poll_write((*self).as_mut(), waker, buf)
        }

        fn poll_vectored_write(mut self: Pin<&mut Self>, waker: &Waker, vec: &[&IoVec])
            -> Poll<Result<usize>>
        {
            T::poll_vectored_write((*self).as_mut(), waker, vec)
        }

        fn poll_flush(mut self: Pin<&mut Self>, waker: &Waker) -> Poll<Result<()>> {
            T::poll_flush((*self).as_mut(), waker)
        }

        fn poll_close(mut self: Pin<&mut Self>, waker: &Waker) -> Poll<Result<()>> {
            T::poll_close((*self).as_mut(), waker)
        }
    }

    macro_rules! delegate_async_write_to_stdio {
        () => {
            fn poll_write(mut self: Pin<&mut Self>, _: &Waker, buf: &[u8])
                -> Poll<Result<usize>>
            {
                Poll::Ready(StdIo::Write::write(&mut *self, buf))
            }

            fn poll_flush(mut self: Pin<&mut Self>, _: &Waker) -> Poll<Result<()>> {
                Poll::Ready(StdIo::Write::flush(&mut *self))
            }

            fn poll_close(self: Pin<&mut Self>, waker: &Waker) -> Poll<Result<()>> {
                self.poll_flush(waker)
            }
        }
    }

    impl<T: AsMut<[u8]> + Unpin> AsyncWrite for StdIo::Cursor<T> {
        fn poll_write(
            mut self: Pin<&mut Self>,
            _: &Waker,
            buf: &[u8],
        ) -> Poll<Result<usize>> {
            let position = self.position();
            let result = {
                let out = (&mut *self).get_mut().as_mut();
                let pos = cmp::min(out.len() as u64, position) as usize;
                StdIo::Write::write(&mut &mut out[pos..], buf)
            };
            if let Ok(offset) = result {
                self.get_mut().set_position(position + offset as u64);
            }
            Poll::Ready(result)
        }

        fn poll_flush(self: Pin<&mut Self>, _: &Waker) -> Poll<Result<()>> {
            Poll::Ready(StdIo::Write::flush(&mut self.get_mut().get_mut().as_mut()))
        }

        fn poll_close(self: Pin<&mut Self>, waker: &Waker) -> Poll<Result<()>> {
            self.poll_flush(waker)
        }
    }

    impl AsyncWrite for Vec<u8> {
        delegate_async_write_to_stdio!();
    }

    impl AsyncWrite for StdIo::Sink {
        delegate_async_write_to_stdio!();
    }
}

#[cfg(feature = "std")]
pub use self::if_std::*;
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Generic types for the handling of [images].
//!
//! [images]: https://drafts.csswg.org/css-images/#image-values

use Atom;
use cssparser::serialize_identifier;
use custom_properties;
use servo_arc::Arc;
use std::fmt;
use style_traits::ToCss;

/// An [image].
///
/// [image]: https://drafts.csswg.org/css-images/#image-values
#[derive(Clone, MallocSizeOf, PartialEq, ToComputedValue)]
pub enum Image<Gradient, MozImageRect, ImageUrl> {
    /// A `<url()>` image.
    Url(ImageUrl),
    /// A `<gradient>` image.  Gradients are rather large, and not nearly as
    /// common as urls, so we box them here to keep the size of this enum sane.
    Gradient(Box<Gradient>),
    /// A `-moz-image-rect` image.  Also fairly large and rare.
    Rect(Box<MozImageRect>),
    /// A `-moz-element(# <element-id>)`
    Element(Atom),
    /// A paint worklet image.
    /// <https://drafts.css-houdini.org/css-paint-api/>
    #[cfg(feature = "servo")]
    PaintWorklet(PaintWorklet),
}

/// A CSS gradient.
/// <https://drafts.csswg.org/css-images/#gradients>
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
pub struct Gradient<LineDirection, Length, LengthOrPercentage, Position, Color, Angle> {
    /// Gradients can be linear or radial.
    pub kind: GradientKind<LineDirection, Length, LengthOrPercentage, Position, Angle>,
    /// The color stops and interpolation hints.
    pub items: Vec<GradientItem<Color, LengthOrPercentage>>,
    /// True if this is a repeating gradient.
    #[compute(clone)]
    pub repeating: bool,
    /// Compatibility mode.
    #[compute(clone)]
    pub compat_mode: CompatMode,
}

#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
/// Whether we used the modern notation or the compatibility `-webkit`, `-moz` prefixes.
pub enum CompatMode {
    /// Modern syntax.
    Modern,
    /// `-webkit` prefix.
    WebKit,
    /// `-moz` prefix
    Moz,
}

/// A gradient kind.
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
pub enum GradientKind<LineDirection, Length, LengthOrPercentage, Position, Angle> {
    /// A linear gradient.
    Linear(LineDirection),
    /// A radial gradient.
    Radial(EndingShape<Length, LengthOrPercentage>, Position, Option<Angle>),
}

/// A radial gradient's ending shape.
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
pub enum EndingShape<Length, LengthOrPercentage> {
    /// A circular gradient.
    Circle(Circle<Length>),
    /// An elliptic gradient.
    Ellipse(Ellipse<LengthOrPercentage>),
}

/// A circle shape.
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
pub enum Circle<Length> {
    /// A circle radius.
    Radius(Length),
    /// A circle extent.
    Extent(ShapeExtent),
}

/// An ellipse shape.
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
pub enum Ellipse<LengthOrPercentage> {
    /// An ellipse pair of radii.
    Radii(LengthOrPercentage, LengthOrPercentage),
    /// An ellipse extent.
    Extent(ShapeExtent),
}

/// <https://drafts.csswg.org/css-images/#typedef-extent-keyword>
define_css_keyword_enum!(ShapeExtent:
    "closest-side" => ClosestSide,
    "farthest-side" => FarthestSide,
    "closest-corner" => ClosestCorner,
    "farthest-corner" => FarthestCorner,
    "contain" => Contain,
    "cover" => Cover
);
add_impls_for_keyword_enum!(ShapeExtent);

/// A gradient item.
/// <https://drafts.csswg.org/css-images-4/#color-stop-syntax>
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
pub enum GradientItem<Color, LengthOrPercentage> {
    /// A color stop.
    ColorStop(ColorStop<Color, LengthOrPercentage>),
    /// An interpolation hint.
    InterpolationHint(LengthOrPercentage),
}

/// A color stop.
/// <https://drafts.csswg.org/css-images/#typedef-color-stop-list>
#[derive(Clone, Copy, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
pub struct ColorStop<Color, LengthOrPercentage> {
    /// The color of this stop.
    pub color: Color,
    /// The position of this stop.
    pub position: Option<LengthOrPercentage>,
}

/// Specified values for a paint worklet.
/// <https://drafts.css-houdini.org/css-paint-api/>
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
pub struct PaintWorklet {
    /// The name the worklet was registered with.
    pub name: Atom,
    /// The arguments for the worklet.
    /// TODO: store a parsed representation of the arguments.
    #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
    pub arguments: Vec<Arc<custom_properties::SpecifiedValue>>,
}

trivial_to_computed_value!(PaintWorklet);

impl ToCss for PaintWorklet {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        dest.write_str("paint(")?;
        serialize_identifier(&*self.name.to_string(), dest)?;
        for argument in &self.arguments {
            dest.write_str(", ")?;
            argument.to_css(dest)?;
        }
        dest.write_str(")")
    }
}

/// Values for `moz-image-rect`.
///
/// `-moz-image-rect(<uri>, top, right, bottom, left);`
#[allow(missing_docs)]
#[css(comma, function)]
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
pub struct MozImageRect<NumberOrPercentage, MozImageRectUrl> {
    pub url: MozImageRectUrl,
    pub top: NumberOrPercentage,
    pub right: NumberOrPercentage,
    pub bottom: NumberOrPercentage,
    pub left: NumberOrPercentage,
}

impl<G, R, U> fmt::Debug for Image<G, R, U>
    where G: fmt::Debug, R: fmt::Debug, U: fmt::Debug + ToCss
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Image::Url(ref url) => url.to_css(f),
            Image::Gradient(ref grad) => grad.fmt(f),
            Image::Rect(ref rect) => rect.fmt(f),
            #[cfg(feature = "servo")]
            Image::PaintWorklet(ref paint_worklet) => paint_worklet.fmt(f),
            Image::Element(ref selector) => {
                f.write_str("-moz-element(#")?;
                serialize_identifier(&selector.to_string(), f)?;
                f.write_str(")")
            },
        }
    }
}

impl<G, R, U> ToCss for Image<G, R, U>
    where G: ToCss, R: ToCss, U: ToCss
{
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match *self {
            Image::Url(ref url) => url.to_css(dest),
            Image::Gradient(ref gradient) => gradient.to_css(dest),
            Image::Rect(ref rect) => rect.to_css(dest),
            #[cfg(feature = "servo")]
            Image::PaintWorklet(ref paint_worklet) => paint_worklet.to_css(dest),
            Image::Element(ref selector) => {
                dest.write_str("-moz-element(#")?;
                serialize_identifier(&selector.to_string(), dest)?;
                dest.write_str(")")
            },
        }
    }
}

impl<D, L, LoP, P, C, A> ToCss for Gradient<D, L, LoP, P, C, A>
    where D: LineDirection, L: ToCss, LoP: ToCss, P: ToCss, C: ToCss, A: ToCss
{
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match self.compat_mode {
            CompatMode::WebKit => dest.write_str("-webkit-")?,
            CompatMode::Moz => dest.write_str("-moz-")?,
            _ => {},
        }

        if self.repeating {
            dest.write_str("repeating-")?;
        }
        dest.write_str(self.kind.label())?;
        dest.write_str("-gradient(")?;
        let mut skip_comma = match self.kind {
            GradientKind::Linear(ref direction)
                if direction.points_downwards(self.compat_mode) => true,
            GradientKind::Linear(ref direction) => {
                direction.to_css(dest, self.compat_mode)?;
                false
            },
            GradientKind::Radial(ref shape, ref position, ref angle) => {
                let omit_shape = match *shape {
                    EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover)) |
                    EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => {
                        true
                    },
                    _ => false,
                };
                if self.compat_mode == CompatMode::Modern {
                    if !omit_shape {
                        shape.to_css(dest)?;
                        dest.write_str(" ")?;
                    }
                    dest.write_str("at ")?;
                    position.to_css(dest)?;
                } else {
                    position.to_css(dest)?;
                    if let Some(ref a) = *angle {
                        dest.write_str(" ")?;
                        a.to_css(dest)?;
                    }
                    if !omit_shape {
                        dest.write_str(", ")?;
                        shape.to_css(dest)?;
                    }
                }
                false
            },
        };
        for item in &self.items {
            if !skip_comma {
                dest.write_str(", ")?;
            }
            skip_comma = false;
            item.to_css(dest)?;
        }
        dest.write_str(")")
    }
}

impl<D, L, LoP, P, A> GradientKind<D, L, LoP, P, A> {
    fn label(&self) -> &str {
        match *self {
            GradientKind::Linear(..) => "linear",
            GradientKind::Radial(..) => "radial",
        }
    }
}

/// The direction of a linear gradient.
pub trait LineDirection {
    /// Whether this direction points towards, and thus can be omitted.
    fn points_downwards(&self, compat_mode: CompatMode) -> bool;

    /// Serialises this direction according to the compatibility mode.
    fn to_css<W>(&self, dest: &mut W, compat_mode: CompatMode) -> fmt::Result
        where W: fmt::Write;
}

impl<L> ToCss for Circle<L>
where
    L: ToCss,
{
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
    where
        W: fmt::Write,
    {
        match *self {
            Circle::Extent(ShapeExtent::FarthestCorner) |
            Circle::Extent(ShapeExtent::Cover) => {
                dest.write_str("circle")
            },
            Circle::Extent(keyword) => {
                dest.write_str("circle ")?;
                keyword.to_css(dest)
            },
            Circle::Radius(ref length) => {
                length.to_css(dest)
            },
        }
    }
}

impl<C, L> fmt::Debug for ColorStop<C, L>
    where C: fmt::Debug, L: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self.color)?;
        if let Some(ref pos) = self.position {
            write!(f, " {:?}", pos)?;
        }
        Ok(())
    }
}
<commit_msg>Use Image::to_css in <Image as Debug>::fmt<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Generic types for the handling of [images].
//!
//! [images]: https://drafts.csswg.org/css-images/#image-values

use Atom;
use cssparser::serialize_identifier;
use custom_properties;
use servo_arc::Arc;
use std::fmt;
use style_traits::ToCss;

/// An [image].
///
/// [image]: https://drafts.csswg.org/css-images/#image-values
#[derive(Clone, MallocSizeOf, PartialEq, ToComputedValue)]
pub enum Image<Gradient, MozImageRect, ImageUrl> {
    /// A `<url()>` image.
    Url(ImageUrl),
    /// A `<gradient>` image.  Gradients are rather large, and not nearly as
    /// common as urls, so we box them here to keep the size of this enum sane.
    Gradient(Box<Gradient>),
    /// A `-moz-image-rect` image.  Also fairly large and rare.
    Rect(Box<MozImageRect>),
    /// A `-moz-element(# <element-id>)`
    Element(Atom),
    /// A paint worklet image.
    /// <https://drafts.css-houdini.org/css-paint-api/>
    #[cfg(feature = "servo")]
    PaintWorklet(PaintWorklet),
}

/// A CSS gradient.
/// <https://drafts.csswg.org/css-images/#gradients>
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
pub struct Gradient<LineDirection, Length, LengthOrPercentage, Position, Color, Angle> {
    /// Gradients can be linear or radial.
    pub kind: GradientKind<LineDirection, Length, LengthOrPercentage, Position, Angle>,
    /// The color stops and interpolation hints.
    pub items: Vec<GradientItem<Color, LengthOrPercentage>>,
    /// True if this is a repeating gradient.
    #[compute(clone)]
    pub repeating: bool,
    /// Compatibility mode.
    #[compute(clone)]
    pub compat_mode: CompatMode,
}

#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
/// Whether we used the modern notation or the compatibility `-webkit`, `-moz` prefixes.
pub enum CompatMode {
    /// Modern syntax.
    Modern,
    /// `-webkit` prefix.
    WebKit,
    /// `-moz` prefix
    Moz,
}

/// A gradient kind.
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
pub enum GradientKind<LineDirection, Length, LengthOrPercentage, Position, Angle> {
    /// A linear gradient.
    Linear(LineDirection),
    /// A radial gradient.
    Radial(EndingShape<Length, LengthOrPercentage>, Position, Option<Angle>),
}

/// A radial gradient's ending shape.
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
pub enum EndingShape<Length, LengthOrPercentage> {
    /// A circular gradient.
    Circle(Circle<Length>),
    /// An elliptic gradient.
    Ellipse(Ellipse<LengthOrPercentage>),
}

/// A circle shape.
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
pub enum Circle<Length> {
    /// A circle radius.
    Radius(Length),
    /// A circle extent.
    Extent(ShapeExtent),
}

/// An ellipse shape.
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
pub enum Ellipse<LengthOrPercentage> {
    /// An ellipse pair of radii.
    Radii(LengthOrPercentage, LengthOrPercentage),
    /// An ellipse extent.
    Extent(ShapeExtent),
}

/// <https://drafts.csswg.org/css-images/#typedef-extent-keyword>
define_css_keyword_enum!(ShapeExtent:
    "closest-side" => ClosestSide,
    "farthest-side" => FarthestSide,
    "closest-corner" => ClosestCorner,
    "farthest-corner" => FarthestCorner,
    "contain" => Contain,
    "cover" => Cover
);
add_impls_for_keyword_enum!(ShapeExtent);

/// A gradient item.
/// <https://drafts.csswg.org/css-images-4/#color-stop-syntax>
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
pub enum GradientItem<Color, LengthOrPercentage> {
    /// A color stop.
    ColorStop(ColorStop<Color, LengthOrPercentage>),
    /// An interpolation hint.
    InterpolationHint(LengthOrPercentage),
}

/// A color stop.
/// <https://drafts.csswg.org/css-images/#typedef-color-stop-list>
#[derive(Clone, Copy, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
pub struct ColorStop<Color, LengthOrPercentage> {
    /// The color of this stop.
    pub color: Color,
    /// The position of this stop.
    pub position: Option<LengthOrPercentage>,
}

/// Specified values for a paint worklet.
/// <https://drafts.css-houdini.org/css-paint-api/>
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
pub struct PaintWorklet {
    /// The name the worklet was registered with.
    pub name: Atom,
    /// The arguments for the worklet.
    /// TODO: store a parsed representation of the arguments.
    #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
    pub arguments: Vec<Arc<custom_properties::SpecifiedValue>>,
}

trivial_to_computed_value!(PaintWorklet);

impl ToCss for PaintWorklet {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        dest.write_str("paint(")?;
        serialize_identifier(&*self.name.to_string(), dest)?;
        for argument in &self.arguments {
            dest.write_str(", ")?;
            argument.to_css(dest)?;
        }
        dest.write_str(")")
    }
}

/// Values for `moz-image-rect`.
///
/// `-moz-image-rect(<uri>, top, right, bottom, left);`
#[allow(missing_docs)]
#[css(comma, function)]
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
pub struct MozImageRect<NumberOrPercentage, MozImageRectUrl> {
    pub url: MozImageRectUrl,
    pub top: NumberOrPercentage,
    pub right: NumberOrPercentage,
    pub bottom: NumberOrPercentage,
    pub left: NumberOrPercentage,
}

impl<G, R, U> fmt::Debug for Image<G, R, U>
    where G: fmt::Debug, R: fmt::Debug, U: fmt::Debug + ToCss
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.to_css(f)
    }
}

impl<G, R, U> ToCss for Image<G, R, U>
    where G: ToCss, R: ToCss, U: ToCss
{
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match *self {
            Image::Url(ref url) => url.to_css(dest),
            Image::Gradient(ref gradient) => gradient.to_css(dest),
            Image::Rect(ref rect) => rect.to_css(dest),
            #[cfg(feature = "servo")]
            Image::PaintWorklet(ref paint_worklet) => paint_worklet.to_css(dest),
            Image::Element(ref selector) => {
                dest.write_str("-moz-element(#")?;
                serialize_identifier(&selector.to_string(), dest)?;
                dest.write_str(")")
            },
        }
    }
}

impl<D, L, LoP, P, C, A> ToCss for Gradient<D, L, LoP, P, C, A>
    where D: LineDirection, L: ToCss, LoP: ToCss, P: ToCss, C: ToCss, A: ToCss
{
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match self.compat_mode {
            CompatMode::WebKit => dest.write_str("-webkit-")?,
            CompatMode::Moz => dest.write_str("-moz-")?,
            _ => {},
        }

        if self.repeating {
            dest.write_str("repeating-")?;
        }
        dest.write_str(self.kind.label())?;
        dest.write_str("-gradient(")?;
        let mut skip_comma = match self.kind {
            GradientKind::Linear(ref direction)
                if direction.points_downwards(self.compat_mode) => true,
            GradientKind::Linear(ref direction) => {
                direction.to_css(dest, self.compat_mode)?;
                false
            },
            GradientKind::Radial(ref shape, ref position, ref angle) => {
                let omit_shape = match *shape {
                    EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover)) |
                    EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => {
                        true
                    },
                    _ => false,
                };
                if self.compat_mode == CompatMode::Modern {
                    if !omit_shape {
                        shape.to_css(dest)?;
                        dest.write_str(" ")?;
                    }
                    dest.write_str("at ")?;
                    position.to_css(dest)?;
                } else {
                    position.to_css(dest)?;
                    if let Some(ref a) = *angle {
                        dest.write_str(" ")?;
                        a.to_css(dest)?;
                    }
                    if !omit_shape {
                        dest.write_str(", ")?;
                        shape.to_css(dest)?;
                    }
                }
                false
            },
        };
        for item in &self.items {
            if !skip_comma {
                dest.write_str(", ")?;
            }
            skip_comma = false;
            item.to_css(dest)?;
        }
        dest.write_str(")")
    }
}

impl<D, L, LoP, P, A> GradientKind<D, L, LoP, P, A> {
    fn label(&self) -> &str {
        match *self {
            GradientKind::Linear(..) => "linear",
            GradientKind::Radial(..) => "radial",
        }
    }
}

/// The direction of a linear gradient.
pub trait LineDirection {
    /// Whether this direction points towards, and thus can be omitted.
    fn points_downwards(&self, compat_mode: CompatMode) -> bool;

    /// Serialises this direction according to the compatibility mode.
    fn to_css<W>(&self, dest: &mut W, compat_mode: CompatMode) -> fmt::Result
        where W: fmt::Write;
}

impl<L> ToCss for Circle<L>
where
    L: ToCss,
{
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
    where
        W: fmt::Write,
    {
        match *self {
            Circle::Extent(ShapeExtent::FarthestCorner) |
            Circle::Extent(ShapeExtent::Cover) => {
                dest.write_str("circle")
            },
            Circle::Extent(keyword) => {
                dest.write_str("circle ")?;
                keyword.to_css(dest)
            },
            Circle::Radius(ref length) => {
                length.to_css(dest)
            },
        }
    }
}

impl<C, L> fmt::Debug for ColorStop<C, L>
    where C: fmt::Debug, L: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self.color)?;
        if let Some(ref pos) = self.position {
            write!(f, " {:?}", pos)?;
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>
extern mod sax;

use extra::treemap::TreeSet;
use self::sax::*;

pub enum Ns { Gl, Glx, Wgl }

fn trim_str<'a>(s: &'a str, trim: &str) -> &'a str {
    if s.starts_with(trim) { s.slice_from(trim.len()) } else { s }
}

fn trim_enum_prefix<'a>(ident: &'a str, ns: Ns) -> &'a str {
    match ns {
        Gl => trim_str(ident, "GL_"),
        Glx => trim_str(ident, "GLX_"),
        Wgl =>  trim_str(ident, "WGL_"),
    }
}

fn trim_cmd_prefix<'a>(ident: &'a str, ns: Ns) -> &'a str {
    match ns {
        Gl => trim_str(ident, "gl"),
        Glx => trim_str(ident, "glx"),
        Wgl =>  trim_str(ident, "wgl"),
    }
}

pub struct Registry {
    groups: ~[Group],
    enums: ~[EnumNs],
    cmds: ~[CmdNs],
}

impl Registry {
    /// Generate a registry from the supplied XML string
    pub fn from_xml(data: &str, ns: Ns) -> Registry {
        RegistryBuilder::parse(data, ns)
    }

    /// Returns a set of all the types used in the supplied registry. This is useful
    /// for working out what conversions are needed for the specific registry.
    pub fn get_tys(&self) -> TreeSet<~str> {
        let mut tys = TreeSet::new();
        for cmds in self.cmds.iter() {
            for def in cmds.defs.iter() {
                tys.insert(def.proto.ty.clone());
                for param in def.params.iter() {
                    tys.insert(param.ty.clone());
                }
            }
        }
        tys
    }
}

pub struct Group {
    name: ~str,
    enums: ~[~str],
}

pub struct EnumNs {
    namespace: ~str,
    group: Option<~str>,
    ty: Option<~str>,
    start: Option<~str>,
    end: Option<~str>,
    vendor: Option<~str>,
    comment: Option<~str>,
    defs: ~[Enum],
}

pub struct Enum {
    ident: ~str,
    value: ~str,
    alias: Option<~str>,
}

pub struct CmdNs {
    namespace: ~str,
    defs: ~[Cmd],
}

pub struct Binding {
    ident: ~str,
    ty: ~str,
    group: Option<~str>,
}

pub struct Cmd {
    proto: Binding,
    params: ~[Binding],
    is_safe: bool,
    alias: Option<~str>,
    vecequiv: Option<~str>,
    glx: Option<GlxOpcode>,
}

pub struct GlxOpcode {
    ty: ~str,
    opcode: ~str,
    name: Option<~str>,
    comment: Option<~str>,
}

struct RegistryBuilder {
    ns: Ns,
    priv port: SaxPort,
}

/// A big, ugly, imperative impl with methods that accumulates a Registry struct
impl<'self> RegistryBuilder {
    fn parse(data: &str, ns: Ns) -> Registry {
        RegistryBuilder {
            ns: ns,
            port: parse_xml(data),
        }.consume_registry()
    }

    fn recv(&self) -> ParseEvent {
        loop {
            match self.port.recv() {
                Ok(StartDocument) => (),
                Ok(Comment(_)) => (),
                Ok(Characters(ref ch)) if ch.is_whitespace() => (),
                Ok(EndDocument) => fail!("The end of the document has been reached"),
                Ok(event) => return event,
                Err(err) => fail!("XML error: %s", err.to_str()),
            }
        }
    }

    fn expect_characters(&self) -> ~str {
        match self.recv() {
            Characters(ref ch) => ch.clone(),
            msg => fail!("Expected characters, found: %s", msg.to_str()),
        }
    }

    fn expect_start_element(&self, name: &str) -> Attributes {
        match self.recv() {
            StartElement(ref n, ref atts) if name == *n => atts.clone(),
            msg => fail!("Expected <%s>, found: %s", name, msg.to_str()),
        }
    }

    fn expect_end_element(&self, name: &str) {
        match self.recv() {
            EndElement(ref n) if name == *n => (),
            msg => fail!("Expected </%s>, found: %s", name, msg.to_str()),
        }
    }

    fn skip_until(&self, event: ParseEvent) {
        loop {
            match self.recv() {
                EndDocument => fail!("Expected %s, but reached the end of the document.",
                                     event.to_str()),
                ref msg if *msg == event => break,
                _ => (),
            }
        }
    }

    fn consume_registry(&self) -> Registry {
        self.expect_start_element("registry");
        let mut registry = Registry {
            groups: ~[],
            enums: ~[],
            cmds: ~[]
        };
        loop {
            match self.recv() {
                // ignores
                Characters(_) | Comment(_) => (),
                StartElement(~"comment", _) => self.skip_until(EndElement(~"comment")),
                StartElement(~"types", _) => self.skip_until(EndElement(~"types")),
                StartElement(~"feature", _) => self.skip_until(EndElement(~"feature")),
                StartElement(~"extensions", _) => self.skip_until(EndElement(~"extensions")),

                // add groups
                StartElement(~"groups", _) => {
                    loop {
                        match self.recv() {
                            StartElement(~"group", ref atts) => {
                                registry.groups.push(
                                    self.consume_group(atts.get_clone("name"))
                                );
                            }
                            EndElement(~"groups") => break,
                            msg => fail!("Expected </groups>, found: %s", msg.to_str()),
                        }
                    }
                }

                // add enum namespace
                StartElement(~"enums", ref atts) => {
                    registry.enums.push(
                        EnumNs {
                            namespace:  atts.get_clone("namespace"),
                            group:      atts.find_clone("group"),
                            ty:         atts.find_clone("type"),
                            start:      atts.find_clone("start"),
                            end:        atts.find_clone("end"),
                            vendor:     atts.find_clone("vendor"),
                            comment:    atts.find_clone("comment"),
                            defs:       self.consume_enums(),
                        }
                    )
                }

                // add command namespace
                StartElement(~"commands", ref atts) => {
                    registry.cmds.push(
                        CmdNs {
                            namespace:  atts.get_clone("namespace"),
                            defs:       self.consume_cmds(),
                        }
                    );
                }

                // finished building the registry
                EndElement(~"registry") => break,

                // error handling
                msg => fail!("Expected </registry>, found: %s", msg.to_str()),
            }
        }
        registry
    }

    fn consume_group(&self, name: ~str) -> Group {
        let mut enms = ~[];
        loop {
            match self.recv() {
                StartElement(~"enum", ref atts) => {
                    enms.push(atts.get_clone("name"));
                    self.expect_end_element("enum");
                }
                EndElement(~"group") => break,
                msg => fail!("Expected </group>, found: %s", msg.to_str()),
            }
        }
        Group {
            name: name,
            enums: enms,
        }
    }

    fn consume_enums(&self) -> ~[Enum] {
        let mut enums = ~[];
        loop {
            match self.recv() {
                // ignores
                Characters(_) | Comment(_) => (),
                StartElement(~"unused", _) => self.skip_until(EndElement(~"unused")),

                // add enum definition
                StartElement(~"enum", ref atts) => {
                    enums.push(
                        Enum {
                            ident:  trim_enum_prefix(atts.get("name"), self.ns).to_owned(),
                            value:  atts.get_clone("value"),
                            alias:  atts.find_clone("alias"),
                        }
                    );
                    self.expect_end_element("enum");
                }

                // finished building the namespace
                EndElement(~"enums") => break,
                // error handling
                msg => fail!("Expected </enums>, found: %s", msg.to_str()),
            }
        }
        enums
    }

    fn consume_cmds(&self) -> ~[Cmd] {
        let mut cmds = ~[];
        loop {
            match self.recv() {
                // add command definition
                StartElement(~"command", _) => {
                    cmds.push(self.consume_cmd());
                }
                // finished building the namespace
                EndElement(~"commands") => break,
                // error handling
                msg => fail!("Expected </commands>, found: %s", msg.to_str()),
            }
        }
        cmds
    }

    fn consume_cmd(&self) -> Cmd {
        // consume command prototype
        let proto_atts = self.expect_start_element("proto");
        let mut proto = self.consume_binding(proto_atts.find_clone("group"));
        proto.ident = trim_cmd_prefix(proto.ident, self.ns).to_owned();
        self.expect_end_element("proto");

        let mut params = ~[];
        let mut alias = None;
        let mut vecequiv = None;
        let mut glx = None;
        loop {
            match self.recv() {
                StartElement(~"param", ref atts) => {
                    params.push(
                        self.consume_binding(atts.find_clone("group"))
                    );
                    self.expect_end_element("param");
                }
                StartElement(~"alias", ref atts) => {
                    alias = atts.find_clone("alias");
                    self.expect_end_element("alias");
                }
                StartElement(~"vecequiv", ref atts) => {
                    vecequiv = atts.find_clone("vecequiv");
                    self.expect_end_element("vecequiv");
                }
                StartElement(~"glx", ref atts) => {
                    glx = Some(GlxOpcode {
                        ty:      atts.get_clone("type"),
                        opcode:  atts.get_clone("opcode"),
                        name:    atts.find_clone("name"),
                        comment: atts.find_clone("comment"),
                    });
                    self.expect_end_element("glx");
                }
                EndElement(~"command") => break,
                msg => fail!("Expected </command>, found: %s", msg.to_str()),
            }
        }
        let is_safe = params.len() <= 0 || params.iter().all(|p| !p.ty.contains_char('*'));

        Cmd {
            proto: proto,
            params: params,
            is_safe: is_safe,
            alias: alias,
            vecequiv: vecequiv,
            glx: glx,
        }
    }

    fn consume_binding(&self, group: Option<~str>) -> Binding {
        // consume type
        let mut ty = ~"";
        loop {
            match self.recv() {
                Characters(ch) => ty.push_str(ch),
                StartElement(~"ptype", _) => (),
                EndElement(~"ptype") => (),
                StartElement(~"name", _) => break,
                msg => fail!("Expected binding, found: %s", msg.to_str()),
            }
        }
        // consume identifier
        let ident = self.expect_characters();
        self.expect_end_element("name");
        Binding {
            ident: ident,
            ty: ty,
            group: group,
        }
    }
}
<commit_msg>Parse <feature> and <extensions>. Don't do anything with it yet.<commit_after>
extern mod sax;

use extra::treemap::TreeSet;
use self::sax::*;

pub enum Ns { Gl, Glx, Wgl }

fn trim_str<'a>(s: &'a str, trim: &str) -> &'a str {
    if s.starts_with(trim) { s.slice_from(trim.len()) } else { s }
}

fn trim_enum_prefix<'a>(ident: &'a str, ns: Ns) -> &'a str {
    match ns {
        Gl => trim_str(ident, "GL_"),
        Glx => trim_str(ident, "GLX_"),
        Wgl =>  trim_str(ident, "WGL_"),
    }
}

fn trim_cmd_prefix<'a>(ident: &'a str, ns: Ns) -> &'a str {
    match ns {
        Gl => trim_str(ident, "gl"),
        Glx => trim_str(ident, "glx"),
        Wgl =>  trim_str(ident, "wgl"),
    }
}

pub struct Registry {
    groups: ~[Group],
    enums: ~[EnumNs],
    cmds: ~[CmdNs],
    features: ~[Feature],
    extensions: ~[Extension],
}

impl Registry {
    /// Generate a registry from the supplied XML string
    pub fn from_xml(data: &str, ns: Ns) -> Registry {
        RegistryBuilder::parse(data, ns)
    }

    /// Returns a set of all the types used in the supplied registry. This is useful
    /// for working out what conversions are needed for the specific registry.
    pub fn get_tys(&self) -> TreeSet<~str> {
        let mut tys = TreeSet::new();
        for cmds in self.cmds.iter() {
            for def in cmds.defs.iter() {
                tys.insert(def.proto.ty.clone());
                for param in def.params.iter() {
                    tys.insert(param.ty.clone());
                }
            }
        }
        tys
    }
}

pub struct Group {
    name: ~str,
    enums: ~[~str],
}

pub struct EnumNs {
    namespace: ~str,
    group: Option<~str>,
    ty: Option<~str>,
    start: Option<~str>,
    end: Option<~str>,
    vendor: Option<~str>,
    comment: Option<~str>,
    defs: ~[Enum],
}

pub struct Enum {
    ident: ~str,
    value: ~str,
    alias: Option<~str>,
}

pub struct CmdNs {
    namespace: ~str,
    defs: ~[Cmd],
}

pub struct Binding {
    ident: ~str,
    ty: ~str,
    group: Option<~str>,
}

pub struct Cmd {
    proto: Binding,
    params: ~[Binding],
    is_safe: bool,
    alias: Option<~str>,
    vecequiv: Option<~str>,
    glx: Option<GlxOpcode>,
}

pub struct Feature {
    api: ~str,
    name: ~str,
    number: ~str,
    requires: ~[Require],
    removes: ~[Remove],
}

pub struct Require {
    comment: Option<~str>,
    /// A reference to the earlier types, by name
    enums: ~[~str],
    /// A reference to the earlier types, by name
    commands: ~[~str],
}

pub struct Remove {
    // always core, for now
    profile: ~str,
    comment: ~str,
    /// A reference to the earlier types, by name
    enums: ~[~str],
    /// A reference to the earlier types, by name
    commands: ~[~str],
}

pub struct Extension {
    name: ~str,
    /// which apis this extension is defined for (see Feature.api)
    supported: ~[~str],
    requires: ~[Require],
}

pub struct GlxOpcode {
    ty: ~str,
    opcode: ~str,
    name: Option<~str>,
    comment: Option<~str>,
}

struct RegistryBuilder {
    ns: Ns,
    priv port: SaxPort,
}

/// A big, ugly, imperative impl with methods that accumulates a Registry struct
impl<'self> RegistryBuilder {
    fn parse(data: &str, ns: Ns) -> Registry {
        RegistryBuilder {
            ns: ns,
            port: parse_xml(data),
        }.consume_registry()
    }

    fn recv(&self) -> ParseEvent {
        loop {
            match self.port.recv() {
                Ok(StartDocument) => (),
                Ok(Comment(_)) => (),
                Ok(Characters(ref ch)) if ch.is_whitespace() => (),
                Ok(EndDocument) => fail!("The end of the document has been reached"),
                Ok(event) => return event,
                Err(err) => fail!("XML error: %s", err.to_str()),
            }
        }
    }

    fn expect_characters(&self) -> ~str {
        match self.recv() {
            Characters(ref ch) => ch.clone(),
            msg => fail!("Expected characters, found: %s", msg.to_str()),
        }
    }

    fn expect_start_element(&self, name: &str) -> Attributes {
        match self.recv() {
            StartElement(ref n, ref atts) if name == *n => atts.clone(),
            msg => fail!("Expected <%s>, found: %s", name, msg.to_str()),
        }
    }

    fn expect_end_element(&self, name: &str) {
        match self.recv() {
            EndElement(ref n) if name == *n => (),
            msg => fail!("Expected </%s>, found: %s", name, msg.to_str()),
        }
    }

    fn skip_until(&self, event: ParseEvent) {
        loop {
            match self.recv() {
                EndDocument => fail!("Expected %s, but reached the end of the document.",
                                     event.to_str()),
                ref msg if *msg == event => break,
                _ => (),
            }
        }
    }

    fn consume_registry(&self) -> Registry {
        self.expect_start_element("registry");
        let mut registry = Registry {
            groups: ~[],
            enums: ~[],
            cmds: ~[],
            features: ~[],
            extensions: ~[],
        };

        loop {
            match self.recv() {
                // ignores
                Characters(_) | Comment(_) => (),
                StartElement(~"comment", _) => self.skip_until(EndElement(~"comment")),
                StartElement(~"types", _) => self.skip_until(EndElement(~"types")),

                // add groups
                StartElement(~"groups", _) => {
                    loop {
                        match self.recv() {
                            StartElement(~"group", ref atts) => {
                                registry.groups.push(
                                    self.consume_group(atts.get_clone("name"))
                                );
                            }
                            EndElement(~"groups") => break,
                            msg => fail!("Expected </groups>, found: %s", msg.to_str()),
                        }
                    }
                }

                // add enum namespace
                StartElement(~"enums", ref atts) => {
                    registry.enums.push(
                        EnumNs {
                            namespace:  atts.get_clone("namespace"),
                            group:      atts.find_clone("group"),
                            ty:         atts.find_clone("type"),
                            start:      atts.find_clone("start"),
                            end:        atts.find_clone("end"),
                            vendor:     atts.find_clone("vendor"),
                            comment:    atts.find_clone("comment"),
                            defs:       self.consume_enums(),
                        }
                    )
                }

                // add command namespace
                StartElement(~"commands", ref atts) => {
                    registry.cmds.push(
                        CmdNs {
                            namespace:  atts.get_clone("namespace"),
                            defs:       self.consume_cmds(),
                        }
                    );
                }

                StartElement(~"feature", ref atts) => {
                    debug2!("Parsing feature: {:?}", atts);
                    registry.features.push(FromXML::convert(self, atts));
                }

                StartElement(~"extensions", ref atts) => {
                    loop {
                        match self.recv() {
                            StartElement(~"extension", ref atts) => {
                                registry.extensions.push(FromXML::convert(self, atts));
                            }
                            EndElement(~"extensions") => break,
                            msg => fail2!("Unexpected message {}", msg.to_str()),
                        }
                    }
                }

                // finished building the registry
                EndElement(~"registry") => break,

                // error handling
                msg => fail!("Expected </registry>, found: %s", msg.to_str()),
            }
        }
        registry
    }

    fn consume_two<'a, T: FromXML, U: FromXML>(&self, one: &'a str, two: &'a str, end: &'a str) -> (~[T], ~[U]) {
        debug2!("consume_two: looking for {:s} and {:s} until {:s}", one, two, end);

        let mut ones = ~[];
        let mut twos = ~[];

        loop {
            match self.recv() {
                StartElement(ref name, ref atts) => {
                    debug2!("Found start element <{:?} {:?}>", name, atts);
                    debug2!("one and two are {:?} and {:?}", one, two);

                    let n = name.clone();

                    if one == n {
                        ones.push(FromXML::convert(self, atts));
                    } else if "type" == n {
                        // XXX: GL1.1 contains types, which we never care about anyway.
                        // Make sure consume_two doesn't get used for things which *do*
                        // care about type.
                        warn!("Ignoring type!");
                        loop;
                    } else if two == n {
                        twos.push(FromXML::convert(self, atts));
                    } else {
                        fail2!("Unexpected element: <{:?} {:?}>", n, atts);
                    }
                },
                EndElement(name) => {
                    debug2!("Found end element </{:?}>", name);

                    if (&[one, two]).iter().any(|&x| x == name) {
                        loop;
                    } else if "type" == name {
                        // XXX: GL1.1 contains types, which we never care about anyway.
                        // Make sure consume_two doesn't get used for things which *do*
                        // care about type.
                        warn!("Ignoring type!");
                        loop;
                    } else if end == name {
                        return (ones, twos);
                    } else {
                        fail2!("Unexpected end element {}", name);
                    }
                },
                msg => fail2!("Unexpected message {}", msg.to_str()) }
        }
    }

    fn consume_group(&self, name: ~str) -> Group {
        let mut enms = ~[];
        loop {
            match self.recv() {
                StartElement(~"enum", ref atts) => {
                    enms.push(atts.get_clone("name"));
                    self.expect_end_element("enum");
                }
                EndElement(~"group") => break,
                msg => fail!("Expected </group>, found: %s", msg.to_str()),
            }
        }
        Group {
            name: name,
            enums: enms,
        }
    }

    fn consume_enums(&self) -> ~[Enum] {
        let mut enums = ~[];
        loop {
            match self.recv() {
                // ignores
                Characters(_) | Comment(_) => (),
                StartElement(~"unused", _) => self.skip_until(EndElement(~"unused")),

                // add enum definition
                StartElement(~"enum", ref atts) => {
                    enums.push(
                        Enum {
                            ident:  trim_enum_prefix(atts.get("name"), self.ns).to_owned(),
                            value:  atts.get_clone("value"),
                            alias:  atts.find_clone("alias"),
                        }
                    );
                    self.expect_end_element("enum");
                }

                // finished building the namespace
                EndElement(~"enums") => break,
                // error handling
                msg => fail!("Expected </enums>, found: %s", msg.to_str()),
            }
        }
        enums
    }

    fn consume_cmds(&self) -> ~[Cmd] {
        let mut cmds = ~[];
        loop {
            match self.recv() {
                // add command definition
                StartElement(~"command", _) => {
                    cmds.push(self.consume_cmd());
                }
                // finished building the namespace
                EndElement(~"commands") => break,
                // error handling
                msg => fail!("Expected </commands>, found: %s", msg.to_str()),
            }
        }
        cmds
    }

    fn consume_cmd(&self) -> Cmd {
        // consume command prototype
        let proto_atts = self.expect_start_element("proto");
        let mut proto = self.consume_binding(proto_atts.find_clone("group"));
        proto.ident = trim_cmd_prefix(proto.ident, self.ns).to_owned();
        self.expect_end_element("proto");

        let mut params = ~[];
        let mut alias = None;
        let mut vecequiv = None;
        let mut glx = None;
        loop {
            match self.recv() {
                StartElement(~"param", ref atts) => {
                    params.push(
                        self.consume_binding(atts.find_clone("group"))
                    );
                    self.expect_end_element("param");
                }
                StartElement(~"alias", ref atts) => {
                    alias = atts.find_clone("alias");
                    self.expect_end_element("alias");
                }
                StartElement(~"vecequiv", ref atts) => {
                    vecequiv = atts.find_clone("vecequiv");
                    self.expect_end_element("vecequiv");
                }
                StartElement(~"glx", ref atts) => {
                    glx = Some(GlxOpcode {
                        ty:      atts.get_clone("type"),
                        opcode:  atts.get_clone("opcode"),
                        name:    atts.find_clone("name"),
                        comment: atts.find_clone("comment"),
                    });
                    self.expect_end_element("glx");
                }
                EndElement(~"command") => break,
                msg => fail!("Expected </command>, found: %s", msg.to_str()),
            }
        }
        let is_safe = params.len() <= 0 || params.iter().all(|p| !p.ty.contains_char('*'));

        Cmd {
            proto: proto,
            params: params,
            is_safe: is_safe,
            alias: alias,
            vecequiv: vecequiv,
            glx: glx,
        }
    }

    fn consume_binding(&self, group: Option<~str>) -> Binding {
        // consume type
        let mut ty = ~"";
        loop {
            match self.recv() {
                Characters(ch) => ty.push_str(ch),
                StartElement(~"ptype", _) => (),
                EndElement(~"ptype") => (),
                StartElement(~"name", _) => break,
                msg => fail!("Expected binding, found: %s", msg.to_str()),
            }
        }
        // consume identifier
        let ident = self.expect_characters();
        self.expect_end_element("name");
        Binding {
            ident: ident,
            ty: ty,
            group: group,
        }
    }
}

trait FromXML {
    fn convert(r: &RegistryBuilder, a: &sax::Attributes) -> Self;
}

impl FromXML for Require {
    fn convert(r: &RegistryBuilder, a: &sax::Attributes) -> Require {
        debug!("Doing a FromXML on Require");
        let comment = a.find_clone("comment");
        let (enums, commands) = r.consume_two("enum", "command", "require");
        Require {
            comment: comment,
            enums: enums,
            commands: commands
        }
    }
}

impl FromXML for Remove {
    fn convert(r: &RegistryBuilder, a: &sax::Attributes) -> Remove {
        debug!("Doing a FromXML on Remove");
        let profile = a.get_clone("profile");
        let comment = a.get_clone("comment");
        let (enums, commands) = r.consume_two("enum", "command", "remove");

        Remove {
            profile: profile,
            comment: comment,
            enums: enums,
            commands: commands
        }
    }
}

impl FromXML for Feature {
    fn convert(r: &RegistryBuilder, a: &sax::Attributes) -> Feature {
        debug!("Doing a FromXML on Feature");
        let api      = a.get_clone("api");
        let name     = a.get_clone("name");
        let number   = a.get_clone("number");

        debug2!("Found api = {:s}, name = {:s}, number = {:s}", api, name, number);

        let (require, remove) = r.consume_two("require", "remove", "feature");

        Feature {
            api: api,
            name: name,
            number: number,
            requires: require,
            removes: remove
        }
    }
}

impl FromXML for Extension {
    fn convert(r: &RegistryBuilder, a: &sax::Attributes) -> Extension {
        debug!("Doing a FromXML on Extension");
        let name = a.get_clone("name");
        let supported = a.get("supported").split_iter('|').map(|x| x.to_owned()).to_owned_vec();
        let mut require = ~[];
        loop {
            match r.recv() {
                StartElement(~"require", ref atts) => {
                    require.push(FromXML::convert(r, atts));
                }
                EndElement(~"extension") => break,
                msg => fail2!("Unexpected message {}", msg.to_str())
            }
        }

        Extension {
            name: name,
            supported: supported,
            requires: require
        }
    }
}

impl FromXML for ~str {
    fn convert(_: &RegistryBuilder, a: &sax::Attributes) -> ~str {
        a.get_clone("name")
    }
}
<|endoftext|>