<commit_before>//! Asynchronous green-threads.
//!
//! ## What are Tasks?
//!
//! A _task_ is a light weight, non-blocking unit of execution. A task is similar
//! to an OS thread, but rather than being managed by the OS scheduler, they are
//! managed by the [Tokio runtime][rt]. Another name for this general pattern is
//! [green threads]. If you are familiar with [Go's goroutines], [Kotlin's
//! coroutines], or [Erlang's processes], you can think of Tokio's tasks as
//! something similar.
//!
//! Key points about tasks include:
//!
//! * Tasks are **light weight**. Because tasks are scheduled by the Tokio
//!   runtime rather than the operating system, creating new tasks or switching
//!   between tasks does not require a context switch and has fairly low
//!   overhead. Creating, running, and destroying large numbers of tasks is
//!   quite cheap, especially compared to OS threads.
//!
//! * Tasks are scheduled **cooperatively**. Most operating systems implement
//!   _preemptive multitasking_. This is a scheduling technique where the
//!   operating system allows each thread to run for a period of time, and then
//!   _preempts_ it, temporarily pausing that thread and switching to another.
//!   Tasks, on the other hand, implement _cooperative multitasking_. In
//!   cooperative multitasking, a task is allowed to run until it _yields_,
//!   indicating to the Tokio runtime's scheduler that it cannot currently
//!   continue executing. When a task yields, the Tokio runtime switches to
//!   executing the next task.
//!
//! * Tasks are **non-blocking**. Typically, when an OS thread performs I/O or
//!   must synchronize with another thread, it _blocks_, allowing the OS to
//!   schedule another thread. When a task cannot continue executing, it must
//!   yield instead, allowing the Tokio runtime to schedule another task. Tasks
//!   should generally not perform system calls or other operations that could
//!   block a thread, as this would prevent other tasks running on the same
//!   thread from executing as well. Instead, this module provides APIs for
//!   running blocking operations in an asynchronous context.
//!
//! [rt]: crate::runtime
//! [green threads]: https://en.wikipedia.org/wiki/Green_threads
//! [Go's goroutines]: https://tour.golang.org/concurrency/1
//! [Kotlin's coroutines]: https://kotlinlang.org/docs/reference/coroutines-overview.html
//! [Erlang's processes]: http://erlang.org/doc/getting_started/conc_prog.html#processes
//!
//! ## Working with Tasks
//!
//! This module provides the following APIs for working with tasks:
//!
//! ### Spawning
//!
//! Perhaps the most important function in this module is [`task::spawn`]. This
//! function can be thought of as an async equivalent to the standard library's
//! [`thread::spawn`][`std::thread::spawn`]. It takes an `async` block or other
//! [future], and creates a new task to run that work concurrently:
//!
//! ```
//! use tokio::task;
//!
//! # async fn doc() {
//! task::spawn(async {
//!     // perform some work here...
//! });
//! # }
//! ```
//!
//! Like [`std::thread::spawn`], `task::spawn` returns a [`JoinHandle`] struct.
//! A `JoinHandle` is itself a future which may be used to await the output of
//! the spawned task. For example:
//!
//! ```
//! use tokio::task;
//!
//! # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let join = task::spawn(async {
//!     // ...
//!     "hello world!"
//! });
//!
//! // ...
//!
//! // Await the result of the spawned task.
//! let result = join.await?;
//! assert_eq!(result, "hello world!");
//! # Ok(())
//! # }
//! ```
//!
//! Again, like `std::thread`'s [`JoinHandle` type][thread_join], if the spawned
//! task panics, awaiting its `JoinHandle` will return a [`JoinError`]`. For
//! example:
//!
//! ```
//! use tokio::task;
//!
//! # #[tokio::main] async fn main() {
//! let join = task::spawn(async {
//!     panic!("something bad happened!")
//! });
//!
//! // The returned result indicates that the task failed.
//! assert!(join.await.is_err());
//! # }
//! ```
//!
//! `spawn`, `JoinHandle`, and `JoinError` are present when the "rt"
//! feature flag is enabled.
//!
//! [`task::spawn`]: crate::task::spawn()
//! [future]: std::future::Future
//! [`std::thread::spawn`]: std::thread::spawn
//! [`JoinHandle`]: crate::task::JoinHandle
//! [thread_join]: std::thread::JoinHandle
//! [`JoinError`]: crate::task::JoinError
//!
//! ### Blocking and Yielding
//!
//! As we discussed above, code running in asynchronous tasks should not perform
//! operations that can block. A blocking operation performed in a task running
//! on a thread that is also running other tasks would block the entire thread,
//! preventing other tasks from running.
//!
//! Instead, Tokio provides two APIs for running blocking operations in an
//! asynchronous context: [`task::spawn_blocking`] and [`task::block_in_place`].
//!
//! Be aware that if you call a non-async method from async code, that non-async
//! method is still inside the asynchronous context, so you should also avoid
//! blocking operations there. This includes destructors of objects destroyed in
//! async code.
//!
//! #### spawn_blocking
//!
//! The `task::spawn_blocking` function is similar to the `task::spawn` function
//! discussed in the previous section, but rather than spawning an
//! _non-blocking_ future on the Tokio runtime, it instead spawns a
//! _blocking_ function on a dedicated thread pool for blocking tasks. For
//! example:
//!
//! ```
//! use tokio::task;
//!
//! # async fn docs() {
//! task::spawn_blocking(|| {
//!     // do some compute-heavy work or call synchronous code
//! });
//! # }
//! ```
//!
//! Just like `task::spawn`, `task::spawn_blocking` returns a `JoinHandle`
//! which we can use to await the result of the blocking operation:
//!
//! ```rust
//! # use tokio::task;
//! # async fn docs() -> Result<(), Box<dyn std::error::Error>>{
//! let join = task::spawn_blocking(|| {
//!     // do some compute-heavy work or call synchronous code
//!     "blocking completed"
//! });
//!
//! let result = join.await?;
//! assert_eq!(result, "blocking completed");
//! # Ok(())
//! # }
//! ```
//!
//! #### block_in_place
//!
//! When using the [multi-threaded runtime][rt-multi-thread], the [`task::block_in_place`]
//! function is also available. Like `task::spawn_blocking`, this function
//! allows running a blocking operation from an asynchronous context. Unlike
//! `spawn_blocking`, however, `block_in_place` works by transitioning the
//! _current_ worker thread to a blocking thread, moving other tasks running on
//! that thread to another worker thread. This can improve performance by avoiding
//! context switches.
//!
//! For example:
//!
//! ```
//! use tokio::task;
//!
//! # async fn docs() {
//! let result = task::block_in_place(|| {
//!     // do some compute-heavy work or call synchronous code
//!     "blocking completed"
//! });
//!
//! assert_eq!(result, "blocking completed");
//! # }
//! ```
//!
//! #### yield_now
//!
//! In addition, this module provides a [`task::yield_now`] async function
//! that is analogous to the standard library's [`thread::yield_now`]. Calling
//! and `await`ing this function will cause the current task to yield to the
//! Tokio runtime's scheduler, allowing other tasks to be
//! scheduled. Eventually, the yielding task will be polled again, allowing it
//! to execute. For example:
//!
//! ```rust
//! use tokio::task;
//!
//! # #[tokio::main] async fn main() {
//! async {
//!     task::spawn(async {
//!         // ...
//!         println!("spawned task done!")
//!     });
//!
//!     // Yield, allowing the newly-spawned task to execute first.
//!     task::yield_now().await;
//!     println!("main task done!");
//! }
//! # .await;
//! # }
//! ```
//!
//! ### Cooperative scheduling
//!
//! A single call to [`poll`] on a top-level task may potentially do a lot of
//! work before it returns `Poll::Pending`. If a task runs for a long period of
//! time without yielding back to the executor, it can starve other tasks
//! waiting on that executor to execute them, or drive underlying resources.
//! Since Rust does not have a runtime, it is difficult to forcibly preempt a
//! long-running task. Instead, this module provides an opt-in mechanism for
//! futures to collaborate with the executor to avoid starvation.
//!
//! Consider a future like this one:
//!
//! ```
//! # use tokio_stream::{Stream, StreamExt};
//! async fn drop_all<I: Stream + Unpin>(mut input: I) {
//!     while let Some(_) = input.next().await {}
//! }
//! ```
//!
//! It may look harmless, but consider what happens under heavy load if the
//! input stream is _always_ ready. If we spawn `drop_all`, the task will never
//! yield, and will starve other tasks and resources on the same executor.
//!
//! To account for this, Tokio has explicit yield points in a number of library
//! functions, which force tasks to return to the executor periodically.
//!
//!
//! #### unconstrained
//!
//! If necessary, [`task::unconstrained`] lets you opt out a future of Tokio's cooperative
//! scheduling. When a future is wrapped with `unconstrained`, it will never be forced to yield to
//! Tokio. For example:
//!
//! ```
//! # #[tokio::main]
//! # async fn main() {
//! use tokio::{task, sync::mpsc};
//!
//! let fut = async {
//!     let (tx, mut rx) = mpsc::unbounded_channel();
//!
//!     for i in 0..1000 {
//!         let _ = tx.send(());
//!         // This will always be ready. If coop was in effect, this code would be forced to yield
//!         // periodically. However, if left unconstrained, then this code will never yield.
//!         rx.recv().await;
//!     }
//! };
//!
//! task::unconstrained(fut).await;
//! # }
//! ```
//!
//! [`task::spawn_blocking`]: crate::task::spawn_blocking
//! [`task::block_in_place`]: crate::task::block_in_place
//! [rt-multi-thread]: ../runtime/index.html#threaded-scheduler
//! [`task::yield_now`]: crate::task::yield_now()
//! [`thread::yield_now`]: std::thread::yield_now
//! [`task::unconstrained`]: crate::task::unconstrained()
//! [`poll`]: method@std::future::Future::poll

cfg_rt! {
    pub use crate::runtime::task::{JoinError, JoinHandle};

    mod blocking;
    pub use blocking::spawn_blocking;

    mod spawn;
    pub use spawn::spawn;

    cfg_rt_multi_thread! {
        pub use blocking::block_in_place;
    }

    mod yield_now;
    pub use yield_now::yield_now;

    mod local;
    pub use local::{spawn_local, LocalSet};

    mod task_local;
    pub use task_local::LocalKey;

    mod unconstrained;
    pub use unconstrained::{unconstrained, Unconstrained};
}
<commit_msg>doc: fix minor doc typo (stray backtick) (#3814)<commit_after>//! Asynchronous green-threads.
//!
//! ## What are Tasks?
//!
//! A _task_ is a light weight, non-blocking unit of execution. A task is similar
//! to an OS thread, but rather than being managed by the OS scheduler, they are
//! managed by the [Tokio runtime][rt]. Another name for this general pattern is
//! [green threads]. If you are familiar with [Go's goroutines], [Kotlin's
//! coroutines], or [Erlang's processes], you can think of Tokio's tasks as
//! something similar.
//!
//! Key points about tasks include:
//!
//! * Tasks are **light weight**. Because tasks are scheduled by the Tokio
//!   runtime rather than the operating system, creating new tasks or switching
//!   between tasks does not require a context switch and has fairly low
//!   overhead. Creating, running, and destroying large numbers of tasks is
//!   quite cheap, especially compared to OS threads.
//!
//! * Tasks are scheduled **cooperatively**. Most operating systems implement
//!   _preemptive multitasking_. This is a scheduling technique where the
//!   operating system allows each thread to run for a period of time, and then
//!   _preempts_ it, temporarily pausing that thread and switching to another.
//!   Tasks, on the other hand, implement _cooperative multitasking_. In
//!   cooperative multitasking, a task is allowed to run until it _yields_,
//!   indicating to the Tokio runtime's scheduler that it cannot currently
//!   continue executing. When a task yields, the Tokio runtime switches to
//!   executing the next task.
//!
//! * Tasks are **non-blocking**. Typically, when an OS thread performs I/O or
//!   must synchronize with another thread, it _blocks_, allowing the OS to
//!   schedule another thread. When a task cannot continue executing, it must
//!   yield instead, allowing the Tokio runtime to schedule another task. Tasks
//!   should generally not perform system calls or other operations that could
//!   block a thread, as this would prevent other tasks running on the same
//!   thread from executing as well. Instead, this module provides APIs for
//!   running blocking operations in an asynchronous context.
//!
//! [rt]: crate::runtime
//! [green threads]: https://en.wikipedia.org/wiki/Green_threads
//! [Go's goroutines]: https://tour.golang.org/concurrency/1
//! [Kotlin's coroutines]: https://kotlinlang.org/docs/reference/coroutines-overview.html
//! [Erlang's processes]: http://erlang.org/doc/getting_started/conc_prog.html#processes
//!
//! ## Working with Tasks
//!
//! This module provides the following APIs for working with tasks:
//!
//! ### Spawning
//!
//! Perhaps the most important function in this module is [`task::spawn`]. This
//! function can be thought of as an async equivalent to the standard library's
//! [`thread::spawn`][`std::thread::spawn`]. It takes an `async` block or other
//! [future], and creates a new task to run that work concurrently:
//!
//! ```
//! use tokio::task;
//!
//! # async fn doc() {
//! task::spawn(async {
//!     // perform some work here...
//! });
//! # }
//! ```
//!
//! Like [`std::thread::spawn`], `task::spawn` returns a [`JoinHandle`] struct.
//! A `JoinHandle` is itself a future which may be used to await the output of
//! the spawned task. For example:
//!
//! ```
//! use tokio::task;
//!
//! # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let join = task::spawn(async {
//!     // ...
//!     "hello world!"
//! });
//!
//! // ...
//!
//! // Await the result of the spawned task.
//! let result = join.await?;
//! assert_eq!(result, "hello world!");
//! # Ok(())
//! # }
//! ```
//!
//! Again, like `std::thread`'s [`JoinHandle` type][thread_join], if the spawned
//! task panics, awaiting its `JoinHandle` will return a [`JoinError`]. For
//! example:
//!
//! ```
//! use tokio::task;
//!
//! # #[tokio::main] async fn main() {
//! let join = task::spawn(async {
//!     panic!("something bad happened!")
//! });
//!
//! // The returned result indicates that the task failed.
//! assert!(join.await.is_err());
//! # }
//! ```
//!
//! `spawn`, `JoinHandle`, and `JoinError` are present when the "rt"
//! feature flag is enabled.
//!
//! [`task::spawn`]: crate::task::spawn()
//! [future]: std::future::Future
//! [`std::thread::spawn`]: std::thread::spawn
//! [`JoinHandle`]: crate::task::JoinHandle
//! [thread_join]: std::thread::JoinHandle
//! [`JoinError`]: crate::task::JoinError
//!
//! ### Blocking and Yielding
//!
//! As we discussed above, code running in asynchronous tasks should not perform
//! operations that can block. A blocking operation performed in a task running
//! on a thread that is also running other tasks would block the entire thread,
//! preventing other tasks from running.
//!
//! Instead, Tokio provides two APIs for running blocking operations in an
//! asynchronous context: [`task::spawn_blocking`] and [`task::block_in_place`].
//!
//! Be aware that if you call a non-async method from async code, that non-async
//! method is still inside the asynchronous context, so you should also avoid
//! blocking operations there. This includes destructors of objects destroyed in
//! async code.
//!
//! #### spawn_blocking
//!
//! The `task::spawn_blocking` function is similar to the `task::spawn` function
//! discussed in the previous section, but rather than spawning an
//! _non-blocking_ future on the Tokio runtime, it instead spawns a
//! _blocking_ function on a dedicated thread pool for blocking tasks. For
//! example:
//!
//! ```
//! use tokio::task;
//!
//! # async fn docs() {
//! task::spawn_blocking(|| {
//!     // do some compute-heavy work or call synchronous code
//! });
//! # }
//! ```
//!
//! Just like `task::spawn`, `task::spawn_blocking` returns a `JoinHandle`
//! which we can use to await the result of the blocking operation:
//!
//! ```rust
//! # use tokio::task;
//! # async fn docs() -> Result<(), Box<dyn std::error::Error>>{
//! let join = task::spawn_blocking(|| {
//!     // do some compute-heavy work or call synchronous code
//!     "blocking completed"
//! });
//!
//! let result = join.await?;
//! assert_eq!(result, "blocking completed");
//! # Ok(())
//! # }
//! ```
//!
//! #### block_in_place
//!
//! When using the [multi-threaded runtime][rt-multi-thread], the [`task::block_in_place`]
//! function is also available. Like `task::spawn_blocking`, this function
//! allows running a blocking operation from an asynchronous context. Unlike
//! `spawn_blocking`, however, `block_in_place` works by transitioning the
//! _current_ worker thread to a blocking thread, moving other tasks running on
//! that thread to another worker thread. This can improve performance by avoiding
//! context switches.
//!
//! For example:
//!
//! ```
//! use tokio::task;
//!
//! # async fn docs() {
//! let result = task::block_in_place(|| {
//!     // do some compute-heavy work or call synchronous code
//!     "blocking completed"
//! });
//!
//! assert_eq!(result, "blocking completed");
//! # }
//! ```
//!
//! #### yield_now
//!
//! In addition, this module provides a [`task::yield_now`] async function
//! that is analogous to the standard library's [`thread::yield_now`]. Calling
//! and `await`ing this function will cause the current task to yield to the
//! Tokio runtime's scheduler, allowing other tasks to be
//! scheduled. Eventually, the yielding task will be polled again, allowing it
//! to execute. For example:
//!
//! ```rust
//! use tokio::task;
//!
//! # #[tokio::main] async fn main() {
//! async {
//!     task::spawn(async {
//!         // ...
//!         println!("spawned task done!")
//!     });
//!
//!     // Yield, allowing the newly-spawned task to execute first.
//!     task::yield_now().await;
//!     println!("main task done!");
//! }
//! # .await;
//! # }
//! ```
//!
//! ### Cooperative scheduling
//!
//! A single call to [`poll`] on a top-level task may potentially do a lot of
//! work before it returns `Poll::Pending`. If a task runs for a long period of
//! time without yielding back to the executor, it can starve other tasks
//! waiting on that executor to execute them, or drive underlying resources.
//! Since Rust does not have a runtime, it is difficult to forcibly preempt a
//! long-running task. Instead, this module provides an opt-in mechanism for
//! futures to collaborate with the executor to avoid starvation.
//!
//! Consider a future like this one:
//!
//! ```
//! # use tokio_stream::{Stream, StreamExt};
//! async fn drop_all<I: Stream + Unpin>(mut input: I) {
//!     while let Some(_) = input.next().await {}
//! }
//! ```
//!
//! It may look harmless, but consider what happens under heavy load if the
//! input stream is _always_ ready. If we spawn `drop_all`, the task will never
//! yield, and will starve other tasks and resources on the same executor.
//!
//! To account for this, Tokio has explicit yield points in a number of library
//! functions, which force tasks to return to the executor periodically.
//!
//!
//! #### unconstrained
//!
//! If necessary, [`task::unconstrained`] lets you opt out a future of Tokio's cooperative
//! scheduling. When a future is wrapped with `unconstrained`, it will never be forced to yield to
//! Tokio. For example:
//!
//! ```
//! # #[tokio::main]
//! # async fn main() {
//! use tokio::{task, sync::mpsc};
//!
//! let fut = async {
//!     let (tx, mut rx) = mpsc::unbounded_channel();
//!
//!     for i in 0..1000 {
//!         let _ = tx.send(());
//!         // This will always be ready. If coop was in effect, this code would be forced to yield
//!         // periodically. However, if left unconstrained, then this code will never yield.
//!         rx.recv().await;
//!     }
//! };
//!
//! task::unconstrained(fut).await;
//! # }
//! ```
//!
//! [`task::spawn_blocking`]: crate::task::spawn_blocking
//! [`task::block_in_place`]: crate::task::block_in_place
//! [rt-multi-thread]: ../runtime/index.html#threaded-scheduler
//! [`task::yield_now`]: crate::task::yield_now()
//! [`thread::yield_now`]: std::thread::yield_now
//! [`task::unconstrained`]: crate::task::unconstrained()
//! [`poll`]: method@std::future::Future::poll

cfg_rt! {
    pub use crate::runtime::task::{JoinError, JoinHandle};

    mod blocking;
    pub use blocking::spawn_blocking;

    mod spawn;
    pub use spawn::spawn;

    cfg_rt_multi_thread! {
        pub use blocking::block_in_place;
    }

    mod yield_now;
    pub use yield_now::yield_now;

    mod local;
    pub use local::{spawn_local, LocalSet};

    mod task_local;
    pub use task_local::LocalKey;

    mod unconstrained;
    pub use unconstrained::{unconstrained, Unconstrained};
}
<|endoftext|>


<commit_before>error_chain! {
    errors {
        MoreDataNeeded
        InvalidData
        ConfigurationIncomplete
    }
}
<commit_msg>codec: Add one more error<commit_after>error_chain! {
    errors {
        MoreDataNeeded
        InvalidData
        ConfigurationIncomplete
        InvalidConfiguration
    }
}
<|endoftext|>


<commit_before>use std::any::Any;

#[cfg(feature = "bytes")]
use bytes::Bytes;
#[cfg(feature = "bytes")]
use chars::Chars;

use core::*;
use super::*;
use super::as_any::AsAny;
use reflect::transmute_eq::transmute_eq;
use std::hash::Hash;
use std::hash::Hasher;
use std::mem;

/// Hack against lack of upcasting in Rust
pub trait AsProtobufValue {
    fn as_protobuf_value(&self) -> &ProtobufValue;
    fn as_protobuf_value_mut(&mut self) -> &mut ProtobufValue;
}

impl<T : ProtobufValue> AsProtobufValue for T {
    fn as_protobuf_value(&self) -> &ProtobufValue {
        self
    }

    fn as_protobuf_value_mut(&mut self) -> &mut ProtobufValue {
        self
    }
}

/// Type implemented by all protobuf singular types
/// (primitives, string, messages, enums).
///
/// Used for dynamic casting in reflection.
pub trait ProtobufValue : Any + AsAny + AsProtobufValue + 'static + Send + Sync {
}

impl ProtobufValue for u32 {
}

impl ProtobufValue for u64 {
}

impl ProtobufValue for i32 {
}

impl ProtobufValue for i64 {
}

impl ProtobufValue for f32 {
}

impl ProtobufValue for f64 {
}

impl ProtobufValue for bool {
}

impl ProtobufValue for String {
}

impl ProtobufValue for Vec<u8> {
}

#[cfg(feature = "bytes")]
impl ProtobufValue for Bytes {
}

#[cfg(feature = "bytes")]
impl ProtobufValue for Chars {
}

// conflicting implementations, so generated code is used instead
/*
impl<E : ProtobufEnum> ProtobufValue for E {
}

impl<M : Message> ProtobufValue for M {
}
*/

#[derive(Debug)]
pub enum ReflectValueRef<'a> {
    U32(u32),
    U64(u64),
    I32(i32),
    I64(i64),
    F32(f32),
    F64(f64),
    Bool(bool),
    String(&'a str),
    Bytes(&'a [u8]),
    Enum(&'static EnumValueDescriptor),
    Message(&'a Message),
}

impl<'a> ReflectValueRef<'a> {
    pub fn is_non_zero(&self) -> bool {
        match *self {
            ReflectValueRef::U32(v) => v != 0,
            ReflectValueRef::U64(v) => v != 0,
            ReflectValueRef::I32(v) => v != 0,
            ReflectValueRef::I64(v) => v != 0,
            ReflectValueRef::F32(v) => v != 0.,
            ReflectValueRef::F64(v) => v != 0.,
            ReflectValueRef::Bool(v) => v,
            ReflectValueRef::String(v) => !v.is_empty(),
            ReflectValueRef::Bytes(v) => !v.is_empty(),
            ReflectValueRef::Enum(v) => v.value() != 0,
            ReflectValueRef::Message(_) => true,
        }
    }

    // Clone to box
    pub fn to_box(&self) -> ReflectValueBox {
        match *self {
            ReflectValueRef::U32(v) => ReflectValueBox::U32(v),
            ReflectValueRef::U64(v) => ReflectValueBox::U64(v),
            ReflectValueRef::I32(v) => ReflectValueBox::I32(v),
            ReflectValueRef::I64(v) => ReflectValueBox::I64(v),
            ReflectValueRef::F32(v) => ReflectValueBox::F32(v),
            ReflectValueRef::F64(v) => ReflectValueBox::F64(v),
            ReflectValueRef::Bool(v) => ReflectValueBox::Bool(v),
            ReflectValueRef::String(v) => ReflectValueBox::String(v.to_owned()),
            ReflectValueRef::Bytes(v) => ReflectValueBox::Bytes(v.to_owned()),
            ReflectValueRef::Enum(v) => ReflectValueBox::Enum(v),
            ReflectValueRef::Message(v) => ReflectValueBox::Message(v.descriptor().clone(v)),
        }
    }
}

#[derive(Debug, Clone)]
pub enum ReflectValueBox {
    U32(u32),
    U64(u64),
    I32(i32),
    I64(i64),
    F32(f32),
    F64(f64),
    Bool(bool),
    String(String),
    Bytes(Vec<u8>),
    Enum(&'static EnumValueDescriptor),
    Message(Box<Message>),
}

fn _assert_value_box_send_sync() {
    fn _assert_send_sync<T : Send + Sync>() {}
    _assert_send_sync::<ReflectValueBox>();
}

#[cfg(not(feature = "bytes"))]
type VecU8OrBytes = Vec<u8>;
#[cfg(feature = "bytes")]
type VecU8OrBytes = Vec<u8>;
#[cfg(not(feature = "bytes"))]
type StringOrChars = String;
#[cfg(feature = "bytes")]
type StringOrChars = Chars;

impl ReflectValueBox {
    pub fn as_value_ref(&self) -> ReflectValueRef {
        use std::ops::Deref;
        match *self {
            ReflectValueBox::U32(v) => ReflectValueRef::U32(v),
            ReflectValueBox::U64(v) => ReflectValueRef::U64(v),
            ReflectValueBox::I32(v) => ReflectValueRef::I32(v),
            ReflectValueBox::I64(v) => ReflectValueRef::I64(v),
            ReflectValueBox::F32(v) => ReflectValueRef::F32(v),
            ReflectValueBox::F64(v) => ReflectValueRef::F64(v),
            ReflectValueBox::Bool(v) => ReflectValueRef::Bool(v),
            ReflectValueBox::String(ref v) => ReflectValueRef::String(v.as_str()),
            ReflectValueBox::Bytes(ref v) => ReflectValueRef::Bytes(v.as_slice()),
            ReflectValueBox::Enum(v) => ReflectValueRef::Enum(v),
            ReflectValueBox::Message(ref v) => ReflectValueRef::Message(v.deref()),
        }
    }

    pub fn as_value(&self) -> &ProtobufValue {
        match self {
            ReflectValueBox::U32(v) => v,
            ReflectValueBox::U64(v) => v,
            ReflectValueBox::I32(v) => v,
            ReflectValueBox::I64(v) => v,
            ReflectValueBox::F32(v) => v,
            ReflectValueBox::F64(v) => v,
            ReflectValueBox::Bool(v) => v,
            ReflectValueBox::String(v) => v,
            ReflectValueBox::Bytes(v) => v,
            ReflectValueBox::Enum(v) => v.protobuf_value(),
            ReflectValueBox::Message(v) => v.as_protobuf_value(),
        }
    }

    pub fn as_value_mut(&mut self) -> &mut ProtobufValue {
        match self {
            ReflectValueBox::U32(v) => v,
            ReflectValueBox::U64(v) => v,
            ReflectValueBox::I32(v) => v,
            ReflectValueBox::I64(v) => v,
            ReflectValueBox::F32(v) => v,
            ReflectValueBox::F64(v) => v,
            ReflectValueBox::Bool(v) => v,
            ReflectValueBox::String(v) => v,
            ReflectValueBox::Bytes(v) => v,
            ReflectValueBox::Enum(_v) => panic!("enum value cannot be mutable"),
            ReflectValueBox::Message(v) => v.as_protobuf_value_mut(),
        }
    }

    pub fn downcast<V : 'static>(self) -> Result<V, Self> {
        match self {
            ReflectValueBox::U32(v) => transmute_eq(v).map_err(ReflectValueBox::U32),
            ReflectValueBox::U64(v) => transmute_eq(v).map_err(ReflectValueBox::U64),
            ReflectValueBox::I32(v) => transmute_eq(v).map_err(ReflectValueBox::I32),
            ReflectValueBox::I64(v) => transmute_eq(v).map_err(ReflectValueBox::I64),
            ReflectValueBox::F32(v) => transmute_eq(v).map_err(ReflectValueBox::F32),
            ReflectValueBox::F64(v) => transmute_eq(v).map_err(ReflectValueBox::F64),
            ReflectValueBox::Bool(v) => transmute_eq(v).map_err(ReflectValueBox::Bool),
            ReflectValueBox::String(v) => {
                transmute_eq::<String, _>(v)
                    .or_else(|v: String| transmute_eq::<StringOrChars, _>(v.into()))
                    .map_err(|v: StringOrChars| ReflectValueBox::String(v.into()))
            },
            ReflectValueBox::Bytes(v) => {
                transmute_eq::<Vec<u8>, _>(v)
                    .or_else(|v: Vec<u8>| transmute_eq::<VecU8OrBytes, _>(v.into()))
                    .map_err(|v: VecU8OrBytes| ReflectValueBox::Bytes(v.into()))
            },
            ReflectValueBox::Enum(e) => {
                e.cast().ok_or(ReflectValueBox::Enum(e))
            }
            ReflectValueBox::Message(m) => {
                m.descriptor().cast(m).map_err(ReflectValueBox::Message)
            }
        }
    }
}

impl<'a> PartialEq for ReflectValueRef<'a> {
    fn eq(&self, other: &ReflectValueRef) -> bool {
        use self::ReflectValueRef::*;
        match (self, other) {
            (U32(a), U32(b)) => a == b,
            (U64(a), U64(b)) => a == b,
            (I32(a), I32(b)) => a == b,
            (I64(a), I64(b)) => a == b,
            // should probably NaN == NaN here
            (F32(a), F32(b)) => a == b,
            (F64(a), F64(b)) => a == b,
            (Bool(a), Bool(b)) => a == b,
            (String(a), String(b)) => a == b,
            (Bytes(a), Bytes(b)) => a == b,
            (Enum(a), Enum(b)) => a == b,
            (Message(a), Message(b)) => {
                use std::ops::Deref;
                a.descriptor() == b.descriptor() && a.descriptor().eq(a.deref(), b.deref())
            },
            _ => false,
        }
    }
}

impl<'a> PartialEq for ReflectValueBox {
    fn eq(&self, other: &Self) -> bool {
        self.as_value_ref() == other.as_value_ref()
    }
}

impl<'a> PartialEq<ReflectValueRef<'a>> for ReflectValueBox {
    fn eq(&self, other: &ReflectValueRef) -> bool {
        self.as_value_ref() == *other
    }
}

impl<'a> PartialEq<ReflectValueBox> for ReflectValueRef<'a> {
    fn eq(&self, other: &ReflectValueBox) -> bool {
        *self == other.as_value_ref()
    }
}

// Panics if contained type is not hashable
impl<'a> Hash for ReflectValueRef<'a> {
    fn hash<H : Hasher>(&self, state: &mut H) {
        use self::ReflectValueRef::*;
        Hash::hash(&mem::discriminant(self), state);
        match self {
            U32(v) => Hash::hash(&v, state),
            U64(v) => Hash::hash(&v, state),
            I32(v) => Hash::hash(&v, state),
            I64(v) => Hash::hash(&v, state),
            Bool(v) => Hash::hash(&v, state),
            String(v) => Hash::hash(&v, state),
            Bytes(v) => Hash::hash(&v, state),
            Enum(v) => Hash::hash(v, state),
            F32(_) | F64(_) | Message(_) => {
                panic!("not hashable: {:?}", self)
            }
        }
    }
}

// Panics if contained type is not hashable
impl Hash for ReflectValueBox {
    fn hash<H : Hasher>(&self, state: &mut H) {
        Hash::hash(&self.as_value_ref(), state)
    }
}
<commit_msg>downcast tests<commit_after>use std::any::Any;

#[cfg(feature = "bytes")]
use bytes::Bytes;
#[cfg(feature = "bytes")]
use chars::Chars;

use core::*;
use super::*;
use super::as_any::AsAny;
use reflect::transmute_eq::transmute_eq;
use std::hash::Hash;
use std::hash::Hasher;
use std::mem;

/// Hack against lack of upcasting in Rust
pub trait AsProtobufValue {
    fn as_protobuf_value(&self) -> &ProtobufValue;
    fn as_protobuf_value_mut(&mut self) -> &mut ProtobufValue;
}

impl<T : ProtobufValue> AsProtobufValue for T {
    fn as_protobuf_value(&self) -> &ProtobufValue {
        self
    }

    fn as_protobuf_value_mut(&mut self) -> &mut ProtobufValue {
        self
    }
}

/// Type implemented by all protobuf singular types
/// (primitives, string, messages, enums).
///
/// Used for dynamic casting in reflection.
pub trait ProtobufValue : Any + AsAny + AsProtobufValue + 'static + Send + Sync {
}

impl ProtobufValue for u32 {
}

impl ProtobufValue for u64 {
}

impl ProtobufValue for i32 {
}

impl ProtobufValue for i64 {
}

impl ProtobufValue for f32 {
}

impl ProtobufValue for f64 {
}

impl ProtobufValue for bool {
}

impl ProtobufValue for String {
}

impl ProtobufValue for Vec<u8> {
}

#[cfg(feature = "bytes")]
impl ProtobufValue for Bytes {
}

#[cfg(feature = "bytes")]
impl ProtobufValue for Chars {
}

// conflicting implementations, so generated code is used instead
/*
impl<E : ProtobufEnum> ProtobufValue for E {
}

impl<M : Message> ProtobufValue for M {
}
*/

#[derive(Debug)]
pub enum ReflectValueRef<'a> {
    U32(u32),
    U64(u64),
    I32(i32),
    I64(i64),
    F32(f32),
    F64(f64),
    Bool(bool),
    String(&'a str),
    Bytes(&'a [u8]),
    Enum(&'static EnumValueDescriptor),
    Message(&'a Message),
}

impl<'a> ReflectValueRef<'a> {
    pub fn is_non_zero(&self) -> bool {
        match *self {
            ReflectValueRef::U32(v) => v != 0,
            ReflectValueRef::U64(v) => v != 0,
            ReflectValueRef::I32(v) => v != 0,
            ReflectValueRef::I64(v) => v != 0,
            ReflectValueRef::F32(v) => v != 0.,
            ReflectValueRef::F64(v) => v != 0.,
            ReflectValueRef::Bool(v) => v,
            ReflectValueRef::String(v) => !v.is_empty(),
            ReflectValueRef::Bytes(v) => !v.is_empty(),
            ReflectValueRef::Enum(v) => v.value() != 0,
            ReflectValueRef::Message(_) => true,
        }
    }

    // Clone to box
    pub fn to_box(&self) -> ReflectValueBox {
        match *self {
            ReflectValueRef::U32(v) => ReflectValueBox::U32(v),
            ReflectValueRef::U64(v) => ReflectValueBox::U64(v),
            ReflectValueRef::I32(v) => ReflectValueBox::I32(v),
            ReflectValueRef::I64(v) => ReflectValueBox::I64(v),
            ReflectValueRef::F32(v) => ReflectValueBox::F32(v),
            ReflectValueRef::F64(v) => ReflectValueBox::F64(v),
            ReflectValueRef::Bool(v) => ReflectValueBox::Bool(v),
            ReflectValueRef::String(v) => ReflectValueBox::String(v.to_owned()),
            ReflectValueRef::Bytes(v) => ReflectValueBox::Bytes(v.to_owned()),
            ReflectValueRef::Enum(v) => ReflectValueBox::Enum(v),
            ReflectValueRef::Message(v) => ReflectValueBox::Message(v.descriptor().clone(v)),
        }
    }
}

#[derive(Debug, Clone)]
pub enum ReflectValueBox {
    U32(u32),
    U64(u64),
    I32(i32),
    I64(i64),
    F32(f32),
    F64(f64),
    Bool(bool),
    String(String),
    Bytes(Vec<u8>),
    Enum(&'static EnumValueDescriptor),
    Message(Box<Message>),
}

fn _assert_value_box_send_sync() {
    fn _assert_send_sync<T : Send + Sync>() {}
    _assert_send_sync::<ReflectValueBox>();
}

#[cfg(not(feature = "bytes"))]
type VecU8OrBytes = Vec<u8>;
#[cfg(feature = "bytes")]
type VecU8OrBytes = Vec<u8>;
#[cfg(not(feature = "bytes"))]
type StringOrChars = String;
#[cfg(feature = "bytes")]
type StringOrChars = Chars;

impl ReflectValueBox {
    pub fn as_value_ref(&self) -> ReflectValueRef {
        use std::ops::Deref;
        match *self {
            ReflectValueBox::U32(v) => ReflectValueRef::U32(v),
            ReflectValueBox::U64(v) => ReflectValueRef::U64(v),
            ReflectValueBox::I32(v) => ReflectValueRef::I32(v),
            ReflectValueBox::I64(v) => ReflectValueRef::I64(v),
            ReflectValueBox::F32(v) => ReflectValueRef::F32(v),
            ReflectValueBox::F64(v) => ReflectValueRef::F64(v),
            ReflectValueBox::Bool(v) => ReflectValueRef::Bool(v),
            ReflectValueBox::String(ref v) => ReflectValueRef::String(v.as_str()),
            ReflectValueBox::Bytes(ref v) => ReflectValueRef::Bytes(v.as_slice()),
            ReflectValueBox::Enum(v) => ReflectValueRef::Enum(v),
            ReflectValueBox::Message(ref v) => ReflectValueRef::Message(v.deref()),
        }
    }

    pub fn as_value(&self) -> &ProtobufValue {
        match self {
            ReflectValueBox::U32(v) => v,
            ReflectValueBox::U64(v) => v,
            ReflectValueBox::I32(v) => v,
            ReflectValueBox::I64(v) => v,
            ReflectValueBox::F32(v) => v,
            ReflectValueBox::F64(v) => v,
            ReflectValueBox::Bool(v) => v,
            ReflectValueBox::String(v) => v,
            ReflectValueBox::Bytes(v) => v,
            ReflectValueBox::Enum(v) => v.protobuf_value(),
            ReflectValueBox::Message(v) => v.as_protobuf_value(),
        }
    }

    pub fn as_value_mut(&mut self) -> &mut ProtobufValue {
        match self {
            ReflectValueBox::U32(v) => v,
            ReflectValueBox::U64(v) => v,
            ReflectValueBox::I32(v) => v,
            ReflectValueBox::I64(v) => v,
            ReflectValueBox::F32(v) => v,
            ReflectValueBox::F64(v) => v,
            ReflectValueBox::Bool(v) => v,
            ReflectValueBox::String(v) => v,
            ReflectValueBox::Bytes(v) => v,
            ReflectValueBox::Enum(_v) => panic!("enum value cannot be mutable"),
            ReflectValueBox::Message(v) => v.as_protobuf_value_mut(),
        }
    }

    pub fn downcast<V : 'static>(self) -> Result<V, Self> {
        match self {
            ReflectValueBox::U32(v) => transmute_eq(v).map_err(ReflectValueBox::U32),
            ReflectValueBox::U64(v) => transmute_eq(v).map_err(ReflectValueBox::U64),
            ReflectValueBox::I32(v) => transmute_eq(v).map_err(ReflectValueBox::I32),
            ReflectValueBox::I64(v) => transmute_eq(v).map_err(ReflectValueBox::I64),
            ReflectValueBox::F32(v) => transmute_eq(v).map_err(ReflectValueBox::F32),
            ReflectValueBox::F64(v) => transmute_eq(v).map_err(ReflectValueBox::F64),
            ReflectValueBox::Bool(v) => transmute_eq(v).map_err(ReflectValueBox::Bool),
            ReflectValueBox::String(v) => {
                transmute_eq::<String, _>(v)
                    .or_else(|v: String| transmute_eq::<StringOrChars, _>(v.into()))
                    .map_err(|v: StringOrChars| ReflectValueBox::String(v.into()))
            },
            ReflectValueBox::Bytes(v) => {
                transmute_eq::<Vec<u8>, _>(v)
                    .or_else(|v: Vec<u8>| transmute_eq::<VecU8OrBytes, _>(v.into()))
                    .map_err(|v: VecU8OrBytes| ReflectValueBox::Bytes(v.into()))
            },
            ReflectValueBox::Enum(e) => {
                e.cast().ok_or(ReflectValueBox::Enum(e))
            }
            ReflectValueBox::Message(m) => {
                m.descriptor().cast(m).map_err(ReflectValueBox::Message)
            }
        }
    }
}

impl<'a> PartialEq for ReflectValueRef<'a> {
    fn eq(&self, other: &ReflectValueRef) -> bool {
        use self::ReflectValueRef::*;
        match (self, other) {
            (U32(a), U32(b)) => a == b,
            (U64(a), U64(b)) => a == b,
            (I32(a), I32(b)) => a == b,
            (I64(a), I64(b)) => a == b,
            // should probably NaN == NaN here
            (F32(a), F32(b)) => a == b,
            (F64(a), F64(b)) => a == b,
            (Bool(a), Bool(b)) => a == b,
            (String(a), String(b)) => a == b,
            (Bytes(a), Bytes(b)) => a == b,
            (Enum(a), Enum(b)) => a == b,
            (Message(a), Message(b)) => {
                use std::ops::Deref;
                a.descriptor() == b.descriptor() && a.descriptor().eq(a.deref(), b.deref())
            },
            _ => false,
        }
    }
}

impl<'a> PartialEq for ReflectValueBox {
    fn eq(&self, other: &Self) -> bool {
        self.as_value_ref() == other.as_value_ref()
    }
}

impl<'a> PartialEq<ReflectValueRef<'a>> for ReflectValueBox {
    fn eq(&self, other: &ReflectValueRef) -> bool {
        self.as_value_ref() == *other
    }
}

impl<'a> PartialEq<ReflectValueBox> for ReflectValueRef<'a> {
    fn eq(&self, other: &ReflectValueBox) -> bool {
        *self == other.as_value_ref()
    }
}

// Panics if contained type is not hashable
impl<'a> Hash for ReflectValueRef<'a> {
    fn hash<H : Hasher>(&self, state: &mut H) {
        use self::ReflectValueRef::*;
        Hash::hash(&mem::discriminant(self), state);
        match self {
            U32(v) => Hash::hash(&v, state),
            U64(v) => Hash::hash(&v, state),
            I32(v) => Hash::hash(&v, state),
            I64(v) => Hash::hash(&v, state),
            Bool(v) => Hash::hash(&v, state),
            String(v) => Hash::hash(&v, state),
            Bytes(v) => Hash::hash(&v, state),
            Enum(v) => Hash::hash(v, state),
            F32(_) | F64(_) | Message(_) => {
                panic!("not hashable: {:?}", self)
            }
        }
    }
}

// Panics if contained type is not hashable
impl Hash for ReflectValueBox {
    fn hash<H : Hasher>(&self, state: &mut H) {
        Hash::hash(&self.as_value_ref(), state)
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn reflect_value_box_downcast_primitive() {
        assert_eq!(Ok(10), ReflectValueBox::U32(10).downcast::<u32>());
        assert_eq!(Err(ReflectValueBox::I32(10)), ReflectValueBox::I32(10).downcast::<u32>());
    }

    #[test]
    fn reflect_value_box_downcast_string() {
        assert_eq!(
            Ok("aa".to_owned()),
            ReflectValueBox::String("aa".to_owned()).downcast::<String>());
        assert_eq!(
            Err(ReflectValueBox::String("aa".to_owned())),
            ReflectValueBox::String("aa".to_owned()).downcast::<u32>());
        assert_eq!(
            Err(ReflectValueBox::Bool(false)),
            ReflectValueBox::Bool(false).downcast::<String>());
    }

    #[test]
    fn reflect_value_box_downcast_chars() {
        assert_eq!(
            Ok(StringOrChars::from("aa".to_owned())),
            ReflectValueBox::String("aa".to_owned()).downcast::<StringOrChars>());
        assert_eq!(
            Err(ReflectValueBox::String("aa".to_owned())),
            ReflectValueBox::String("aa".to_owned()).downcast::<u32>());
        assert_eq!(
            Err(ReflectValueBox::Bool(false)),
            ReflectValueBox::Bool(false).downcast::<StringOrChars>());
    }

}
<|endoftext|>


<commit_before>// A version of pstree targetting linux written in rust!
//
// This is based on the following exercise from the excellent
// book "The Linux Programming Interface" by Michael Kerridsk.
//
//----------------------------------------------------------------------
//
// Write a program that draws a tree showing the hierarchical
// parent-child relationships of all processes on the system, going all
// the way back to init.  For each process, the program should display
// the process ID and the command being executed.  The output of the
// program should be similar to that produced by pstree(1), although it
// does need not to be as sophisticated.  The parent of each process on
// the system can be found by inspecing the PPid: line of all of the
// /proc/PID/status files on the system.  Be careful to handle the
// possibilty that a process's parent (and thus its /proc/PID directory)
// disappears during the scan of all /proc/PID directories.

// Implementation Notes
// --------------------
// The linux /proc filesystem is a virtual filesystem that provides information
// about processes running on a linux system among other things.  The /proc
// filesystem contains a directory, /proc/<pid>, for each running process in
// the system.
//
// Each process directory has a status file with contents including a bunch
// of different items, notably the process name and its parent process id (ppid).
// And with that information, we can build the process tree.

use std::io::fs::PathExtensions;
use std::io::fs;
use std::io::File;
use std::io::BufferedReader;
use std::collections::hashmap::HashMap;

fn process_status_file(status_path: &Path) {
    let mut status_file = BufferedReader::new(File::open(status_path));
    let mut status_data = HashMap::new();
    for line in status_file.lines() {
        let linetext = match line {
            Err(why) => fail!("{}", why.desc),
            Ok(l) => l
        };
        let parts: Vec<&str> = linetext.as_slice().splitn(2, ':').collect();
        if parts.len() == 2 {
            let key = parts[0].trim();
            let value = parts[1].trim();
            status_data.insert(key.to_string(), value.to_string());
        };
    }

    let name_key = &("Name".to_string());
    let pid_key = &("Pid".to_string());
    let ppid_key = &("PPid".to_string());

    if status_data.contains_key(name_key) &&
        status_data.contains_key(pid_key) &&
        status_data.contains_key(ppid_key) {
            println!("{}#{} -> {}",
                     status_data.get(name_key),
                     status_data.get(pid_key),
                     status_data.get(ppid_key));
        }
}

fn dump_process_info() {
    let proc_directory = Path::new("/proc");
    let proc_directory_contents = match fs::readdir(&proc_directory) {
        Err(why) => fail!("{}", why.desc),
        Ok(res) => res
    };
    for entry in proc_directory_contents.iter() {
        if entry.is_dir() {
            let status_path = entry.join("status");
            if status_path.exists() {
                process_status_file(&status_path);
            }
        }
    }
}

fn main() {
    dump_process_info();
}
<commit_msg>pstree: lots of fiddling and learning the language<commit_after>// A version of pstree targetting linux written in rust!
//
// This is based on the following exercise from the excellent
// book "The Linux Programming Interface" by Michael Kerridsk.
//
//----------------------------------------------------------------------
//
// Write a program that draws a tree showing the hierarchical
// parent-child relationships of all processes on the system, going all
// the way back to init.  For each process, the program should display
// the process ID and the command being executed.  The output of the
// program should be similar to that produced by pstree(1), although it
// does need not to be as sophisticated.  The parent of each process on
// the system can be found by inspecing the PPid: line of all of the
// /proc/PID/status files on the system.  Be careful to handle the
// possibilty that a process's parent (and thus its /proc/PID directory)
// disappears during the scan of all /proc/PID directories.

// Implementation Notes
// --------------------
// The linux /proc filesystem is a virtual filesystem that provides information
// about processes running on a linux system among other things.  The /proc
// filesystem contains a directory, /proc/<pid>, for each running process in
// the system.
//
// Each process directory has a status file with contents including a bunch
// of different items, notably the process name and its parent process id (ppid).
// And with that information, we can build the process tree.

use std::io::fs::PathExtensions;
use std::io::fs;
use std::io::File;
use std::io::BufferedReader;
use std::fmt;

struct ProcessRecord {
    name: String,
    pid: int,
    ppid: int
}

impl fmt::Show for ProcessRecord {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ProcessRecord [ name: {}, pid: {}, ppid: {} )",
               self.name,
               self.pid,
               self.ppid)
    }
}

// Given a status file path, return a hashmap with the following form:
// pid -> ProcessRecord
fn get_status_info(status_path: &Path) -> Option<ProcessRecord> {
    let mut pid : Option<int> = None;
    let mut ppid : Option<int> = None;
    let mut name : Option<String> = None;

    let mut status_file = BufferedReader::new(File::open(status_path));
    for line in status_file.lines() {
        let unwrapped = line.unwrap(); // need a new lifeline
        let parts : Vec<&str> = unwrapped.as_slice().splitn(2, ':').collect();
        if parts.len() == 2 {
            let key = parts[0].trim();
            let value = parts[1].trim();
            match key {
                "Name" => name = Some(value.to_string()),
                "Pid" => pid = from_str(value),
                "PPid" => ppid = from_str(value),
                _ => (),
            }
        }
    }

    return if pid.is_some() && ppid.is_some() && name.is_some() {
        Some(ProcessRecord { name: name.unwrap(), pid: pid.unwrap(), ppid: ppid.unwrap() })
    } else {
        None
    }
}

fn dump_process_info() {
    let proc_directory = Path::new("/proc");
    let proc_directory_contents = match fs::readdir(&proc_directory) {
        Err(why) => fail!("{}", why.desc),
        Ok(res) => res
    };
    for entry in proc_directory_contents.iter() {
        if entry.is_dir() {
            let status_path = entry.join("status");
            if status_path.exists() {
                let record = get_status_info(&status_path);
                match record {
                    Some(record) => println!("{}", record),
                    None => ()
                }
            }
        }
    }
}

fn main() {
    dump_process_info();
}
<|endoftext|>


<commit_before>use crate::base::{
    ast::{self, MutVisitor, SpannedExpr, SpannedIdent},
    fnv::{FnvMap, FnvSet},
    pos::{BytePos, Span},
    symbol::Symbol,
    types::{self, AppVec, ArcType, BuiltinType, Generic, Type, TypeInterner},
};

use crate::{substitution::Substitution, typ::RcType, typecheck::Typecheck};

pub(crate) struct TypeGeneralizer<'a, 'b: 'a> {
    level: u32,
    unbound_variables: FnvMap<u32, RcType>,
    /// We delay updating the substitution until after all recursive bindings have been typechecked
    /// to ensure that they get can figure out which variable got generalized for each binding
    delayed_generalizations: Vec<(u32, RcType)>,
    variable_generator: TypeVariableGenerator,
    pub tc: &'a mut Typecheck<'b>,
    span: Span<BytePos>,
}

impl<'a, 'b> Drop for TypeGeneralizer<'a, 'b> {
    fn drop(&mut self) {
        for (id, gen) in self.delayed_generalizations.drain(..) {
            self.tc.subs.replace(id, gen);
        }
    }
}

impl<'a, 'b> ::std::ops::Deref for TypeGeneralizer<'a, 'b> {
    type Target = Typecheck<'b>;
    fn deref(&self) -> &Typecheck<'b> {
        self.tc
    }
}

impl<'a, 'b> ::std::ops::DerefMut for TypeGeneralizer<'a, 'b> {
    fn deref_mut(&mut self) -> &mut Typecheck<'b> {
        self.tc
    }
}

impl<'a, 'b> TypeInterner<Symbol, RcType> for TypeGeneralizer<'a, 'b> {
    fn intern(&mut self, typ: Type<Symbol, RcType>) -> RcType {
        (&self.tc.subs).intern(typ)
    }
}

impl<'a, 'b> TypeGeneralizer<'a, 'b> {
    pub(crate) fn new(
        level: u32,
        tc: &'a mut Typecheck<'b>,
        typ: &RcType,
        span: Span<BytePos>,
    ) -> TypeGeneralizer<'a, 'b> {
        TypeGeneralizer {
            level,
            unbound_variables: Default::default(),
            delayed_generalizations: Vec::new(),
            variable_generator: TypeVariableGenerator::new(&tc.subs, typ),
            tc,
            span,
        }
    }

    /// Generalizing updates all variables which are above `level` into "generic variables". A
    /// generic variable is instantiated with a fresh type variable each time it is refered to.
    ///
    /// Generalzing is a crucial part when inferring types as types will otherwise be less general
    /// than they need to. Consider the following expression.
    /// ```f#
    /// let id x = x
    /// in id 2
    /// ```
    /// If the variable in `id` was not generalized before the `id 2` call the variable would be
    /// unified to an `Int` which is not what we want. Generalazing before checking the body of the
    /// `let` basically infers that the variables in `id` does not refer to anything outside the
    /// `let` scope and can thus be "generalized" into `a -> a` which is instantiated with a fresh
    /// type variable in the `id 2` call.
    pub(crate) fn generalize_variables<'i>(
        &mut self,
        args: &mut Iterator<Item = &'i mut SpannedIdent<Symbol>>,
        expr: &mut SpannedExpr<Symbol>,
    ) {
        self.tc.type_variables.enter_scope();

        // Replaces all type variables with their inferred types
        struct ReplaceVisitor<'a: 'c, 'b: 'a, 'c> {
            generalizer: &'c mut TypeGeneralizer<'a, 'b>,
        }

        impl<'a, 'b, 'c, 'd> MutVisitor<'d> for ReplaceVisitor<'a, 'b, 'c> {
            type Ident = Symbol;

            fn visit_expr(&mut self, e: &'d mut SpannedExpr<Self::Ident>) {
                self.generalizer.tc.type_variables.enter_scope();
                self.generalizer.span = e.span;
                ast::walk_mut_expr(self, e);
                self.generalizer.tc.type_variables.exit_scope();
            }

            fn visit_spanned_typed_ident(&mut self, id: &mut SpannedIdent<Symbol>) {
                self.generalizer.span = id.span;
                self.visit_ident(&mut id.value)
            }
            fn visit_typ(&mut self, typ: &mut ArcType) {
                if let Type::Variable(var) = &**typ {
                    let ref typ = self.generalizer.tc.subs.arc_real(typ).clone();
                    {
                        let type_cache = &self.generalizer.tc.type_cache;
                        self.generalizer.tc.type_variables.extend(
                            typ.forall_params()
                                .map(|param| (param.id.clone(), type_cache.hole())),
                        );
                    }
                    debug!("Variable generalize {}", typ);
                    if let Some(typ) = self.generalizer.generalize_type(typ) {
                        debug!("End generalize {}", typ);
                        self.generalizer.tc.subs.replace(var.id, typ);
                    } else {
                        debug!("End variable generalize");
                    }
                }
            }
        }

        {
            let mut visitor = ReplaceVisitor { generalizer: self };
            visitor.visit_expr(expr);
            for arg in &mut *args {
                visitor.visit_typ(&mut arg.value.typ)
            }
        }

        self.tc.type_variables.exit_scope();
    }

    pub(crate) fn generalize_type_top(&mut self, typ: &mut RcType) {
        self.tc.type_variables.enter_scope();

        let mut result_type = self.generalize_type(typ);

        self.tc.type_variables.exit_scope();

        if result_type.is_none() && !self.unbound_variables.is_empty() {
            result_type = Some(typ.clone());
        }

        result_type = result_type.map(|typ| {
            let delayed_generalizations = &mut self.delayed_generalizations;
            let mut params = self
                .unbound_variables
                .drain()
                .map(|(id, generic)| {
                    let t = match &*generic {
                        Type::Generic(g) => g.clone(),
                        _ => unreachable!(),
                    };
                    delayed_generalizations.push((id, generic));
                    t
                })
                .collect::<Vec<_>>();
            params.sort_unstable_by(|l, r| l.id.declared_name().cmp(r.id.declared_name()));

            self.tc.forall(params, typ)
        });
        if let Some(finished) = result_type {
            *typ = finished;
        }
        debug!("End generalize {}", typ);
    }

    pub(crate) fn generalize_type(&mut self, typ: &RcType) -> Option<RcType> {
        let replacement = self.subs.replace_variable(typ);
        let mut typ = typ;
        if let Some(ref t) = replacement {
            typ = t;
        }
        trace!("GEN: {}", typ);

        if !typ.needs_generalize() {
            trace!("No need to generalize: {}", typ);
            return replacement;
        }

        let new_type = match **typ {
            Type::Variable(ref var) if self.subs.get_level(var.id) >= self.level => {
                let Self {
                    tc,
                    variable_generator,
                    ..
                } = self;
                let gen = self.unbound_variables.entry(var.id).or_insert_with(|| {
                    // Create a prefix if none exists
                    let id = variable_generator.next_variable(tc);

                    let gen: RcType = tc.generic(Generic::new(id.clone(), var.kind.clone()));
                    debug!("Gen {} to {}", var.id, gen);

                    gen
                });

                Some(gen.clone())
            }

            Type::Forall(ref params, ref typ) => {
                self.type_variables.enter_scope();
                let hole = self.hole();
                self.type_variables
                    .extend(params.iter().map(|param| (param.id.clone(), hole.clone())));

                trace!("Generalize forall => `{}`", typ);

                let new_type = self.generalize_type(&typ);
                self.type_variables.exit_scope();

                Some(self.forall(params.clone(), new_type.unwrap_or_else(|| typ.clone())))
            }

            Type::Skolem(ref skolem) => {
                let in_scope = self.type_variables.contains_key(&skolem.name);
                if self.subs.get_level(skolem.id) >= self.level {
                    let generic = self.generic(Generic {
                        id: skolem.name.clone(),
                        kind: skolem.kind.clone(),
                    });

                    if !in_scope {
                        self.unbound_variables
                            .insert(skolem.id.clone(), generic.clone());
                    }

                    Some(generic)
                } else {
                    replacement.clone()
                }
            }

            _ => {
                // Ensure that the forall's variables don't look unbound
                if let Type::Forall(ref params, _) = **typ {
                    let type_cache = &self.tc.type_cache;
                    self.tc.type_variables.extend(
                        params
                            .iter()
                            .map(|param| (param.id.clone(), type_cache.hole())),
                    );
                }

                types::walk_move_type_opt(
                    typ,
                    &mut types::InternerVisitor::control(self, |self_, typ: &RcType| {
                        self_.generalize_type(typ)
                    }),
                )
                .map(|t| unroll_typ(self.tc, &t).unwrap_or(t))
                .or_else(|| replacement.clone())
            }
        };

        new_type
    }
}

struct TypeVariableGenerator {
    map: FnvSet<Symbol>,
    name: String,
    i: u32,
}

impl TypeVariableGenerator {
    fn new(subs: &Substitution<RcType>, typ: &RcType) -> TypeVariableGenerator {
        fn gather_foralls(map: &mut FnvSet<Symbol>, subs: &Substitution<RcType>, typ: &RcType) {
            let typ = subs.real(typ);
            if let Type::Forall(ref params, _) = **typ {
                map.extend(params.iter().map(|param| param.id.clone()));
            }
            types::walk_type_(
                typ,
                &mut types::ControlVisitation(|typ: &RcType| {
                    gather_foralls(map, subs, typ);
                }),
            );
        }
        let mut map = FnvSet::default();
        gather_foralls(&mut map, subs, typ);
        TypeVariableGenerator {
            map,
            name: "".to_string(),
            i: 0,
        }
    }
    /// Generate a generic variable name which is not used in the current scope
    fn next_variable(&mut self, tc: &mut Typecheck) -> Symbol {
        let symbol = if self.name.is_empty() {
            self.next_variable_(tc)
        } else {
            let name = format!("{}{}", self.name, self.i);
            self.i += 1;
            tc.symbols.symbol(&name[..])
        };
        self.map.insert(symbol.clone());
        let hole = tc.hole();
        tc.type_variables.insert(symbol.clone(), hole);
        symbol
    }

    fn next_variable_(&mut self, tc: &mut Typecheck) -> Symbol {
        for c in b'a'..(b'z' + 1) {
            self.name.push(c as char);
            let symbol = tc.symbols.symbol(&self.name[..]);
            if !self.map.contains(&symbol) && tc.type_variables.get(&symbol).is_none() {
                return symbol;
            }
            self.name.pop();
        }
        self.name.push('a');
        self.next_variable_(tc)
    }
}

fn unroll_typ(interner: &mut impl TypeInterner<Symbol, RcType>, typ: &RcType) -> Option<RcType> {
    let mut args = AppVec::new();
    let mut current = match **typ {
        Type::App(ref l, ref rest) => {
            // No need to unroll if `l` is not an application as that will just result in returning
            // an application that is identical to `typ`
            match **l {
                Type::App(..) => (),
                _ => return None,
            }
            args.extend(rest.iter().rev().cloned());
            l
        }
        _ => return unroll_record(interner, typ),
    };
    while let Type::App(ref l, ref rest) = **current {
        args.extend(rest.iter().rev().cloned());
        current = l;
    }
    if args.is_empty() {
        None
    } else {
        args.reverse();
        match **current {
            Type::Builtin(BuiltinType::Function) if args.len() == 2 => {
                let ret = args.pop().unwrap();
                Some(interner.function(args, ret))
            }
            _ => Some(interner.app(current.clone(), args)),
        }
    }
}

fn unroll_record(
    interner: &mut impl TypeInterner<Symbol, RcType>,
    typ: &Type<Symbol, RcType>,
) -> Option<RcType> {
    let mut new_types = Vec::new();
    let mut new_fields = Vec::new();
    let mut current = match *typ {
        Type::ExtendRow {
            ref types,
            ref fields,
            ref rest,
        } => match **rest {
            Type::ExtendRow { .. } => {
                new_types.extend_from_slice(types);
                new_fields.extend_from_slice(fields);
                rest
            }
            _ => return None,
        },
        _ => return None,
    };
    while let Type::ExtendRow {
        ref types,
        ref fields,
        ref rest,
    } = **current
    {
        new_types.extend_from_slice(types);
        new_fields.extend_from_slice(fields);
        current = rest;
    }
    if new_types.is_empty() && new_fields.is_empty() {
        None
    } else {
        Some(interner.extend_row(new_types, new_fields, current.clone()))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use crate::base::types::SharedInterner;

    #[test]
    fn unroll_typ_test() {
        let mut interner = SharedInterner::default();

        let i: RcType = interner.int();
        let s: RcType = interner.string();
        assert_eq!(
            unroll_typ(
                &mut &interner,
                &(&interner).app(
                    (&interner).app(i.clone(), collect![s.clone()]),
                    collect![i.clone()]
                )
            ),
            Some(interner.app(i.clone(), collect![s.clone(), i.clone()]))
        );
        assert_eq!(
            unroll_typ(
                &mut &interner,
                &(&interner).app(
                    (&interner).app(i.clone(), collect![i.clone()]),
                    collect![s.clone()]
                )
            ),
            Some(interner.app(i.clone(), collect![i.clone(), s.clone()]))
        );
        let f: RcType = interner.function_builtin();
        assert_eq!(
            unroll_typ(
                &mut &interner,
                &(&interner).app(
                    (&interner).app(f.clone(), collect![i.clone()]),
                    collect![s.clone()]
                )
            ),
            Some(interner.function(vec![i.clone()], s.clone()))
        );
    }
}
<commit_msg>refactor: Remove some duplication from generalize_type<commit_after>use crate::base::{
    ast::{self, MutVisitor, SpannedExpr, SpannedIdent},
    fnv::{FnvMap, FnvSet},
    pos::{BytePos, Span},
    symbol::Symbol,
    types::{self, AppVec, ArcType, BuiltinType, Generic, Type, TypeInterner},
};

use crate::{substitution::Substitution, typ::RcType, typecheck::Typecheck};

pub(crate) struct TypeGeneralizer<'a, 'b: 'a> {
    level: u32,
    unbound_variables: FnvMap<u32, RcType>,
    /// We delay updating the substitution until after all recursive bindings have been typechecked
    /// to ensure that they get can figure out which variable got generalized for each binding
    delayed_generalizations: Vec<(u32, RcType)>,
    variable_generator: TypeVariableGenerator,
    pub tc: &'a mut Typecheck<'b>,
    span: Span<BytePos>,
}

impl<'a, 'b> Drop for TypeGeneralizer<'a, 'b> {
    fn drop(&mut self) {
        for (id, gen) in self.delayed_generalizations.drain(..) {
            self.tc.subs.replace(id, gen);
        }
    }
}

impl<'a, 'b> ::std::ops::Deref for TypeGeneralizer<'a, 'b> {
    type Target = Typecheck<'b>;
    fn deref(&self) -> &Typecheck<'b> {
        self.tc
    }
}

impl<'a, 'b> ::std::ops::DerefMut for TypeGeneralizer<'a, 'b> {
    fn deref_mut(&mut self) -> &mut Typecheck<'b> {
        self.tc
    }
}

impl<'a, 'b> TypeInterner<Symbol, RcType> for TypeGeneralizer<'a, 'b> {
    fn intern(&mut self, typ: Type<Symbol, RcType>) -> RcType {
        (&self.tc.subs).intern(typ)
    }
}

impl<'a, 'b> TypeGeneralizer<'a, 'b> {
    pub(crate) fn new(
        level: u32,
        tc: &'a mut Typecheck<'b>,
        typ: &RcType,
        span: Span<BytePos>,
    ) -> TypeGeneralizer<'a, 'b> {
        TypeGeneralizer {
            level,
            unbound_variables: Default::default(),
            delayed_generalizations: Vec::new(),
            variable_generator: TypeVariableGenerator::new(&tc.subs, typ),
            tc,
            span,
        }
    }

    /// Generalizing updates all variables which are above `level` into "generic variables". A
    /// generic variable is instantiated with a fresh type variable each time it is refered to.
    ///
    /// Generalzing is a crucial part when inferring types as types will otherwise be less general
    /// than they need to. Consider the following expression.
    /// ```f#
    /// let id x = x
    /// in id 2
    /// ```
    /// If the variable in `id` was not generalized before the `id 2` call the variable would be
    /// unified to an `Int` which is not what we want. Generalazing before checking the body of the
    /// `let` basically infers that the variables in `id` does not refer to anything outside the
    /// `let` scope and can thus be "generalized" into `a -> a` which is instantiated with a fresh
    /// type variable in the `id 2` call.
    pub(crate) fn generalize_variables<'i>(
        &mut self,
        args: &mut Iterator<Item = &'i mut SpannedIdent<Symbol>>,
        expr: &mut SpannedExpr<Symbol>,
    ) {
        self.tc.type_variables.enter_scope();

        // Replaces all type variables with their inferred types
        struct ReplaceVisitor<'a: 'c, 'b: 'a, 'c> {
            generalizer: &'c mut TypeGeneralizer<'a, 'b>,
        }

        impl<'a, 'b, 'c, 'd> MutVisitor<'d> for ReplaceVisitor<'a, 'b, 'c> {
            type Ident = Symbol;

            fn visit_expr(&mut self, e: &'d mut SpannedExpr<Self::Ident>) {
                self.generalizer.tc.type_variables.enter_scope();
                self.generalizer.span = e.span;
                ast::walk_mut_expr(self, e);
                self.generalizer.tc.type_variables.exit_scope();
            }

            fn visit_spanned_typed_ident(&mut self, id: &mut SpannedIdent<Symbol>) {
                self.generalizer.span = id.span;
                self.visit_ident(&mut id.value)
            }
            fn visit_typ(&mut self, typ: &mut ArcType) {
                if let Type::Variable(var) = &**typ {
                    let ref typ = self.generalizer.tc.subs.arc_real(typ).clone();
                    {
                        let type_cache = &self.generalizer.tc.type_cache;
                        self.generalizer.tc.type_variables.extend(
                            typ.forall_params()
                                .map(|param| (param.id.clone(), type_cache.hole())),
                        );
                    }
                    debug!("Variable generalize {}", typ);
                    if let Some(typ) = self.generalizer.generalize_type(typ) {
                        debug!("End generalize {}", typ);
                        self.generalizer.tc.subs.replace(var.id, typ);
                    } else {
                        debug!("End variable generalize");
                    }
                }
            }
        }

        {
            let mut visitor = ReplaceVisitor { generalizer: self };
            visitor.visit_expr(expr);
            for arg in &mut *args {
                visitor.visit_typ(&mut arg.value.typ)
            }
        }

        self.tc.type_variables.exit_scope();
    }

    pub(crate) fn generalize_type_top(&mut self, typ: &mut RcType) {
        self.tc.type_variables.enter_scope();

        let mut result_type = self.generalize_type(typ);

        self.tc.type_variables.exit_scope();

        if result_type.is_none() && !self.unbound_variables.is_empty() {
            result_type = Some(typ.clone());
        }

        result_type = result_type.map(|typ| {
            let delayed_generalizations = &mut self.delayed_generalizations;
            let mut params = self
                .unbound_variables
                .drain()
                .map(|(id, generic)| {
                    let t = match &*generic {
                        Type::Generic(g) => g.clone(),
                        _ => unreachable!(),
                    };
                    delayed_generalizations.push((id, generic));
                    t
                })
                .collect::<Vec<_>>();
            params.sort_unstable_by(|l, r| l.id.declared_name().cmp(r.id.declared_name()));

            self.tc.forall(params, typ)
        });
        if let Some(finished) = result_type {
            *typ = finished;
        }
        debug!("End generalize {}", typ);
    }

    pub(crate) fn generalize_type(&mut self, typ: &RcType) -> Option<RcType> {
        let replacement = self.subs.replace_variable(typ);
        let mut typ = typ;
        if let Some(ref t) = replacement {
            typ = t;
        }
        trace!("GEN: {}", typ);

        if !typ.needs_generalize() {
            trace!("No need to generalize: {}", typ);
            return replacement;
        }

        let new_type = match **typ {
            Type::Variable(ref var) if self.subs.get_level(var.id) >= self.level => {
                let Self {
                    tc,
                    variable_generator,
                    ..
                } = self;
                let gen = self.unbound_variables.entry(var.id).or_insert_with(|| {
                    // Create a prefix if none exists
                    let id = variable_generator.next_variable(tc);

                    let gen: RcType = tc.generic(Generic::new(id.clone(), var.kind.clone()));
                    debug!("Gen {} to {}", var.id, gen);

                    gen
                });

                Some(gen.clone())
            }

            Type::Skolem(ref skolem) => {
                let in_scope = self.type_variables.contains_key(&skolem.name);
                if self.subs.get_level(skolem.id) >= self.level {
                    let generic = self.generic(Generic {
                        id: skolem.name.clone(),
                        kind: skolem.kind.clone(),
                    });

                    if !in_scope {
                        self.unbound_variables
                            .insert(skolem.id.clone(), generic.clone());
                    }

                    Some(generic)
                } else {
                    replacement.clone()
                }
            }

            _ => {
                self.type_variables.enter_scope();
                // Ensure that the forall's variables don't look unbound
                if let Type::Forall(ref params, _) = **typ {
                    let type_cache = &self.tc.type_cache;
                    self.tc.type_variables.extend(
                        params
                            .iter()
                            .map(|param| (param.id.clone(), type_cache.hole())),
                    );
                }

                let new_type = types::walk_move_type_opt(
                    typ,
                    &mut types::InternerVisitor::control(self, |self_, typ: &RcType| {
                        self_.generalize_type(typ)
                    }),
                )
                .map(|t| unroll_typ(self.tc, &t).unwrap_or(t))
                .or_else(|| replacement.clone());

                self.type_variables.exit_scope();

                new_type
            }
        };

        new_type
    }
}

struct TypeVariableGenerator {
    map: FnvSet<Symbol>,
    name: String,
    i: u32,
}

impl TypeVariableGenerator {
    fn new(subs: &Substitution<RcType>, typ: &RcType) -> TypeVariableGenerator {
        fn gather_foralls(map: &mut FnvSet<Symbol>, subs: &Substitution<RcType>, typ: &RcType) {
            let typ = subs.real(typ);
            if let Type::Forall(ref params, _) = **typ {
                map.extend(params.iter().map(|param| param.id.clone()));
            }
            types::walk_type_(
                typ,
                &mut types::ControlVisitation(|typ: &RcType| {
                    gather_foralls(map, subs, typ);
                }),
            );
        }
        let mut map = FnvSet::default();
        gather_foralls(&mut map, subs, typ);
        TypeVariableGenerator {
            map,
            name: "".to_string(),
            i: 0,
        }
    }
    /// Generate a generic variable name which is not used in the current scope
    fn next_variable(&mut self, tc: &mut Typecheck) -> Symbol {
        let symbol = if self.name.is_empty() {
            self.next_variable_(tc)
        } else {
            let name = format!("{}{}", self.name, self.i);
            self.i += 1;
            tc.symbols.symbol(&name[..])
        };
        self.map.insert(symbol.clone());
        let hole = tc.hole();
        tc.type_variables.insert(symbol.clone(), hole);
        symbol
    }

    fn next_variable_(&mut self, tc: &mut Typecheck) -> Symbol {
        for c in b'a'..(b'z' + 1) {
            self.name.push(c as char);
            let symbol = tc.symbols.symbol(&self.name[..]);
            if !self.map.contains(&symbol) && tc.type_variables.get(&symbol).is_none() {
                return symbol;
            }
            self.name.pop();
        }
        self.name.push('a');
        self.next_variable_(tc)
    }
}

fn unroll_typ(interner: &mut impl TypeInterner<Symbol, RcType>, typ: &RcType) -> Option<RcType> {
    let mut args = AppVec::new();
    let mut current = match **typ {
        Type::App(ref l, ref rest) => {
            // No need to unroll if `l` is not an application as that will just result in returning
            // an application that is identical to `typ`
            match **l {
                Type::App(..) => (),
                _ => return None,
            }
            args.extend(rest.iter().rev().cloned());
            l
        }
        _ => return unroll_record(interner, typ),
    };
    while let Type::App(ref l, ref rest) = **current {
        args.extend(rest.iter().rev().cloned());
        current = l;
    }
    if args.is_empty() {
        None
    } else {
        args.reverse();
        match **current {
            Type::Builtin(BuiltinType::Function) if args.len() == 2 => {
                let ret = args.pop().unwrap();
                Some(interner.function(args, ret))
            }
            _ => Some(interner.app(current.clone(), args)),
        }
    }
}

fn unroll_record(
    interner: &mut impl TypeInterner<Symbol, RcType>,
    typ: &Type<Symbol, RcType>,
) -> Option<RcType> {
    let mut new_types = Vec::new();
    let mut new_fields = Vec::new();
    let mut current = match *typ {
        Type::ExtendRow {
            ref types,
            ref fields,
            ref rest,
        } => match **rest {
            Type::ExtendRow { .. } => {
                new_types.extend_from_slice(types);
                new_fields.extend_from_slice(fields);
                rest
            }
            _ => return None,
        },
        _ => return None,
    };
    while let Type::ExtendRow {
        ref types,
        ref fields,
        ref rest,
    } = **current
    {
        new_types.extend_from_slice(types);
        new_fields.extend_from_slice(fields);
        current = rest;
    }
    if new_types.is_empty() && new_fields.is_empty() {
        None
    } else {
        Some(interner.extend_row(new_types, new_fields, current.clone()))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use crate::base::types::SharedInterner;

    #[test]
    fn unroll_typ_test() {
        let mut interner = SharedInterner::default();

        let i: RcType = interner.int();
        let s: RcType = interner.string();
        assert_eq!(
            unroll_typ(
                &mut &interner,
                &(&interner).app(
                    (&interner).app(i.clone(), collect![s.clone()]),
                    collect![i.clone()]
                )
            ),
            Some(interner.app(i.clone(), collect![s.clone(), i.clone()]))
        );
        assert_eq!(
            unroll_typ(
                &mut &interner,
                &(&interner).app(
                    (&interner).app(i.clone(), collect![i.clone()]),
                    collect![s.clone()]
                )
            ),
            Some(interner.app(i.clone(), collect![i.clone(), s.clone()]))
        );
        let f: RcType = interner.function_builtin();
        assert_eq!(
            unroll_typ(
                &mut &interner,
                &(&interner).app(
                    (&interner).app(f.clone(), collect![i.clone()]),
                    collect![s.clone()]
                )
            ),
            Some(interner.function(vec![i.clone()], s.clone()))
        );
    }
}
<|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/. */

//! A centralized set of stylesheets for a document.

use dom::TElement;
use invalidation::stylesheets::StylesheetInvalidationSet;
use media_queries::Device;
use shared_lock::SharedRwLockReadGuard;
use std::slice;
use stylesheets::{Origin, OriginSet, StylesheetInDocument};

/// Entry for a StylesheetSet. We don't bother creating a constructor, because
/// there's no sensible defaults for the member variables.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
struct StylesheetSetEntry<S>
where
    S: StylesheetInDocument + PartialEq + 'static,
{
    sheet: S,
}

impl<S> StylesheetSetEntry<S>
where
    S: StylesheetInDocument + PartialEq + 'static,
{
    fn new(sheet: S) -> Self {
        Self { sheet }
    }
}

/// A iterator over the stylesheets of a list of entries in the StylesheetSet.
#[derive(Clone)]
pub struct StylesheetIterator<'a, S>(slice::Iter<'a, StylesheetSetEntry<S>>)
where
    S: StylesheetInDocument + PartialEq + 'static;

impl<'a, S> Iterator for StylesheetIterator<'a, S>
where
    S: StylesheetInDocument + PartialEq + 'static,
{
    type Item = &'a S;

    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().map(|entry| &entry.sheet)
    }
}

/// A struct to iterate over the different stylesheets to be flushed.
pub struct StylesheetFlusher<'a, 'b, S>
where
    'b: 'a,
    S: StylesheetInDocument + PartialEq + 'static,
{
    iter: slice::IterMut<'a, StylesheetSetEntry<S>>,
    guard: &'a SharedRwLockReadGuard<'b>,
    origins_dirty: OriginSet,
    author_style_disabled: bool,
    had_invalidations: bool,
}

/// The type of rebuild that we need to do for a given stylesheet.
pub enum SheetRebuildKind {
    /// For now we only support full rebuilds, in the future we'll implement
    /// partial rebuilds.
    Full,
}

impl<'a, 'b, S> StylesheetFlusher<'a, 'b, S>
where
    'b: 'a,
    S: StylesheetInDocument + PartialEq + 'static,
{
    /// The set of origins to fully rebuild, which need to be cleared
    /// beforehand.
    pub fn origins_to_fully_rebuild(&self) -> OriginSet {
        self.origins_dirty
    }

    /// Returns whether running the whole flushing process would be a no-op.
    pub fn nothing_to_do(&self) -> bool {
        self.origins_dirty.is_empty()
    }

    /// Returns whether any DOM invalidations were processed as a result of the
    /// stylesheet flush.
    pub fn had_invalidations(&self) -> bool {
        self.had_invalidations
    }
}

impl<'a, 'b, S> Iterator for StylesheetFlusher<'a, 'b, S>
where
    'b: 'a,
    S: StylesheetInDocument + PartialEq + 'static,
{
    type Item = (&'a S, SheetRebuildKind);

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            let potential_sheet = match self.iter.next() {
                None => return None,
                Some(s) => s,
            };

            let origin = potential_sheet.sheet.contents(self.guard).origin;
            if !self.origins_dirty.contains(origin.into()) {
                continue;
            }

            if self.author_style_disabled && matches!(origin, Origin::Author) {
                continue;
            }

            return Some((&potential_sheet.sheet, SheetRebuildKind::Full))
        }
    }
}

/// The set of stylesheets effective for a given document.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct StylesheetSet<S>
where
    S: StylesheetInDocument + PartialEq + 'static,
{
    /// The actual list of all the stylesheets that apply to the given document,
    /// each stylesheet associated with a unique ID.
    ///
    /// This is only a list of top-level stylesheets, and as such it doesn't
    /// include recursive `@import` rules.
    entries: Vec<StylesheetSetEntry<S>>,

    /// The invalidations for stylesheets added or removed from this document.
    invalidations: StylesheetInvalidationSet,

    /// The origins whose stylesheets have changed so far.
    origins_dirty: OriginSet,

    /// Has author style been disabled?
    author_style_disabled: bool,
}

impl<S> StylesheetSet<S>
where
    S: StylesheetInDocument + PartialEq + 'static,
{
    /// Create a new empty StylesheetSet.
    pub fn new() -> Self {
        StylesheetSet {
            entries: vec![],
            invalidations: StylesheetInvalidationSet::new(),
            origins_dirty: OriginSet::empty(),
            author_style_disabled: false,
        }
    }

    /// Returns the number of stylesheets in the set.
    pub fn len(&self) -> usize {
        self.entries.len()
    }

    /// Returns the number of stylesheets in the set.
    pub fn get(&self, index: usize) -> Option<&S> {
        self.entries.get(index).map(|s| &s.sheet)
    }

    /// Returns whether author styles have been disabled for the current
    /// stylesheet set.
    pub fn author_style_disabled(&self) -> bool {
        self.author_style_disabled
    }

    fn remove_stylesheet_if_present(&mut self, sheet: &S) {
        self.entries.retain(|entry| entry.sheet != *sheet);
    }

    fn collect_invalidations_for(
        &mut self,
        device: Option<&Device>,
        sheet: &S,
        guard: &SharedRwLockReadGuard,
    ) {
        if let Some(device) = device {
            self.invalidations.collect_invalidations_for(device, sheet, guard);
        }
        self.origins_dirty |= sheet.contents(guard).origin;
    }

    /// Appends a new stylesheet to the current set.
    ///
    /// No device implies not computing invalidations.
    pub fn append_stylesheet(
        &mut self,
        device: Option<&Device>,
        sheet: S,
        guard: &SharedRwLockReadGuard
    ) {
        debug!("StylesheetSet::append_stylesheet");
        self.remove_stylesheet_if_present(&sheet);
        self.collect_invalidations_for(device, &sheet, guard);
        self.entries.push(StylesheetSetEntry::new(sheet));
    }

    /// Prepend a new stylesheet to the current set.
    pub fn prepend_stylesheet(
        &mut self,
        device: Option<&Device>,
        sheet: S,
        guard: &SharedRwLockReadGuard
    ) {
        debug!("StylesheetSet::prepend_stylesheet");
        self.remove_stylesheet_if_present(&sheet);
        self.collect_invalidations_for(device, &sheet, guard);
        self.entries.insert(0, StylesheetSetEntry::new(sheet));
    }

    /// Insert a given stylesheet before another stylesheet in the document.
    pub fn insert_stylesheet_before(
        &mut self,
        device: Option<&Device>,
        sheet: S,
        before_sheet: S,
        guard: &SharedRwLockReadGuard,
    ) {
        debug!("StylesheetSet::insert_stylesheet_before");
        self.remove_stylesheet_if_present(&sheet);
        let index = self.entries.iter().position(|entry| {
            entry.sheet == before_sheet
        }).expect("`before_sheet` stylesheet not found");
        self.collect_invalidations_for(device, &sheet, guard);
        self.entries.insert(index, StylesheetSetEntry::new(sheet));
    }

    /// Remove a given stylesheet from the set.
    pub fn remove_stylesheet(
        &mut self,
        device: Option<&Device>,
        sheet: S,
        guard: &SharedRwLockReadGuard,
    ) {
        debug!("StylesheetSet::remove_stylesheet");
        self.remove_stylesheet_if_present(&sheet);
        self.collect_invalidations_for(device, &sheet, guard);
    }

    /// Notes that the author style has been disabled for this document.
    pub fn set_author_style_disabled(&mut self, disabled: bool) {
        debug!("StylesheetSet::set_author_style_disabled");
        if self.author_style_disabled == disabled {
            return;
        }
        self.author_style_disabled = disabled;
        self.invalidations.invalidate_fully();
        self.origins_dirty |= Origin::Author;
    }

    /// Returns whether the given set has changed from the last flush.
    pub fn has_changed(&self) -> bool {
        !self.origins_dirty.is_empty()
    }

    /// Flush the current set, unmarking it as dirty, and returns a
    /// `StylesheetFlusher` in order to rebuild the stylist.
    pub fn flush<'a, 'b, E>(
        &'a mut self,
        document_element: Option<E>,
        guard: &'a SharedRwLockReadGuard<'b>,
    ) -> StylesheetFlusher<'a, 'b, S>
    where
        E: TElement,
    {
        use std::mem;

        debug!("StylesheetSet::flush");

        let had_invalidations = self.invalidations.flush(document_element);
        let origins_dirty = mem::replace(&mut self.origins_dirty, OriginSet::empty());

        StylesheetFlusher {
            iter: self.entries.iter_mut(),
            author_style_disabled: self.author_style_disabled,
            had_invalidations,
            origins_dirty,
            guard,
        }
    }

    /// Flush stylesheets, but without running any of the invalidation passes.
    #[cfg(feature = "servo")]
    pub fn flush_without_invalidation(&mut self) -> OriginSet {
        use std::mem;

        debug!("StylesheetSet::flush_without_invalidation");

        self.invalidations.clear();
        mem::replace(&mut self.origins_dirty, OriginSet::empty())
    }

    /// Returns an iterator over the current list of stylesheets.
    pub fn iter(&self) -> StylesheetIterator<S> {
        StylesheetIterator(self.entries.iter())
    }

    /// Mark the stylesheets for the specified origin as dirty, because
    /// something external may have invalidated it.
    pub fn force_dirty(&mut self, origins: OriginSet) {
        self.invalidations.invalidate_fully();
        self.origins_dirty |= origins;
    }
}
<commit_msg>style: Ensure the flusher is completely consumed before getting dropped.<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/. */

//! A centralized set of stylesheets for a document.

use dom::TElement;
use invalidation::stylesheets::StylesheetInvalidationSet;
use media_queries::Device;
use shared_lock::SharedRwLockReadGuard;
use std::slice;
use stylesheets::{Origin, OriginSet, StylesheetInDocument};

/// Entry for a StylesheetSet. We don't bother creating a constructor, because
/// there's no sensible defaults for the member variables.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
struct StylesheetSetEntry<S>
where
    S: StylesheetInDocument + PartialEq + 'static,
{
    sheet: S,
}

impl<S> StylesheetSetEntry<S>
where
    S: StylesheetInDocument + PartialEq + 'static,
{
    fn new(sheet: S) -> Self {
        Self { sheet }
    }
}

/// A iterator over the stylesheets of a list of entries in the StylesheetSet.
#[derive(Clone)]
pub struct StylesheetIterator<'a, S>(slice::Iter<'a, StylesheetSetEntry<S>>)
where
    S: StylesheetInDocument + PartialEq + 'static;

impl<'a, S> Iterator for StylesheetIterator<'a, S>
where
    S: StylesheetInDocument + PartialEq + 'static,
{
    type Item = &'a S;

    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().map(|entry| &entry.sheet)
    }
}

/// A struct to iterate over the different stylesheets to be flushed.
pub struct StylesheetFlusher<'a, 'b, S>
where
    'b: 'a,
    S: StylesheetInDocument + PartialEq + 'static,
{
    iter: slice::IterMut<'a, StylesheetSetEntry<S>>,
    guard: &'a SharedRwLockReadGuard<'b>,
    origins_dirty: OriginSet,
    author_style_disabled: bool,
    had_invalidations: bool,
}

/// The type of rebuild that we need to do for a given stylesheet.
pub enum SheetRebuildKind {
    /// For now we only support full rebuilds, in the future we'll implement
    /// partial rebuilds.
    Full,
}

impl<'a, 'b, S> StylesheetFlusher<'a, 'b, S>
where
    'b: 'a,
    S: StylesheetInDocument + PartialEq + 'static,
{
    /// The set of origins to fully rebuild, which need to be cleared
    /// beforehand.
    pub fn origins_to_fully_rebuild(&self) -> OriginSet {
        self.origins_dirty
    }

    /// Returns whether running the whole flushing process would be a no-op.
    pub fn nothing_to_do(&self) -> bool {
        self.origins_dirty.is_empty()
    }

    /// Returns whether any DOM invalidations were processed as a result of the
    /// stylesheet flush.
    pub fn had_invalidations(&self) -> bool {
        self.had_invalidations
    }
}

#[cfg(debug_assertions)]
impl<'a, 'b, S> Drop for StylesheetFlusher<'a, 'b, S>
where
    'b: 'a,
    S: StylesheetInDocument + PartialEq + 'static,
{
    fn drop(&mut self) {
        debug_assert!(
            self.iter.next().is_none(),
            "You're supposed to fully consume the flusher",
        );
    }
}

impl<'a, 'b, S> Iterator for StylesheetFlusher<'a, 'b, S>
where
    'b: 'a,
    S: StylesheetInDocument + PartialEq + 'static,
{
    type Item = (&'a S, SheetRebuildKind);

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            let potential_sheet = match self.iter.next() {
                None => return None,
                Some(s) => s,
            };

            let origin = potential_sheet.sheet.contents(self.guard).origin;
            if !self.origins_dirty.contains(origin.into()) {
                continue;
            }

            if self.author_style_disabled && matches!(origin, Origin::Author) {
                continue;
            }

            return Some((&potential_sheet.sheet, SheetRebuildKind::Full))
        }
    }
}

/// The set of stylesheets effective for a given document.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct StylesheetSet<S>
where
    S: StylesheetInDocument + PartialEq + 'static,
{
    /// The actual list of all the stylesheets that apply to the given document,
    /// each stylesheet associated with a unique ID.
    ///
    /// This is only a list of top-level stylesheets, and as such it doesn't
    /// include recursive `@import` rules.
    entries: Vec<StylesheetSetEntry<S>>,

    /// The invalidations for stylesheets added or removed from this document.
    invalidations: StylesheetInvalidationSet,

    /// The origins whose stylesheets have changed so far.
    origins_dirty: OriginSet,

    /// Has author style been disabled?
    author_style_disabled: bool,
}

impl<S> StylesheetSet<S>
where
    S: StylesheetInDocument + PartialEq + 'static,
{
    /// Create a new empty StylesheetSet.
    pub fn new() -> Self {
        StylesheetSet {
            entries: vec![],
            invalidations: StylesheetInvalidationSet::new(),
            origins_dirty: OriginSet::empty(),
            author_style_disabled: false,
        }
    }

    /// Returns the number of stylesheets in the set.
    pub fn len(&self) -> usize {
        self.entries.len()
    }

    /// Returns the number of stylesheets in the set.
    pub fn get(&self, index: usize) -> Option<&S> {
        self.entries.get(index).map(|s| &s.sheet)
    }

    /// Returns whether author styles have been disabled for the current
    /// stylesheet set.
    pub fn author_style_disabled(&self) -> bool {
        self.author_style_disabled
    }

    fn remove_stylesheet_if_present(&mut self, sheet: &S) {
        self.entries.retain(|entry| entry.sheet != *sheet);
    }

    fn collect_invalidations_for(
        &mut self,
        device: Option<&Device>,
        sheet: &S,
        guard: &SharedRwLockReadGuard,
    ) {
        if let Some(device) = device {
            self.invalidations.collect_invalidations_for(device, sheet, guard);
        }
        self.origins_dirty |= sheet.contents(guard).origin;
    }

    /// Appends a new stylesheet to the current set.
    ///
    /// No device implies not computing invalidations.
    pub fn append_stylesheet(
        &mut self,
        device: Option<&Device>,
        sheet: S,
        guard: &SharedRwLockReadGuard
    ) {
        debug!("StylesheetSet::append_stylesheet");
        self.remove_stylesheet_if_present(&sheet);
        self.collect_invalidations_for(device, &sheet, guard);
        self.entries.push(StylesheetSetEntry::new(sheet));
    }

    /// Prepend a new stylesheet to the current set.
    pub fn prepend_stylesheet(
        &mut self,
        device: Option<&Device>,
        sheet: S,
        guard: &SharedRwLockReadGuard
    ) {
        debug!("StylesheetSet::prepend_stylesheet");
        self.remove_stylesheet_if_present(&sheet);
        self.collect_invalidations_for(device, &sheet, guard);
        self.entries.insert(0, StylesheetSetEntry::new(sheet));
    }

    /// Insert a given stylesheet before another stylesheet in the document.
    pub fn insert_stylesheet_before(
        &mut self,
        device: Option<&Device>,
        sheet: S,
        before_sheet: S,
        guard: &SharedRwLockReadGuard,
    ) {
        debug!("StylesheetSet::insert_stylesheet_before");
        self.remove_stylesheet_if_present(&sheet);
        let index = self.entries.iter().position(|entry| {
            entry.sheet == before_sheet
        }).expect("`before_sheet` stylesheet not found");
        self.collect_invalidations_for(device, &sheet, guard);
        self.entries.insert(index, StylesheetSetEntry::new(sheet));
    }

    /// Remove a given stylesheet from the set.
    pub fn remove_stylesheet(
        &mut self,
        device: Option<&Device>,
        sheet: S,
        guard: &SharedRwLockReadGuard,
    ) {
        debug!("StylesheetSet::remove_stylesheet");
        self.remove_stylesheet_if_present(&sheet);
        self.collect_invalidations_for(device, &sheet, guard);
    }

    /// Notes that the author style has been disabled for this document.
    pub fn set_author_style_disabled(&mut self, disabled: bool) {
        debug!("StylesheetSet::set_author_style_disabled");
        if self.author_style_disabled == disabled {
            return;
        }
        self.author_style_disabled = disabled;
        self.invalidations.invalidate_fully();
        self.origins_dirty |= Origin::Author;
    }

    /// Returns whether the given set has changed from the last flush.
    pub fn has_changed(&self) -> bool {
        !self.origins_dirty.is_empty()
    }

    /// Flush the current set, unmarking it as dirty, and returns a
    /// `StylesheetFlusher` in order to rebuild the stylist.
    pub fn flush<'a, 'b, E>(
        &'a mut self,
        document_element: Option<E>,
        guard: &'a SharedRwLockReadGuard<'b>,
    ) -> StylesheetFlusher<'a, 'b, S>
    where
        E: TElement,
    {
        use std::mem;

        debug!("StylesheetSet::flush");

        let had_invalidations = self.invalidations.flush(document_element);
        let origins_dirty = mem::replace(&mut self.origins_dirty, OriginSet::empty());

        StylesheetFlusher {
            iter: self.entries.iter_mut(),
            author_style_disabled: self.author_style_disabled,
            had_invalidations,
            origins_dirty,
            guard,
        }
    }

    /// Flush stylesheets, but without running any of the invalidation passes.
    #[cfg(feature = "servo")]
    pub fn flush_without_invalidation(&mut self) -> OriginSet {
        use std::mem;

        debug!("StylesheetSet::flush_without_invalidation");

        self.invalidations.clear();
        mem::replace(&mut self.origins_dirty, OriginSet::empty())
    }

    /// Returns an iterator over the current list of stylesheets.
    pub fn iter(&self) -> StylesheetIterator<S> {
        StylesheetIterator(self.entries.iter())
    }

    /// Mark the stylesheets for the specified origin as dirty, because
    /// something external may have invalidated it.
    pub fn force_dirty(&mut self, origins: OriginSet) {
        self.invalidations.invalidate_fully();
        self.origins_dirty |= origins;
    }
}
<|endoftext|>