<commit_before>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A single-producer single-consumer concurrent queue
//!
//! This module contains the implementation of an SPSC queue which can be used
//! concurrently between two threads. This data structure is safe to use and
//! enforces the semantics that there is one pusher and one popper.

// http://www.1024cores.net/home/lock-free-algorithms/queues/unbounded-spsc-queue

use alloc::boxed::Box;
use core::ptr;
use core::cell::UnsafeCell;

use sync::atomic::{AtomicPtr, AtomicUsize, Ordering};

use super::cache_aligned::CacheAligned;

// Node within the linked list queue of messages to send
struct Node<T> {
    // FIXME: this could be an uninitialized T if we're careful enough, and
    //      that would reduce memory usage (and be a bit faster).
    //      is it worth it?
    value: Option<T>,           // nullable for re-use of nodes
    cached: bool,               // This node goes into the node cache
    next: AtomicPtr<Node<T>>,   // next node in the queue
}

/// The single-producer single-consumer queue. This structure is not cloneable,
/// but it can be safely shared in an Arc if it is guaranteed that there
/// is only one popper and one pusher touching the queue at any one point in
/// time.
pub struct Queue<T, ProducerAddition=(), ConsumerAddition=()> {
    // consumer fields
    consumer: CacheAligned<Consumer<T, ConsumerAddition>>,

    // producer fields
    producer: CacheAligned<Producer<T, ProducerAddition>>,
}

struct Consumer<T, Addition> {
    tail: UnsafeCell<*mut Node<T>>, // where to pop from
    tail_prev: AtomicPtr<Node<T>>, // where to pop from
    cache_bound: usize, // maximum cache size
    cached_nodes: AtomicUsize, // number of nodes marked as cachable
    addition: Addition,
}

struct Producer<T, Addition> {
    head: UnsafeCell<*mut Node<T>>,      // where to push to
    first: UnsafeCell<*mut Node<T>>,     // where to get new nodes from
    tail_copy: UnsafeCell<*mut Node<T>>, // between first/tail
    addition: Addition,
}

unsafe impl<T: Send, P: Send + Sync, C: Send + Sync> Send for Queue<T, P, C> { }

unsafe impl<T: Send, P: Send + Sync, C: Send + Sync> Sync for Queue<T, P, C> { }

impl<T> Node<T> {
    fn new() -> *mut Node<T> {
        Box::into_raw(box Node {
            value: None,
            cached: false,
            next: AtomicPtr::new(ptr::null_mut::<Node<T>>()),
        })
    }
}

impl<T> Queue<T> {
    #[cfg(test)]
    /// Creates a new queue.
    ///
    /// This is unsafe as the type system doesn't enforce a single
    /// consumer-producer relationship. It also allows the consumer to `pop`
    /// items while there is a `peek` active due to all methods having a
    /// non-mutable receiver.
    ///
    /// # Arguments
    ///
    ///   * `bound` - This queue implementation is implemented with a linked
    ///               list, and this means that a push is always a malloc. In
    ///               order to amortize this cost, an internal cache of nodes is
    ///               maintained to prevent a malloc from always being
    ///               necessary. This bound is the limit on the size of the
    ///               cache (if desired). If the value is 0, then the cache has
    ///               no bound. Otherwise, the cache will never grow larger than
    ///               `bound` (although the queue itself could be much larger.
    pub unsafe fn new(bound: usize) -> Queue<T> {
        Self::with_additions(bound, (), ())
    }
}

impl<T, ProducerAddition, ConsumerAddition> Queue<T, ProducerAddition, ConsumerAddition> {

    /// Creates a new queue. With given additional elements in the producer and
    /// consumer portions of the queue.
    ///
    /// Due to the performance implications of cache-contention,
    /// we wish to keep fields used mainly by the producer on a separate cache
    /// line than those used by the consumer.
    /// Since cache lines are usually 64 bytes, it is unreasonably expensive to
    /// allocate one for small fields, so we allow users to insert additional
    /// fields into the cache lines already allocated by this for the producer
    /// and consumer.
    ///
    /// This is unsafe as the type system doesn't enforce a single
    /// consumer-producer relationship. It also allows the consumer to `pop`
    /// items while there is a `peek` active due to all methods having a
    /// non-mutable receiver.
    ///
    /// # Arguments
    ///
    ///   * `bound` - This queue implementation is implemented with a linked
    ///               list, and this means that a push is always a malloc. In
    ///               order to amortize this cost, an internal cache of nodes is
    ///               maintained to prevent a malloc from always being
    ///               necessary. This bound is the limit on the size of the
    ///               cache (if desired). If the value is 0, then the cache has
    ///               no bound. Otherwise, the cache will never grow larger than
    ///               `bound` (although the queue itself could be much larger.
    pub unsafe fn with_additions(
        bound: usize,
        producer_addition: ProducerAddition,
        consumer_addition: ConsumerAddition,
    ) -> Self {
        let n1 = Node::new();
        let n2 = Node::new();
        (*n1).next.store(n2, Ordering::Relaxed);
        Queue {
            consumer: CacheAligned::new(Consumer {
                tail: UnsafeCell::new(n2),
                tail_prev: AtomicPtr::new(n1),
                cache_bound: bound,
                cached_nodes: AtomicUsize::new(0),
                addition: consumer_addition
            }),
            producer: CacheAligned::new(Producer {
                head: UnsafeCell::new(n2),
                first: UnsafeCell::new(n1),
                tail_copy: UnsafeCell::new(n1),
                addition: producer_addition
            }),
        }
    }

    /// Pushes a new value onto this queue. Note that to use this function
    /// safely, it must be externally guaranteed that there is only one pusher.
    pub fn push(&self, t: T) {
        unsafe {
            // Acquire a node (which either uses a cached one or allocates a new
            // one), and then append this to the 'head' node.
            let n = self.alloc();
            assert!((*n).value.is_none());
            (*n).value = Some(t);
            (*n).next.store(ptr::null_mut(), Ordering::Relaxed);
            (**self.producer.head.get()).next.store(n, Ordering::Release);
            *(&self.producer.head).get() = n;
        }
    }

    unsafe fn alloc(&self) -> *mut Node<T> {
        // First try to see if we can consume the 'first' node for our uses.
        if *self.producer.first.get() != *self.producer.tail_copy.get() {
            let ret = *self.producer.first.get();
            *self.producer.0.first.get() = (*ret).next.load(Ordering::Relaxed);
            return ret;
        }
        // If the above fails, then update our copy of the tail and try
        // again.
        *self.producer.0.tail_copy.get() =
            self.consumer.tail_prev.load(Ordering::Acquire);
        if *self.producer.first.get() != *self.producer.tail_copy.get() {
            let ret = *self.producer.first.get();
            *self.producer.0.first.get() = (*ret).next.load(Ordering::Relaxed);
            return ret;
        }
        // If all of that fails, then we have to allocate a new node
        // (there's nothing in the node cache).
        Node::new()
    }

    /// Attempts to pop a value from this queue. Remember that to use this type
    /// safely you must ensure that there is only one popper at a time.
    pub fn pop(&self) -> Option<T> {
        unsafe {
            // The `tail` node is not actually a used node, but rather a
            // sentinel from where we should start popping from. Hence, look at
            // tail's next field and see if we can use it. If we do a pop, then
            // the current tail node is a candidate for going into the cache.
            let tail = *self.consumer.tail.get();
            let next = (*tail).next.load(Ordering::Acquire);
            if next.is_null() { return None }
            assert!((*next).value.is_some());
            let ret = (*next).value.take();

            *self.consumer.0.tail.get() = next;
            if self.consumer.cache_bound == 0 {
                self.consumer.tail_prev.store(tail, Ordering::Release);
            } else {
                let cached_nodes = self.consumer.cached_nodes.load(Ordering::Relaxed);
                if cached_nodes < self.consumer.cache_bound && !(*tail).cached {
                    self.consumer.cached_nodes.store(cached_nodes, Ordering::Relaxed);
                    (*tail).cached = true;
                }

                if (*tail).cached {
                    self.consumer.tail_prev.store(tail, Ordering::Release);
                } else {
                    (*self.consumer.tail_prev.load(Ordering::Relaxed))
                        .next.store(next, Ordering::Relaxed);
                    // We have successfully erased all references to 'tail', so
                    // now we can safely drop it.
                    let _: Box<Node<T>> = Box::from_raw(tail);
                }
            }
            ret
        }
    }

    /// Attempts to peek at the head of the queue, returning `None` if the queue
    /// has no data currently
    ///
    /// # Warning
    /// The reference returned is invalid if it is not used before the consumer
    /// pops the value off the queue. If the producer then pushes another value
    /// onto the queue, it will overwrite the value pointed to by the reference.
    pub fn peek(&self) -> Option<&mut T> {
        // This is essentially the same as above with all the popping bits
        // stripped out.
        unsafe {
            let tail = *self.consumer.tail.get();
            let next = (*tail).next.load(Ordering::Acquire);
            if next.is_null() { None } else { (*next).value.as_mut() }
        }
    }

    pub fn producer_addition(&self) -> &ProducerAddition {
        &self.producer.addition
    }

    pub fn consumer_addition(&self) -> &ConsumerAddition {
        &self.consumer.addition
    }
}

impl<T, ProducerAddition, ConsumerAddition> Drop for Queue<T, ProducerAddition, ConsumerAddition> {
    fn drop(&mut self) {
        unsafe {
            let mut cur = *self.producer.first.get();
            while !cur.is_null() {
                let next = (*cur).next.load(Ordering::Relaxed);
                let _n: Box<Node<T>> = Box::from_raw(cur);
                cur = next;
            }
        }
    }
}

#[cfg(all(test, not(target_os = "emscripten")))]
mod tests {
    use sync::Arc;
    use super::Queue;
    use thread;
    use sync::mpsc::channel;

    #[test]
    fn smoke() {
        unsafe {
            let queue = Queue::new(0);
            queue.push(1);
            queue.push(2);
            assert_eq!(queue.pop(), Some(1));
            assert_eq!(queue.pop(), Some(2));
            assert_eq!(queue.pop(), None);
            queue.push(3);
            queue.push(4);
            assert_eq!(queue.pop(), Some(3));
            assert_eq!(queue.pop(), Some(4));
            assert_eq!(queue.pop(), None);
        }
    }

    #[test]
    fn peek() {
        unsafe {
            let queue = Queue::new(0);
            queue.push(vec![1]);

            // Ensure the borrowchecker works
            match queue.peek() {
                Some(vec) => {
                    assert_eq!(&*vec, &[1]);
                },
                None => unreachable!()
            }

            match queue.pop() {
                Some(vec) => {
                    assert_eq!(&*vec, &[1]);
                },
                None => unreachable!()
            }
        }
    }

    #[test]
    fn drop_full() {
        unsafe {
            let q: Queue<Box<_>> = Queue::new(0);
            q.push(box 1);
            q.push(box 2);
        }
    }

    #[test]
    fn smoke_bound() {
        unsafe {
            let q = Queue::new(0);
            q.push(1);
            q.push(2);
            assert_eq!(q.pop(), Some(1));
            assert_eq!(q.pop(), Some(2));
            assert_eq!(q.pop(), None);
            q.push(3);
            q.push(4);
            assert_eq!(q.pop(), Some(3));
            assert_eq!(q.pop(), Some(4));
            assert_eq!(q.pop(), None);
        }
    }

    #[test]
    fn stress() {
        unsafe {
            stress_bound(0);
            stress_bound(1);
        }

        unsafe fn stress_bound(bound: usize) {
            let q = Arc::new(Queue::new(bound));

            let (tx, rx) = channel();
            let q2 = q.clone();
            let _t = thread::spawn(move|| {
                for _ in 0..100000 {
                    loop {
                        match q2.pop() {
                            Some(1) => break,
                            Some(_) => panic!(),
                            None => {}
                        }
                    }
                }
                tx.send(()).unwrap();
            });
            for _ in 0..100000 {
                q.push(1);
            }
            rx.recv().unwrap();
        }
    }
}
<commit_msg>cfg out Queue::new for emscripten<commit_after>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A single-producer single-consumer concurrent queue
//!
//! This module contains the implementation of an SPSC queue which can be used
//! concurrently between two threads. This data structure is safe to use and
//! enforces the semantics that there is one pusher and one popper.

// http://www.1024cores.net/home/lock-free-algorithms/queues/unbounded-spsc-queue

use alloc::boxed::Box;
use core::ptr;
use core::cell::UnsafeCell;

use sync::atomic::{AtomicPtr, AtomicUsize, Ordering};

use super::cache_aligned::CacheAligned;

// Node within the linked list queue of messages to send
struct Node<T> {
    // FIXME: this could be an uninitialized T if we're careful enough, and
    //      that would reduce memory usage (and be a bit faster).
    //      is it worth it?
    value: Option<T>,           // nullable for re-use of nodes
    cached: bool,               // This node goes into the node cache
    next: AtomicPtr<Node<T>>,   // next node in the queue
}

/// The single-producer single-consumer queue. This structure is not cloneable,
/// but it can be safely shared in an Arc if it is guaranteed that there
/// is only one popper and one pusher touching the queue at any one point in
/// time.
pub struct Queue<T, ProducerAddition=(), ConsumerAddition=()> {
    // consumer fields
    consumer: CacheAligned<Consumer<T, ConsumerAddition>>,

    // producer fields
    producer: CacheAligned<Producer<T, ProducerAddition>>,
}

struct Consumer<T, Addition> {
    tail: UnsafeCell<*mut Node<T>>, // where to pop from
    tail_prev: AtomicPtr<Node<T>>, // where to pop from
    cache_bound: usize, // maximum cache size
    cached_nodes: AtomicUsize, // number of nodes marked as cachable
    addition: Addition,
}

struct Producer<T, Addition> {
    head: UnsafeCell<*mut Node<T>>,      // where to push to
    first: UnsafeCell<*mut Node<T>>,     // where to get new nodes from
    tail_copy: UnsafeCell<*mut Node<T>>, // between first/tail
    addition: Addition,
}

unsafe impl<T: Send, P: Send + Sync, C: Send + Sync> Send for Queue<T, P, C> { }

unsafe impl<T: Send, P: Send + Sync, C: Send + Sync> Sync for Queue<T, P, C> { }

impl<T> Node<T> {
    fn new() -> *mut Node<T> {
        Box::into_raw(box Node {
            value: None,
            cached: false,
            next: AtomicPtr::new(ptr::null_mut::<Node<T>>()),
        })
    }
}

impl<T> Queue<T> {
    #[cfg(all(test, not(target_os = "emscripten")))]
    /// Creates a new queue.
    ///
    /// This is unsafe as the type system doesn't enforce a single
    /// consumer-producer relationship. It also allows the consumer to `pop`
    /// items while there is a `peek` active due to all methods having a
    /// non-mutable receiver.
    ///
    /// # Arguments
    ///
    ///   * `bound` - This queue implementation is implemented with a linked
    ///               list, and this means that a push is always a malloc. In
    ///               order to amortize this cost, an internal cache of nodes is
    ///               maintained to prevent a malloc from always being
    ///               necessary. This bound is the limit on the size of the
    ///               cache (if desired). If the value is 0, then the cache has
    ///               no bound. Otherwise, the cache will never grow larger than
    ///               `bound` (although the queue itself could be much larger.
    pub unsafe fn new(bound: usize) -> Queue<T> {
        Self::with_additions(bound, (), ())
    }
}

impl<T, ProducerAddition, ConsumerAddition> Queue<T, ProducerAddition, ConsumerAddition> {

    /// Creates a new queue. With given additional elements in the producer and
    /// consumer portions of the queue.
    ///
    /// Due to the performance implications of cache-contention,
    /// we wish to keep fields used mainly by the producer on a separate cache
    /// line than those used by the consumer.
    /// Since cache lines are usually 64 bytes, it is unreasonably expensive to
    /// allocate one for small fields, so we allow users to insert additional
    /// fields into the cache lines already allocated by this for the producer
    /// and consumer.
    ///
    /// This is unsafe as the type system doesn't enforce a single
    /// consumer-producer relationship. It also allows the consumer to `pop`
    /// items while there is a `peek` active due to all methods having a
    /// non-mutable receiver.
    ///
    /// # Arguments
    ///
    ///   * `bound` - This queue implementation is implemented with a linked
    ///               list, and this means that a push is always a malloc. In
    ///               order to amortize this cost, an internal cache of nodes is
    ///               maintained to prevent a malloc from always being
    ///               necessary. This bound is the limit on the size of the
    ///               cache (if desired). If the value is 0, then the cache has
    ///               no bound. Otherwise, the cache will never grow larger than
    ///               `bound` (although the queue itself could be much larger.
    pub unsafe fn with_additions(
        bound: usize,
        producer_addition: ProducerAddition,
        consumer_addition: ConsumerAddition,
    ) -> Self {
        let n1 = Node::new();
        let n2 = Node::new();
        (*n1).next.store(n2, Ordering::Relaxed);
        Queue {
            consumer: CacheAligned::new(Consumer {
                tail: UnsafeCell::new(n2),
                tail_prev: AtomicPtr::new(n1),
                cache_bound: bound,
                cached_nodes: AtomicUsize::new(0),
                addition: consumer_addition
            }),
            producer: CacheAligned::new(Producer {
                head: UnsafeCell::new(n2),
                first: UnsafeCell::new(n1),
                tail_copy: UnsafeCell::new(n1),
                addition: producer_addition
            }),
        }
    }

    /// Pushes a new value onto this queue. Note that to use this function
    /// safely, it must be externally guaranteed that there is only one pusher.
    pub fn push(&self, t: T) {
        unsafe {
            // Acquire a node (which either uses a cached one or allocates a new
            // one), and then append this to the 'head' node.
            let n = self.alloc();
            assert!((*n).value.is_none());
            (*n).value = Some(t);
            (*n).next.store(ptr::null_mut(), Ordering::Relaxed);
            (**self.producer.head.get()).next.store(n, Ordering::Release);
            *(&self.producer.head).get() = n;
        }
    }

    unsafe fn alloc(&self) -> *mut Node<T> {
        // First try to see if we can consume the 'first' node for our uses.
        if *self.producer.first.get() != *self.producer.tail_copy.get() {
            let ret = *self.producer.first.get();
            *self.producer.0.first.get() = (*ret).next.load(Ordering::Relaxed);
            return ret;
        }
        // If the above fails, then update our copy of the tail and try
        // again.
        *self.producer.0.tail_copy.get() =
            self.consumer.tail_prev.load(Ordering::Acquire);
        if *self.producer.first.get() != *self.producer.tail_copy.get() {
            let ret = *self.producer.first.get();
            *self.producer.0.first.get() = (*ret).next.load(Ordering::Relaxed);
            return ret;
        }
        // If all of that fails, then we have to allocate a new node
        // (there's nothing in the node cache).
        Node::new()
    }

    /// Attempts to pop a value from this queue. Remember that to use this type
    /// safely you must ensure that there is only one popper at a time.
    pub fn pop(&self) -> Option<T> {
        unsafe {
            // The `tail` node is not actually a used node, but rather a
            // sentinel from where we should start popping from. Hence, look at
            // tail's next field and see if we can use it. If we do a pop, then
            // the current tail node is a candidate for going into the cache.
            let tail = *self.consumer.tail.get();
            let next = (*tail).next.load(Ordering::Acquire);
            if next.is_null() { return None }
            assert!((*next).value.is_some());
            let ret = (*next).value.take();

            *self.consumer.0.tail.get() = next;
            if self.consumer.cache_bound == 0 {
                self.consumer.tail_prev.store(tail, Ordering::Release);
            } else {
                let cached_nodes = self.consumer.cached_nodes.load(Ordering::Relaxed);
                if cached_nodes < self.consumer.cache_bound && !(*tail).cached {
                    self.consumer.cached_nodes.store(cached_nodes, Ordering::Relaxed);
                    (*tail).cached = true;
                }

                if (*tail).cached {
                    self.consumer.tail_prev.store(tail, Ordering::Release);
                } else {
                    (*self.consumer.tail_prev.load(Ordering::Relaxed))
                        .next.store(next, Ordering::Relaxed);
                    // We have successfully erased all references to 'tail', so
                    // now we can safely drop it.
                    let _: Box<Node<T>> = Box::from_raw(tail);
                }
            }
            ret
        }
    }

    /// Attempts to peek at the head of the queue, returning `None` if the queue
    /// has no data currently
    ///
    /// # Warning
    /// The reference returned is invalid if it is not used before the consumer
    /// pops the value off the queue. If the producer then pushes another value
    /// onto the queue, it will overwrite the value pointed to by the reference.
    pub fn peek(&self) -> Option<&mut T> {
        // This is essentially the same as above with all the popping bits
        // stripped out.
        unsafe {
            let tail = *self.consumer.tail.get();
            let next = (*tail).next.load(Ordering::Acquire);
            if next.is_null() { None } else { (*next).value.as_mut() }
        }
    }

    pub fn producer_addition(&self) -> &ProducerAddition {
        &self.producer.addition
    }

    pub fn consumer_addition(&self) -> &ConsumerAddition {
        &self.consumer.addition
    }
}

impl<T, ProducerAddition, ConsumerAddition> Drop for Queue<T, ProducerAddition, ConsumerAddition> {
    fn drop(&mut self) {
        unsafe {
            let mut cur = *self.producer.first.get();
            while !cur.is_null() {
                let next = (*cur).next.load(Ordering::Relaxed);
                let _n: Box<Node<T>> = Box::from_raw(cur);
                cur = next;
            }
        }
    }
}

#[cfg(all(test, not(target_os = "emscripten")))]
mod tests {
    use sync::Arc;
    use super::Queue;
    use thread;
    use sync::mpsc::channel;

    #[test]
    fn smoke() {
        unsafe {
            let queue = Queue::new(0);
            queue.push(1);
            queue.push(2);
            assert_eq!(queue.pop(), Some(1));
            assert_eq!(queue.pop(), Some(2));
            assert_eq!(queue.pop(), None);
            queue.push(3);
            queue.push(4);
            assert_eq!(queue.pop(), Some(3));
            assert_eq!(queue.pop(), Some(4));
            assert_eq!(queue.pop(), None);
        }
    }

    #[test]
    fn peek() {
        unsafe {
            let queue = Queue::new(0);
            queue.push(vec![1]);

            // Ensure the borrowchecker works
            match queue.peek() {
                Some(vec) => {
                    assert_eq!(&*vec, &[1]);
                },
                None => unreachable!()
            }

            match queue.pop() {
                Some(vec) => {
                    assert_eq!(&*vec, &[1]);
                },
                None => unreachable!()
            }
        }
    }

    #[test]
    fn drop_full() {
        unsafe {
            let q: Queue<Box<_>> = Queue::new(0);
            q.push(box 1);
            q.push(box 2);
        }
    }

    #[test]
    fn smoke_bound() {
        unsafe {
            let q = Queue::new(0);
            q.push(1);
            q.push(2);
            assert_eq!(q.pop(), Some(1));
            assert_eq!(q.pop(), Some(2));
            assert_eq!(q.pop(), None);
            q.push(3);
            q.push(4);
            assert_eq!(q.pop(), Some(3));
            assert_eq!(q.pop(), Some(4));
            assert_eq!(q.pop(), None);
        }
    }

    #[test]
    fn stress() {
        unsafe {
            stress_bound(0);
            stress_bound(1);
        }

        unsafe fn stress_bound(bound: usize) {
            let q = Arc::new(Queue::new(bound));

            let (tx, rx) = channel();
            let q2 = q.clone();
            let _t = thread::spawn(move|| {
                for _ in 0..100000 {
                    loop {
                        match q2.pop() {
                            Some(1) => break,
                            Some(_) => panic!(),
                            None => {}
                        }
                    }
                }
                tx.send(()).unwrap();
            });
            for _ in 0..100000 {
                q.push(1);
            }
            rx.recv().unwrap();
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Unix-specific extensions to primitives in the `std::process` module.

#![stable(feature = "rust1", since = "1.0.0")]

use io;
use os::unix::io::{FromRawFd, RawFd, AsRawFd, IntoRawFd};
use process;
use sys;
use sys_common::{AsInnerMut, AsInner, FromInner, IntoInner};

/// Unix-specific extensions to the `std::process::Command` builder
#[stable(feature = "rust1", since = "1.0.0")]
pub trait CommandExt {
    /// Sets the child process's user id. This translates to a
    /// `setuid` call in the child process. Failure in the `setuid`
    /// call will cause the spawn to fail.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn uid(&mut self, id: u32) -> &mut process::Command;

    /// Similar to `uid`, but sets the group id of the child process. This has
    /// the same semantics as the `uid` field.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn gid(&mut self, id: u32) -> &mut process::Command;

    /// Schedules a closure to be run just before the `exec` function is
    /// invoked.
    ///
    /// The closure is allowed to return an I/O error whose OS error code will
    /// be communicated back to the parent and returned as an error from when
    /// the spawn was requested.
    ///
    /// Multiple closures can be registered and they will be called in order of
    /// their registration. If a closure returns `Err` then no further closures
    /// will be called and the spawn operation will immediately return with a
    /// failure.
    ///
    /// # Notes
    ///
    /// This closure will be run in the context of the child process after a
    /// `fork`. This primarily means that any modifications made to memory on
    /// behalf of this closure will **not** be visible to the parent process.
    /// This is often a very constrained environment where normal operations
    /// like `malloc` or acquiring a mutex are not guaranteed to work (due to
    /// other threads perhaps still running when the `fork` was run).
    ///
    /// When this closure is run, aspects such as the stdio file descriptors and
    /// working directory have successfully been changed, so output to these
    /// locations may not appear where intended.
    #[stable(feature = "process_exec", since = "1.15.0")]
    fn before_exec<F>(&mut self, f: F) -> &mut process::Command
        where F: FnMut() -> io::Result<()> + Send + Sync + 'static;

    /// Performs all the required setup by this `Command`, followed by calling
    /// the `execvp` syscall.
    ///
    /// On success this function will not return, and otherwise it will return
    /// an error indicating why the exec (or another part of the setup of the
    /// `Command`) failed.
    ///
    /// `exec` not returning has the same implications as calling
    /// [`process::exit`] – no destructors on the current stack or any other
    /// thread’s stack will be run. Therefore, it is recommended to only call
    /// `exec` at a point where it is fine to not run any destructors. Note,
    /// that the `execvp` syscall independently guarantees that all memory is
    /// freed and all file descriptors with the `CLOEXEC` option (set by default
    /// on all file descriptors opened by the standard library) are closed.
    ///
    /// This function, unlike `spawn`, will **not** `fork` the process to create
    /// a new child. Like spawn, however, the default behavior for the stdio
    /// descriptors will be to inherited from the current process.
    ///
    /// [`process::exit`]: ../../../process/fn.exit.html
    ///
    /// # Notes
    ///
    /// The process may be in a "broken state" if this function returns in
    /// error. For example the working directory, environment variables, signal
    /// handling settings, various user/group information, or aspects of stdio
    /// file descriptors may have changed. If a "transactional spawn" is
    /// required to gracefully handle errors it is recommended to use the
    /// cross-platform `spawn` instead.
    #[stable(feature = "process_exec2", since = "1.9.0")]
    fn exec(&mut self) -> io::Error;
}

#[stable(feature = "rust1", since = "1.0.0")]
impl CommandExt for process::Command {
    fn uid(&mut self, id: u32) -> &mut process::Command {
        self.as_inner_mut().uid(id);
        self
    }

    fn gid(&mut self, id: u32) -> &mut process::Command {
        self.as_inner_mut().gid(id);
        self
    }

    fn before_exec<F>(&mut self, f: F) -> &mut process::Command
        where F: FnMut() -> io::Result<()> + Send + Sync + 'static
    {
        self.as_inner_mut().before_exec(Box::new(f));
        self
    }

    fn exec(&mut self) -> io::Error {
        self.as_inner_mut().exec(sys::process::Stdio::Inherit)
    }
}

/// Unix-specific extensions to `std::process::ExitStatus`
#[stable(feature = "rust1", since = "1.0.0")]
pub trait ExitStatusExt {
    /// Creates a new `ExitStatus` from the raw underlying `i32` return value of
    /// a process.
    #[stable(feature = "exit_status_from", since = "1.12.0")]
    fn from_raw(raw: i32) -> Self;

    /// If the process was terminated by a signal, returns that signal.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn signal(&self) -> Option<i32>;
}

#[stable(feature = "rust1", since = "1.0.0")]
impl ExitStatusExt for process::ExitStatus {
    fn from_raw(raw: i32) -> Self {
        process::ExitStatus::from_inner(From::from(raw))
    }

    fn signal(&self) -> Option<i32> {
        self.as_inner().signal()
    }
}

#[stable(feature = "process_extensions", since = "1.2.0")]
impl FromRawFd for process::Stdio {
    unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio {
        let fd = sys::fd::FileDesc::new(fd);
        let io = sys::process::Stdio::Fd(fd);
        process::Stdio::from_inner(io)
    }
}

#[stable(feature = "process_extensions", since = "1.2.0")]
impl AsRawFd for process::ChildStdin {
    fn as_raw_fd(&self) -> RawFd {
        self.as_inner().fd().raw()
    }
}

#[stable(feature = "process_extensions", since = "1.2.0")]
impl AsRawFd for process::ChildStdout {
    fn as_raw_fd(&self) -> RawFd {
        self.as_inner().fd().raw()
    }
}

#[stable(feature = "process_extensions", since = "1.2.0")]
impl AsRawFd for process::ChildStderr {
    fn as_raw_fd(&self) -> RawFd {
        self.as_inner().fd().raw()
    }
}

#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawFd for process::ChildStdin {
    fn into_raw_fd(self) -> RawFd {
        self.into_inner().into_fd().into_raw()
    }
}

#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawFd for process::ChildStdout {
    fn into_raw_fd(self) -> RawFd {
        self.into_inner().into_fd().into_raw()
    }
}

#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawFd for process::ChildStderr {
    fn into_raw_fd(self) -> RawFd {
        self.into_inner().into_fd().into_raw()
    }
}

/// Returns the OS-assigned process identifier associated with this process's parent.
#[unstable(feature = "unix_ppid", issue = "46104")]
pub fn parent_id() -> u32 {
    ::sys::os::getppid()
}
<commit_msg>Stabilize parent_id()<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Unix-specific extensions to primitives in the `std::process` module.

#![stable(feature = "rust1", since = "1.0.0")]

use io;
use os::unix::io::{FromRawFd, RawFd, AsRawFd, IntoRawFd};
use process;
use sys;
use sys_common::{AsInnerMut, AsInner, FromInner, IntoInner};

/// Unix-specific extensions to the `std::process::Command` builder
#[stable(feature = "rust1", since = "1.0.0")]
pub trait CommandExt {
    /// Sets the child process's user id. This translates to a
    /// `setuid` call in the child process. Failure in the `setuid`
    /// call will cause the spawn to fail.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn uid(&mut self, id: u32) -> &mut process::Command;

    /// Similar to `uid`, but sets the group id of the child process. This has
    /// the same semantics as the `uid` field.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn gid(&mut self, id: u32) -> &mut process::Command;

    /// Schedules a closure to be run just before the `exec` function is
    /// invoked.
    ///
    /// The closure is allowed to return an I/O error whose OS error code will
    /// be communicated back to the parent and returned as an error from when
    /// the spawn was requested.
    ///
    /// Multiple closures can be registered and they will be called in order of
    /// their registration. If a closure returns `Err` then no further closures
    /// will be called and the spawn operation will immediately return with a
    /// failure.
    ///
    /// # Notes
    ///
    /// This closure will be run in the context of the child process after a
    /// `fork`. This primarily means that any modifications made to memory on
    /// behalf of this closure will **not** be visible to the parent process.
    /// This is often a very constrained environment where normal operations
    /// like `malloc` or acquiring a mutex are not guaranteed to work (due to
    /// other threads perhaps still running when the `fork` was run).
    ///
    /// When this closure is run, aspects such as the stdio file descriptors and
    /// working directory have successfully been changed, so output to these
    /// locations may not appear where intended.
    #[stable(feature = "process_exec", since = "1.15.0")]
    fn before_exec<F>(&mut self, f: F) -> &mut process::Command
        where F: FnMut() -> io::Result<()> + Send + Sync + 'static;

    /// Performs all the required setup by this `Command`, followed by calling
    /// the `execvp` syscall.
    ///
    /// On success this function will not return, and otherwise it will return
    /// an error indicating why the exec (or another part of the setup of the
    /// `Command`) failed.
    ///
    /// `exec` not returning has the same implications as calling
    /// [`process::exit`] – no destructors on the current stack or any other
    /// thread’s stack will be run. Therefore, it is recommended to only call
    /// `exec` at a point where it is fine to not run any destructors. Note,
    /// that the `execvp` syscall independently guarantees that all memory is
    /// freed and all file descriptors with the `CLOEXEC` option (set by default
    /// on all file descriptors opened by the standard library) are closed.
    ///
    /// This function, unlike `spawn`, will **not** `fork` the process to create
    /// a new child. Like spawn, however, the default behavior for the stdio
    /// descriptors will be to inherited from the current process.
    ///
    /// [`process::exit`]: ../../../process/fn.exit.html
    ///
    /// # Notes
    ///
    /// The process may be in a "broken state" if this function returns in
    /// error. For example the working directory, environment variables, signal
    /// handling settings, various user/group information, or aspects of stdio
    /// file descriptors may have changed. If a "transactional spawn" is
    /// required to gracefully handle errors it is recommended to use the
    /// cross-platform `spawn` instead.
    #[stable(feature = "process_exec2", since = "1.9.0")]
    fn exec(&mut self) -> io::Error;
}

#[stable(feature = "rust1", since = "1.0.0")]
impl CommandExt for process::Command {
    fn uid(&mut self, id: u32) -> &mut process::Command {
        self.as_inner_mut().uid(id);
        self
    }

    fn gid(&mut self, id: u32) -> &mut process::Command {
        self.as_inner_mut().gid(id);
        self
    }

    fn before_exec<F>(&mut self, f: F) -> &mut process::Command
        where F: FnMut() -> io::Result<()> + Send + Sync + 'static
    {
        self.as_inner_mut().before_exec(Box::new(f));
        self
    }

    fn exec(&mut self) -> io::Error {
        self.as_inner_mut().exec(sys::process::Stdio::Inherit)
    }
}

/// Unix-specific extensions to `std::process::ExitStatus`
#[stable(feature = "rust1", since = "1.0.0")]
pub trait ExitStatusExt {
    /// Creates a new `ExitStatus` from the raw underlying `i32` return value of
    /// a process.
    #[stable(feature = "exit_status_from", since = "1.12.0")]
    fn from_raw(raw: i32) -> Self;

    /// If the process was terminated by a signal, returns that signal.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn signal(&self) -> Option<i32>;
}

#[stable(feature = "rust1", since = "1.0.0")]
impl ExitStatusExt for process::ExitStatus {
    fn from_raw(raw: i32) -> Self {
        process::ExitStatus::from_inner(From::from(raw))
    }

    fn signal(&self) -> Option<i32> {
        self.as_inner().signal()
    }
}

#[stable(feature = "process_extensions", since = "1.2.0")]
impl FromRawFd for process::Stdio {
    unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio {
        let fd = sys::fd::FileDesc::new(fd);
        let io = sys::process::Stdio::Fd(fd);
        process::Stdio::from_inner(io)
    }
}

#[stable(feature = "process_extensions", since = "1.2.0")]
impl AsRawFd for process::ChildStdin {
    fn as_raw_fd(&self) -> RawFd {
        self.as_inner().fd().raw()
    }
}

#[stable(feature = "process_extensions", since = "1.2.0")]
impl AsRawFd for process::ChildStdout {
    fn as_raw_fd(&self) -> RawFd {
        self.as_inner().fd().raw()
    }
}

#[stable(feature = "process_extensions", since = "1.2.0")]
impl AsRawFd for process::ChildStderr {
    fn as_raw_fd(&self) -> RawFd {
        self.as_inner().fd().raw()
    }
}

#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawFd for process::ChildStdin {
    fn into_raw_fd(self) -> RawFd {
        self.into_inner().into_fd().into_raw()
    }
}

#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawFd for process::ChildStdout {
    fn into_raw_fd(self) -> RawFd {
        self.into_inner().into_fd().into_raw()
    }
}

#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawFd for process::ChildStderr {
    fn into_raw_fd(self) -> RawFd {
        self.into_inner().into_fd().into_raw()
    }
}

/// Returns the OS-assigned process identifier associated with this process's parent.
#[stable(feature = "unix_ppid", since = "1.27.0")]
pub fn parent_id() -> u32 {
    ::sys::os::getppid()
}
<|endoftext|>


<commit_before>import std._vec;
import std._str;
import std.option;
import std.option.some;
import std.option.none;
import std.map.hashmap;

import driver.session;
import ast.ident;
import front.parser.parser;
import front.parser.spanned;
import front.parser.new_parser;
import front.parser.parse_mod_items;
import util.common;
import util.common.filename;
import util.common.append;
import util.common.span;
import util.common.new_str_hash;

// Simple dynamic-typed value type for eval_expr.
tag val {
    val_bool(bool);
    val_int(int);
    val_str(str);
}

type env = vec[tup(ident, val)];

fn mk_env() -> env {
    let env e = vec();
    ret e;
}

fn val_is_bool(val v) -> bool {
    alt (v) {
        case (val_bool(_)) { ret true; }
        case (_) { }
    }
    ret false;
}

fn val_is_int(val v) -> bool {
    alt (v) {
        case (val_bool(_)) { ret true; }
        case (_) { }
    }
    ret false;
}

fn val_is_str(val v) -> bool {
    alt (v) {
        case (val_str(_)) { ret true; }
        case (_) { }
    }
    ret false;
}

fn val_as_bool(val v) -> bool {
    alt (v) {
        case (val_bool(?b)) { ret b; }
        case (_) { }
    }
    fail;
}

fn val_as_int(val v) -> int {
    alt (v) {
        case (val_int(?i)) { ret i; }
        case (_) { }
    }
    fail;
}

fn val_as_str(val v) -> str {
    alt (v) {
        case (val_str(?s)) { ret s; }
        case (_) { }
    }
    fail;
}

fn lookup(session.session sess, env e, span sp, ident i) -> val {
    for (tup(ident, val) pair in e) {
        if (_str.eq(i, pair._0)) {
            ret pair._1;
        }
    }
    sess.span_err(sp, "unknown variable: " + i);
    fail;
}

fn eval_lit(session.session sess, env e, span sp, @ast.lit lit) -> val {
    alt (lit.node) {
        case (ast.lit_bool(?b)) { ret val_bool(b); }
        case (ast.lit_int(?i)) { ret val_int(i); }
        case (ast.lit_str(?s)) { ret val_str(s); }
        case (_) {
            sess.span_err(sp, "evaluating unsupported literal");
        }
    }
    fail;
}

fn eval_expr(session.session sess, env e, @ast.expr x) -> val {
    alt (x.node) {
        case (ast.expr_path(?pth, _, _)) {
            if (_vec.len[ident](pth.node.idents) == 1u &&
                _vec.len[@ast.ty](pth.node.types) == 0u) {
                ret lookup(sess, e, x.span, pth.node.idents.(0));
            }
            sess.span_err(x.span, "evaluating structured path-name");
        }

        case (ast.expr_lit(?lit, _)) {
            ret eval_lit(sess, e, x.span, lit);
        }

        case (ast.expr_unary(?op, ?a, _)) {
            auto av = eval_expr(sess, e, a);
            alt (op) {
                case (ast.not) {
                    if (val_is_bool(av)) {
                        ret val_bool(!val_as_bool(av));
                    }
                    sess.span_err(x.span, "bad types in '!' expression");
                }
                case (_) {
                    sess.span_err(x.span, "evaluating unsupported unop");
                }
            }
        }

        case (ast.expr_binary(?op, ?a, ?b, _)) {
            auto av = eval_expr(sess, e, a);
            auto bv = eval_expr(sess, e, b);
            alt (op) {
                case (ast.add) {
                    if (val_is_int(av) && val_is_int(bv)) {
                        ret val_int(val_as_int(av) + val_as_int(bv));
                    }
                    if (val_is_str(av) && val_is_str(bv)) {
                        ret val_str(val_as_str(av) + val_as_str(bv));
                    }
                    sess.span_err(x.span, "bad types in '+' expression");
                }

                case (ast.sub) {
                    if (val_is_int(av) && val_is_int(bv)) {
                        ret val_int(val_as_int(av) - val_as_int(bv));
                    }
                    sess.span_err(x.span, "bad types in '-' expression");
                }

                case (ast.mul) {
                    if (val_is_int(av) && val_is_int(bv)) {
                        ret val_int(val_as_int(av) * val_as_int(bv));
                    }
                    sess.span_err(x.span, "bad types in '*' expression");
                }

                case (ast.div) {
                    if (val_is_int(av) && val_is_int(bv)) {
                        ret val_int(val_as_int(av) / val_as_int(bv));
                    }
                    sess.span_err(x.span, "bad types in '/' expression");
                }

                case (ast.rem) {
                    if (val_is_int(av) && val_is_int(bv)) {
                        ret val_int(val_as_int(av) % val_as_int(bv));
                    }
                    sess.span_err(x.span, "bad types in '%' expression");
                }

                case (ast.and) {
                    if (val_is_bool(av) && val_is_bool(bv)) {
                        ret val_bool(val_as_bool(av) && val_as_bool(bv));
                    }
                    sess.span_err(x.span, "bad types in '&&' expression");
                }

                case (ast.or) {
                    if (val_is_bool(av) && val_is_bool(bv)) {
                        ret val_bool(val_as_bool(av) || val_as_bool(bv));
                    }
                    sess.span_err(x.span, "bad types in '||' expression");
                }

                case (ast.eq) {
                    ret val_bool(val_eq(sess, x.span, av, bv));
                }

                case (ast.ne) {
                    ret val_bool(! val_eq(sess, x.span, av, bv));
                }

                case (_) {
                    sess.span_err(x.span, "evaluating unsupported binop");
                }
            }
        }
        case (_) {
            sess.span_err(x.span, "evaluating unsupported expression");
        }
    }
    fail;
}

fn val_eq(session.session sess, span sp, val av, val bv) -> bool {
    if (val_is_bool(av) && val_is_bool(bv)) {
        ret val_as_bool(av) == val_as_bool(bv);
    }
    if (val_is_int(av) && val_is_int(bv)) {
        ret val_as_int(av) == val_as_int(bv);
    }
    if (val_is_str(av) && val_is_str(bv)) {
        ret _str.eq(val_as_str(av),
                    val_as_str(bv));
    }
    sess.span_err(sp, "bad types in comparison");
    fail;
}

impure fn eval_crate_directives(parser p,
                                env e,
                                vec[@ast.crate_directive] cdirs,
                                str prefix,
                                &mutable vec[@ast.view_item] view_items,
                                &mutable vec[@ast.item] items,
                                hashmap[ast.ident,
                                        ast.mod_index_entry] index) {

    for (@ast.crate_directive sub_cdir in cdirs) {
        eval_crate_directive(p, e, sub_cdir, prefix,
                             view_items, items, index);
    }
}

impure fn eval_crate_directives_to_mod(parser p,
                                       env e,
                                       vec[@ast.crate_directive] cdirs,
                                       str prefix) -> ast._mod {
    let vec[@ast.view_item] view_items = vec();
    let vec[@ast.item] items = vec();
    auto index = new_str_hash[ast.mod_index_entry]();

    eval_crate_directives(p, e, cdirs, prefix,
                          view_items, items, index);

    ret rec(view_items=view_items, items=items, index=index);
}

impure fn eval_crate_directive_block(parser p,
                                     env e,
                                     &ast.block blk,
                                     str prefix,
                                     &mutable vec[@ast.view_item] view_items,
                                     &mutable vec[@ast.item] items,
                                     hashmap[ast.ident,
                                             ast.mod_index_entry] index) {

    for (@ast.stmt s in blk.node.stmts) {
        alt (s.node) {
            case (ast.stmt_crate_directive(?cdir)) {
                eval_crate_directive(p, e, cdir, prefix,
                                     view_items, items, index);
            }
            case (_) {
                auto sess = p.get_session();
                sess.span_err(s.span,
                              "unsupported stmt in crate-directive block");
            }
        }
    }
}

impure fn eval_crate_directive_expr(parser p,
                                    env e,
                                    @ast.expr x,
                                    str prefix,
                                    &mutable vec[@ast.view_item] view_items,
                                    &mutable vec[@ast.item] items,
                                    hashmap[ast.ident,
                                            ast.mod_index_entry] index) {
    auto sess = p.get_session();

    alt (x.node) {

        case (ast.expr_if(?cond, ?thn, ?elifs, ?elopt, _)) {
            auto cv = eval_expr(sess, e, cond);
            if (!val_is_bool(cv)) {
                sess.span_err(x.span, "bad cond type in 'if'");
            }

            if (val_as_bool(cv)) {
                ret eval_crate_directive_block(p, e, thn, prefix,
                                               view_items, items,
                                               index);
            }

            for (tup(@ast.expr, ast.block) elif in elifs) {
                auto cv = eval_expr(sess, e, elif._0);
                if (!val_is_bool(cv)) {
                    sess.span_err(x.span, "bad cond type in 'else if'");
                }

                if (val_as_bool(cv)) {
                    ret eval_crate_directive_block(p, e, elif._1, prefix,
                                                   view_items, items,
                                                   index);
                }
            }

            alt (elopt) {
                case (some[ast.block](?els)) {
                    ret eval_crate_directive_block(p, e, els, prefix,
                                                   view_items, items,
                                                   index);
                }
                case (_) {
                    // Absent-else is ok.
                }
            }
        }

        case (ast.expr_alt(?v, ?arms, _)) {
            auto vv = eval_expr(sess, e, v);
            for (ast.arm arm in arms) {
                alt (arm.pat.node) {
                    case (ast.pat_lit(?lit, _)) {
                        auto pv = eval_lit(sess, e,
                                           arm.pat.span, lit);
                        if (val_eq(sess, arm.pat.span, vv, pv)) {
                            ret eval_crate_directive_block
                                (p, e, arm.block, prefix,
                                 view_items, items, index);
                        }
                    }
                    case (ast.pat_wild(_)) {
                        ret eval_crate_directive_block
                            (p, e, arm.block, prefix,
                             view_items, items, index);
                    }
                    case (_) {
                        sess.span_err(arm.pat.span,
                                      "bad pattern type in 'alt'");
                    }
                }
            }
            sess.span_err(x.span, "no cases matched in 'alt'");
        }

        case (_) {
            sess.span_err(x.span, "unsupported expr type");
        }
    }
}

impure fn eval_crate_directive(parser p,
                               env e,
                               @ast.crate_directive cdir,
                               str prefix,
                               &mutable vec[@ast.view_item] view_items,
                               &mutable vec[@ast.item] items,
                               hashmap[ast.ident,
                                       ast.mod_index_entry] index) {
    alt (cdir.node) {

        case (ast.cdir_let(?id, ?x, ?cdirs)) {
            auto v = eval_expr(p.get_session(), e, x);
            auto e0 = vec(tup(id, v)) + e;
            eval_crate_directives(p, e0, cdirs, prefix,
                                  view_items, items, index);
        }

        case (ast.cdir_expr(?x)) {
            eval_crate_directive_expr(p, e, x, prefix,
                                      view_items, items, index);
        }

        case (ast.cdir_src_mod(?id, ?file_opt)) {

            auto file_path = id + ".rs";
            alt (file_opt) {
                case (some[filename](?f)) {
                    file_path = f;
                }
                case (none[filename]) {}
            }

            auto full_path = prefix + std.os.path_sep() + file_path;

            auto p0 = new_parser(p.get_session(), e, 0, full_path);
            auto m0 = parse_mod_items(p0, token.EOF);
            auto im = ast.item_mod(id, m0, p.next_def_id());
            auto i = @spanned(cdir.span, cdir.span, im);
            ast.index_item(index, i);
            append[@ast.item](items, i);
        }

        case (ast.cdir_dir_mod(?id, ?dir_opt, ?cdirs)) {

            auto path = id;
            alt (dir_opt) {
                case (some[filename](?d)) {
                    path = d;
                }
                case (none[filename]) {}
            }

            auto full_path = prefix + std.os.path_sep() + path;
            auto m0 = eval_crate_directives_to_mod(p, e, cdirs, full_path);
            auto im = ast.item_mod(id, m0, p.next_def_id());
            auto i = @spanned(cdir.span, cdir.span, im);
            ast.index_item(index, i);
            append[@ast.item](items, i);
        }

        case (ast.cdir_view_item(?vi)) {
            append[@ast.view_item](view_items, vi);
            ast.index_view_item(index, vi);
        }

        case (ast.cdir_meta(?mi)) {}
        case (ast.cdir_syntax(?pth)) {}
        case (ast.cdir_auth(?pth, ?eff)) {}
    }
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<commit_msg>Fix eval typo (caught by Martin Hock).<commit_after>import std._vec;
import std._str;
import std.option;
import std.option.some;
import std.option.none;
import std.map.hashmap;

import driver.session;
import ast.ident;
import front.parser.parser;
import front.parser.spanned;
import front.parser.new_parser;
import front.parser.parse_mod_items;
import util.common;
import util.common.filename;
import util.common.append;
import util.common.span;
import util.common.new_str_hash;

// Simple dynamic-typed value type for eval_expr.
tag val {
    val_bool(bool);
    val_int(int);
    val_str(str);
}

type env = vec[tup(ident, val)];

fn mk_env() -> env {
    let env e = vec();
    ret e;
}

fn val_is_bool(val v) -> bool {
    alt (v) {
        case (val_bool(_)) { ret true; }
        case (_) { }
    }
    ret false;
}

fn val_is_int(val v) -> bool {
    alt (v) {
        case (val_int(_)) { ret true; }
        case (_) { }
    }
    ret false;
}

fn val_is_str(val v) -> bool {
    alt (v) {
        case (val_str(_)) { ret true; }
        case (_) { }
    }
    ret false;
}

fn val_as_bool(val v) -> bool {
    alt (v) {
        case (val_bool(?b)) { ret b; }
        case (_) { }
    }
    fail;
}

fn val_as_int(val v) -> int {
    alt (v) {
        case (val_int(?i)) { ret i; }
        case (_) { }
    }
    fail;
}

fn val_as_str(val v) -> str {
    alt (v) {
        case (val_str(?s)) { ret s; }
        case (_) { }
    }
    fail;
}

fn lookup(session.session sess, env e, span sp, ident i) -> val {
    for (tup(ident, val) pair in e) {
        if (_str.eq(i, pair._0)) {
            ret pair._1;
        }
    }
    sess.span_err(sp, "unknown variable: " + i);
    fail;
}

fn eval_lit(session.session sess, env e, span sp, @ast.lit lit) -> val {
    alt (lit.node) {
        case (ast.lit_bool(?b)) { ret val_bool(b); }
        case (ast.lit_int(?i)) { ret val_int(i); }
        case (ast.lit_str(?s)) { ret val_str(s); }
        case (_) {
            sess.span_err(sp, "evaluating unsupported literal");
        }
    }
    fail;
}

fn eval_expr(session.session sess, env e, @ast.expr x) -> val {
    alt (x.node) {
        case (ast.expr_path(?pth, _, _)) {
            if (_vec.len[ident](pth.node.idents) == 1u &&
                _vec.len[@ast.ty](pth.node.types) == 0u) {
                ret lookup(sess, e, x.span, pth.node.idents.(0));
            }
            sess.span_err(x.span, "evaluating structured path-name");
        }

        case (ast.expr_lit(?lit, _)) {
            ret eval_lit(sess, e, x.span, lit);
        }

        case (ast.expr_unary(?op, ?a, _)) {
            auto av = eval_expr(sess, e, a);
            alt (op) {
                case (ast.not) {
                    if (val_is_bool(av)) {
                        ret val_bool(!val_as_bool(av));
                    }
                    sess.span_err(x.span, "bad types in '!' expression");
                }
                case (_) {
                    sess.span_err(x.span, "evaluating unsupported unop");
                }
            }
        }

        case (ast.expr_binary(?op, ?a, ?b, _)) {
            auto av = eval_expr(sess, e, a);
            auto bv = eval_expr(sess, e, b);
            alt (op) {
                case (ast.add) {
                    if (val_is_int(av) && val_is_int(bv)) {
                        ret val_int(val_as_int(av) + val_as_int(bv));
                    }
                    if (val_is_str(av) && val_is_str(bv)) {
                        ret val_str(val_as_str(av) + val_as_str(bv));
                    }
                    sess.span_err(x.span, "bad types in '+' expression");
                }

                case (ast.sub) {
                    if (val_is_int(av) && val_is_int(bv)) {
                        ret val_int(val_as_int(av) - val_as_int(bv));
                    }
                    sess.span_err(x.span, "bad types in '-' expression");
                }

                case (ast.mul) {
                    if (val_is_int(av) && val_is_int(bv)) {
                        ret val_int(val_as_int(av) * val_as_int(bv));
                    }
                    sess.span_err(x.span, "bad types in '*' expression");
                }

                case (ast.div) {
                    if (val_is_int(av) && val_is_int(bv)) {
                        ret val_int(val_as_int(av) / val_as_int(bv));
                    }
                    sess.span_err(x.span, "bad types in '/' expression");
                }

                case (ast.rem) {
                    if (val_is_int(av) && val_is_int(bv)) {
                        ret val_int(val_as_int(av) % val_as_int(bv));
                    }
                    sess.span_err(x.span, "bad types in '%' expression");
                }

                case (ast.and) {
                    if (val_is_bool(av) && val_is_bool(bv)) {
                        ret val_bool(val_as_bool(av) && val_as_bool(bv));
                    }
                    sess.span_err(x.span, "bad types in '&&' expression");
                }

                case (ast.or) {
                    if (val_is_bool(av) && val_is_bool(bv)) {
                        ret val_bool(val_as_bool(av) || val_as_bool(bv));
                    }
                    sess.span_err(x.span, "bad types in '||' expression");
                }

                case (ast.eq) {
                    ret val_bool(val_eq(sess, x.span, av, bv));
                }

                case (ast.ne) {
                    ret val_bool(! val_eq(sess, x.span, av, bv));
                }

                case (_) {
                    sess.span_err(x.span, "evaluating unsupported binop");
                }
            }
        }
        case (_) {
            sess.span_err(x.span, "evaluating unsupported expression");
        }
    }
    fail;
}

fn val_eq(session.session sess, span sp, val av, val bv) -> bool {
    if (val_is_bool(av) && val_is_bool(bv)) {
        ret val_as_bool(av) == val_as_bool(bv);
    }
    if (val_is_int(av) && val_is_int(bv)) {
        ret val_as_int(av) == val_as_int(bv);
    }
    if (val_is_str(av) && val_is_str(bv)) {
        ret _str.eq(val_as_str(av),
                    val_as_str(bv));
    }
    sess.span_err(sp, "bad types in comparison");
    fail;
}

impure fn eval_crate_directives(parser p,
                                env e,
                                vec[@ast.crate_directive] cdirs,
                                str prefix,
                                &mutable vec[@ast.view_item] view_items,
                                &mutable vec[@ast.item] items,
                                hashmap[ast.ident,
                                        ast.mod_index_entry] index) {

    for (@ast.crate_directive sub_cdir in cdirs) {
        eval_crate_directive(p, e, sub_cdir, prefix,
                             view_items, items, index);
    }
}

impure fn eval_crate_directives_to_mod(parser p,
                                       env e,
                                       vec[@ast.crate_directive] cdirs,
                                       str prefix) -> ast._mod {
    let vec[@ast.view_item] view_items = vec();
    let vec[@ast.item] items = vec();
    auto index = new_str_hash[ast.mod_index_entry]();

    eval_crate_directives(p, e, cdirs, prefix,
                          view_items, items, index);

    ret rec(view_items=view_items, items=items, index=index);
}

impure fn eval_crate_directive_block(parser p,
                                     env e,
                                     &ast.block blk,
                                     str prefix,
                                     &mutable vec[@ast.view_item] view_items,
                                     &mutable vec[@ast.item] items,
                                     hashmap[ast.ident,
                                             ast.mod_index_entry] index) {

    for (@ast.stmt s in blk.node.stmts) {
        alt (s.node) {
            case (ast.stmt_crate_directive(?cdir)) {
                eval_crate_directive(p, e, cdir, prefix,
                                     view_items, items, index);
            }
            case (_) {
                auto sess = p.get_session();
                sess.span_err(s.span,
                              "unsupported stmt in crate-directive block");
            }
        }
    }
}

impure fn eval_crate_directive_expr(parser p,
                                    env e,
                                    @ast.expr x,
                                    str prefix,
                                    &mutable vec[@ast.view_item] view_items,
                                    &mutable vec[@ast.item] items,
                                    hashmap[ast.ident,
                                            ast.mod_index_entry] index) {
    auto sess = p.get_session();

    alt (x.node) {

        case (ast.expr_if(?cond, ?thn, ?elifs, ?elopt, _)) {
            auto cv = eval_expr(sess, e, cond);
            if (!val_is_bool(cv)) {
                sess.span_err(x.span, "bad cond type in 'if'");
            }

            if (val_as_bool(cv)) {
                ret eval_crate_directive_block(p, e, thn, prefix,
                                               view_items, items,
                                               index);
            }

            for (tup(@ast.expr, ast.block) elif in elifs) {
                auto cv = eval_expr(sess, e, elif._0);
                if (!val_is_bool(cv)) {
                    sess.span_err(x.span, "bad cond type in 'else if'");
                }

                if (val_as_bool(cv)) {
                    ret eval_crate_directive_block(p, e, elif._1, prefix,
                                                   view_items, items,
                                                   index);
                }
            }

            alt (elopt) {
                case (some[ast.block](?els)) {
                    ret eval_crate_directive_block(p, e, els, prefix,
                                                   view_items, items,
                                                   index);
                }
                case (_) {
                    // Absent-else is ok.
                }
            }
        }

        case (ast.expr_alt(?v, ?arms, _)) {
            auto vv = eval_expr(sess, e, v);
            for (ast.arm arm in arms) {
                alt (arm.pat.node) {
                    case (ast.pat_lit(?lit, _)) {
                        auto pv = eval_lit(sess, e,
                                           arm.pat.span, lit);
                        if (val_eq(sess, arm.pat.span, vv, pv)) {
                            ret eval_crate_directive_block
                                (p, e, arm.block, prefix,
                                 view_items, items, index);
                        }
                    }
                    case (ast.pat_wild(_)) {
                        ret eval_crate_directive_block
                            (p, e, arm.block, prefix,
                             view_items, items, index);
                    }
                    case (_) {
                        sess.span_err(arm.pat.span,
                                      "bad pattern type in 'alt'");
                    }
                }
            }
            sess.span_err(x.span, "no cases matched in 'alt'");
        }

        case (_) {
            sess.span_err(x.span, "unsupported expr type");
        }
    }
}

impure fn eval_crate_directive(parser p,
                               env e,
                               @ast.crate_directive cdir,
                               str prefix,
                               &mutable vec[@ast.view_item] view_items,
                               &mutable vec[@ast.item] items,
                               hashmap[ast.ident,
                                       ast.mod_index_entry] index) {
    alt (cdir.node) {

        case (ast.cdir_let(?id, ?x, ?cdirs)) {
            auto v = eval_expr(p.get_session(), e, x);
            auto e0 = vec(tup(id, v)) + e;
            eval_crate_directives(p, e0, cdirs, prefix,
                                  view_items, items, index);
        }

        case (ast.cdir_expr(?x)) {
            eval_crate_directive_expr(p, e, x, prefix,
                                      view_items, items, index);
        }

        case (ast.cdir_src_mod(?id, ?file_opt)) {

            auto file_path = id + ".rs";
            alt (file_opt) {
                case (some[filename](?f)) {
                    file_path = f;
                }
                case (none[filename]) {}
            }

            auto full_path = prefix + std.os.path_sep() + file_path;

            auto p0 = new_parser(p.get_session(), e, 0, full_path);
            auto m0 = parse_mod_items(p0, token.EOF);
            auto im = ast.item_mod(id, m0, p.next_def_id());
            auto i = @spanned(cdir.span, cdir.span, im);
            ast.index_item(index, i);
            append[@ast.item](items, i);
        }

        case (ast.cdir_dir_mod(?id, ?dir_opt, ?cdirs)) {

            auto path = id;
            alt (dir_opt) {
                case (some[filename](?d)) {
                    path = d;
                }
                case (none[filename]) {}
            }

            auto full_path = prefix + std.os.path_sep() + path;
            auto m0 = eval_crate_directives_to_mod(p, e, cdirs, full_path);
            auto im = ast.item_mod(id, m0, p.next_def_id());
            auto i = @spanned(cdir.span, cdir.span, im);
            ast.index_item(index, i);
            append[@ast.item](items, i);
        }

        case (ast.cdir_view_item(?vi)) {
            append[@ast.view_item](view_items, vi);
            ast.index_view_item(index, vi);
        }

        case (ast.cdir_meta(?mi)) {}
        case (ast.cdir_syntax(?pth)) {}
        case (ast.cdir_auth(?pth, ?eff)) {}
    }
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<|endoftext|>


<commit_before><commit_msg>Fix: Ignore broken pipe when reporting ids<commit_after><|endoftext|>


<commit_before>//! Details of the `ruma_api` procedural macro.

use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens};
use syn::{
    braced,
    parse::{Parse, ParseStream, Result},
    Field, FieldValue, Ident, Meta, Token,
};

mod attribute;
mod metadata;
mod request;
mod response;

use self::{metadata::Metadata, request::Request, response::Response};

/// Removes `serde` attributes from struct fields.
pub fn strip_serde_attrs(field: &Field) -> Field {
    let mut field = field.clone();

    field.attrs = field
        .attrs
        .into_iter()
        .filter(|attr| {
            let meta = attr
                .parse_meta()
                .expect("ruma_api! could not parse field attributes");

            match meta {
                Meta::List(meta_list) => {
                    let segments = &meta_list.path.segments;
                    segments.len() != 1 || segments[0].ident != "serde"
                }
                _ => true,
            }
        })
        .collect();

    field
}

/// The result of processing the `ruma_api` macro, ready for output back to source code.
pub struct Api {
    /// The `metadata` section of the macro.
    metadata: Metadata,
    /// The `request` section of the macro.
    request: Request,
    /// The `response` section of the macro.
    response: Response,
}

impl From<RawApi> for Api {
    fn from(raw_api: RawApi) -> Self {
        let res = Self {
            metadata: raw_api.metadata.into(),
            request: raw_api.request.into(),
            response: raw_api.response.into(),
        };

        assert!(
            !(res.metadata.method == "GET"
                && (res.request.has_body_fields() || res.request.newtype_body_field().is_some())),
            "GET endpoints can't have body fields"
        );

        res
    }
}

impl ToTokens for Api {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let description = &self.metadata.description;
        let method = &self.metadata.method;
        // We don't (currently) use this literal as a literal in the generated code. Instead we just
        // put it into doc comments, for which the span information is irrelevant. So we can work
        // with only the literal's value from here on.
        let name = &self.metadata.name.value();
        let path = &self.metadata.path;
        let rate_limited = &self.metadata.rate_limited;
        let requires_authentication = &self.metadata.requires_authentication;

        let request = &self.request;
        let request_types = quote! { #request };
        let response = &self.response;
        let response_types = quote! { #response };

        let set_request_path = if self.request.has_path_fields() {
            let path_str = path.value();

            assert!(path_str.starts_with('/'), "path needs to start with '/'");
            assert!(
                path_str.chars().filter(|c| *c == ':').count() == self.request.path_field_count(),
                "number of declared path parameters needs to match amount of placeholders in path"
            );

            let request_path_init_fields = self.request.request_path_init_fields();

            let path_segments = path_str[1..].split('/');
            let path_segment_push = path_segments.clone().map(|segment| {
                let arg = if segment.starts_with(':') {
                    let path_var = &segment[1..];
                    let path_var_ident = Ident::new(path_var, Span::call_site());
                    quote!(&request_path.#path_var_ident.to_string())
                } else {
                    quote!(#segment)
                };

                quote! {
                    path_segments.push(#arg);
                }
            });

            quote! {
                let request_path = RequestPath {
                    #request_path_init_fields
                };

                // This `unwrap()` can only fail when the url is a
                // cannot-be-base url like `mailto:` or `data:`, which is not
                // the case for our placeholder url.
                let mut path_segments = url.path_segments_mut().unwrap();
                #(#path_segment_push)*
            }
        } else {
            quote! {
                url.set_path(metadata.path);
            }
        };

        let set_request_query = if self.request.has_query_fields() {
            let request_query_init_fields = self.request.request_query_init_fields();

            quote! {
                let request_query = RequestQuery {
                    #request_query_init_fields
                };

                url.set_query(Some(&ruma_api::exports::serde_urlencoded::to_string(
                    request_query,
                )?));
            }
        } else {
            TokenStream::new()
        };

        let add_headers_to_request = if self.request.has_header_fields() {
            let add_headers = self.request.add_headers_to_request();
            quote! {
                let headers = http_request.headers_mut();
                #add_headers
            }
        } else {
            TokenStream::new()
        };

        let create_http_request = if let Some(field) = self.request.newtype_body_field() {
            let field_name = field
                .ident
                .as_ref()
                .expect("expected field to have an identifier");

            quote! {
                let request_body = RequestBody(request.#field_name);

                let mut http_request = ruma_api::exports::http::Request::new(
                    ruma_api::exports::serde_json::to_vec(&request_body)?,
                );
            }
        } else if self.request.has_body_fields() {
            let request_body_init_fields = self.request.request_body_init_fields();

            quote! {
                let request_body = RequestBody {
                    #request_body_init_fields
                };

                let mut http_request = ruma_api::exports::http::Request::new(
                    ruma_api::exports::serde_json::to_vec(&request_body)?,
                );
            }
        } else {
            quote! {
                let mut http_request = ruma_api::exports::http::Request::new(Vec::new());
            }
        };

        let try_deserialize_response_body = if let Some(field) = self.response.newtype_body_field()
        {
            let field_type = &field.ty;

            quote! {
                ruma_api::exports::serde_json::from_slice::<#field_type>(
                    http_response.into_body().as_slice(),
                )?
            }
        } else if self.response.has_body_fields() {
            quote! {
                ruma_api::exports::serde_json::from_slice::<ResponseBody>(
                    http_response.into_body().as_slice(),
                )?
            }
        } else {
            quote! {
                ()
            }
        };

        let extract_response_headers = if self.response.has_header_fields() {
            quote! {
                let mut headers = http_response.headers().clone();
            }
        } else {
            TokenStream::new()
        };

        let response_init_fields = if self.response.has_fields() {
            self.response.init_fields()
        } else {
            TokenStream::new()
        };

        let request_doc = format!(
            "Data for a request to the `{}` API endpoint.\n\n{}",
            name,
            description.value()
        );
        let response_doc = format!("Data in the response from the `{}` API endpoint.", name);

        let api = quote! {
            use ruma_api::exports::serde::de::{Error as _, IntoDeserializer as _};
            use ruma_api::exports::serde::Deserialize as _;
            use ruma_api::Endpoint as _;

            use std::convert::TryInto as _;

            #[doc = #request_doc]
            #request_types

            impl std::convert::TryFrom<Request> for ruma_api::exports::http::Request<Vec<u8>> {
                type Error = ruma_api::Error;

                #[allow(unused_mut, unused_variables)]
                fn try_from(request: Request) -> Result<Self, Self::Error> {
                    let metadata = Request::METADATA;

                    // Use dummy homeserver url which has to be overwritten in
                    // the calling code. Previously (with http::Uri) this was
                    // not required, but Url::parse only accepts absolute urls.
                    let mut url =
                        ruma_api::exports::url::Url::parse("http://invalid-host-please-change/")
                            .unwrap();

                    { #set_request_path }
                    { #set_request_query }

                    #create_http_request

                    *http_request.method_mut() = ruma_api::exports::http::Method::#method;
                    *http_request.uri_mut() = url.into_string().parse().unwrap();

                    { #add_headers_to_request }

                    Ok(http_request)
                }
            }

            #[doc = #response_doc]
            #response_types

            impl std::convert::TryFrom<ruma_api::exports::http::Response<Vec<u8>>> for Response {
                type Error = ruma_api::Error;

                #[allow(unused_variables)]
                fn try_from(
                    http_response: ruma_api::exports::http::Response<Vec<u8>>,
                ) -> Result<Self, Self::Error> {
                    if http_response.status().is_success() {
                        #extract_response_headers

                        let response_body = #try_deserialize_response_body;
                        Ok(Response {
                            #response_init_fields
                        })
                    } else {
                        Err(http_response.status().clone().into())
                    }
                }
            }

            impl ruma_api::Endpoint for Request {
                type Response = Response;

                /// Metadata for the `#name` endpoint.
                const METADATA: ruma_api::Metadata = ruma_api::Metadata {
                    description: #description,
                    method: ruma_api::exports::http::Method::#method,
                    name: #name,
                    path: #path,
                    rate_limited: #rate_limited,
                    requires_authentication: #requires_authentication,
                };
            }
        };

        api.to_tokens(tokens);
    }
}

/// Custom keyword macros for syn.
mod kw {
    use syn::custom_keyword;

    custom_keyword!(metadata);
    custom_keyword!(request);
    custom_keyword!(response);
}

/// The entire `ruma_api!` macro structure directly as it appears in the source code..
pub struct RawApi {
    /// The `metadata` section of the macro.
    pub metadata: Vec<FieldValue>,
    /// The `request` section of the macro.
    pub request: Vec<Field>,
    /// The `response` section of the macro.
    pub response: Vec<Field>,
}

impl Parse for RawApi {
    fn parse(input: ParseStream<'_>) -> Result<Self> {
        input.parse::<kw::metadata>()?;
        let metadata;
        braced!(metadata in input);

        input.parse::<kw::request>()?;
        let request;
        braced!(request in input);

        input.parse::<kw::response>()?;
        let response;
        braced!(response in input);

        Ok(Self {
            metadata: metadata
                .parse_terminated::<FieldValue, Token![,]>(FieldValue::parse)?
                .into_iter()
                .collect(),
            request: request
                .parse_terminated::<Field, Token![,]>(Field::parse_named)?
                .into_iter()
                .collect(),
            response: response
                .parse_terminated::<Field, Token![,]>(Field::parse_named)?
                .into_iter()
                .collect(),
        })
    }
}
<commit_msg>Simplify ruma_api_macros::api::strip_serde_attrs<commit_after>//! Details of the `ruma_api` procedural macro.

use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens};
use syn::{
    braced,
    parse::{Parse, ParseStream, Result},
    Field, FieldValue, Ident, Token,
};

mod attribute;
mod metadata;
mod request;
mod response;

use self::{metadata::Metadata, request::Request, response::Response};

/// Removes `serde` attributes from struct fields.
pub fn strip_serde_attrs(field: &Field) -> Field {
    let mut field = field.clone();
    field
        .attrs
        .retain(|attr| attr.path.segments.len() != 1 || attr.path.segments[0].ident != "serde");
    field
}

/// The result of processing the `ruma_api` macro, ready for output back to source code.
pub struct Api {
    /// The `metadata` section of the macro.
    metadata: Metadata,
    /// The `request` section of the macro.
    request: Request,
    /// The `response` section of the macro.
    response: Response,
}

impl From<RawApi> for Api {
    fn from(raw_api: RawApi) -> Self {
        let res = Self {
            metadata: raw_api.metadata.into(),
            request: raw_api.request.into(),
            response: raw_api.response.into(),
        };

        assert!(
            !(res.metadata.method == "GET"
                && (res.request.has_body_fields() || res.request.newtype_body_field().is_some())),
            "GET endpoints can't have body fields"
        );

        res
    }
}

impl ToTokens for Api {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let description = &self.metadata.description;
        let method = &self.metadata.method;
        // We don't (currently) use this literal as a literal in the generated code. Instead we just
        // put it into doc comments, for which the span information is irrelevant. So we can work
        // with only the literal's value from here on.
        let name = &self.metadata.name.value();
        let path = &self.metadata.path;
        let rate_limited = &self.metadata.rate_limited;
        let requires_authentication = &self.metadata.requires_authentication;

        let request = &self.request;
        let request_types = quote! { #request };
        let response = &self.response;
        let response_types = quote! { #response };

        let set_request_path = if self.request.has_path_fields() {
            let path_str = path.value();

            assert!(path_str.starts_with('/'), "path needs to start with '/'");
            assert!(
                path_str.chars().filter(|c| *c == ':').count() == self.request.path_field_count(),
                "number of declared path parameters needs to match amount of placeholders in path"
            );

            let request_path_init_fields = self.request.request_path_init_fields();

            let path_segments = path_str[1..].split('/');
            let path_segment_push = path_segments.clone().map(|segment| {
                let arg = if segment.starts_with(':') {
                    let path_var = &segment[1..];
                    let path_var_ident = Ident::new(path_var, Span::call_site());
                    quote!(&request_path.#path_var_ident.to_string())
                } else {
                    quote!(#segment)
                };

                quote! {
                    path_segments.push(#arg);
                }
            });

            quote! {
                let request_path = RequestPath {
                    #request_path_init_fields
                };

                // This `unwrap()` can only fail when the url is a
                // cannot-be-base url like `mailto:` or `data:`, which is not
                // the case for our placeholder url.
                let mut path_segments = url.path_segments_mut().unwrap();
                #(#path_segment_push)*
            }
        } else {
            quote! {
                url.set_path(metadata.path);
            }
        };

        let set_request_query = if self.request.has_query_fields() {
            let request_query_init_fields = self.request.request_query_init_fields();

            quote! {
                let request_query = RequestQuery {
                    #request_query_init_fields
                };

                url.set_query(Some(&ruma_api::exports::serde_urlencoded::to_string(
                    request_query,
                )?));
            }
        } else {
            TokenStream::new()
        };

        let add_headers_to_request = if self.request.has_header_fields() {
            let add_headers = self.request.add_headers_to_request();
            quote! {
                let headers = http_request.headers_mut();
                #add_headers
            }
        } else {
            TokenStream::new()
        };

        let create_http_request = if let Some(field) = self.request.newtype_body_field() {
            let field_name = field
                .ident
                .as_ref()
                .expect("expected field to have an identifier");

            quote! {
                let request_body = RequestBody(request.#field_name);

                let mut http_request = ruma_api::exports::http::Request::new(
                    ruma_api::exports::serde_json::to_vec(&request_body)?,
                );
            }
        } else if self.request.has_body_fields() {
            let request_body_init_fields = self.request.request_body_init_fields();

            quote! {
                let request_body = RequestBody {
                    #request_body_init_fields
                };

                let mut http_request = ruma_api::exports::http::Request::new(
                    ruma_api::exports::serde_json::to_vec(&request_body)?,
                );
            }
        } else {
            quote! {
                let mut http_request = ruma_api::exports::http::Request::new(Vec::new());
            }
        };

        let try_deserialize_response_body = if let Some(field) = self.response.newtype_body_field()
        {
            let field_type = &field.ty;

            quote! {
                ruma_api::exports::serde_json::from_slice::<#field_type>(
                    http_response.into_body().as_slice(),
                )?
            }
        } else if self.response.has_body_fields() {
            quote! {
                ruma_api::exports::serde_json::from_slice::<ResponseBody>(
                    http_response.into_body().as_slice(),
                )?
            }
        } else {
            quote! {
                ()
            }
        };

        let extract_response_headers = if self.response.has_header_fields() {
            quote! {
                let mut headers = http_response.headers().clone();
            }
        } else {
            TokenStream::new()
        };

        let response_init_fields = if self.response.has_fields() {
            self.response.init_fields()
        } else {
            TokenStream::new()
        };

        let request_doc = format!(
            "Data for a request to the `{}` API endpoint.\n\n{}",
            name,
            description.value()
        );
        let response_doc = format!("Data in the response from the `{}` API endpoint.", name);

        let api = quote! {
            use ruma_api::exports::serde::de::{Error as _, IntoDeserializer as _};
            use ruma_api::exports::serde::Deserialize as _;
            use ruma_api::Endpoint as _;

            use std::convert::TryInto as _;

            #[doc = #request_doc]
            #request_types

            impl std::convert::TryFrom<Request> for ruma_api::exports::http::Request<Vec<u8>> {
                type Error = ruma_api::Error;

                #[allow(unused_mut, unused_variables)]
                fn try_from(request: Request) -> Result<Self, Self::Error> {
                    let metadata = Request::METADATA;

                    // Use dummy homeserver url which has to be overwritten in
                    // the calling code. Previously (with http::Uri) this was
                    // not required, but Url::parse only accepts absolute urls.
                    let mut url =
                        ruma_api::exports::url::Url::parse("http://invalid-host-please-change/")
                            .unwrap();

                    { #set_request_path }
                    { #set_request_query }

                    #create_http_request

                    *http_request.method_mut() = ruma_api::exports::http::Method::#method;
                    *http_request.uri_mut() = url.into_string().parse().unwrap();

                    { #add_headers_to_request }

                    Ok(http_request)
                }
            }

            #[doc = #response_doc]
            #response_types

            impl std::convert::TryFrom<ruma_api::exports::http::Response<Vec<u8>>> for Response {
                type Error = ruma_api::Error;

                #[allow(unused_variables)]
                fn try_from(
                    http_response: ruma_api::exports::http::Response<Vec<u8>>,
                ) -> Result<Self, Self::Error> {
                    if http_response.status().is_success() {
                        #extract_response_headers

                        let response_body = #try_deserialize_response_body;
                        Ok(Response {
                            #response_init_fields
                        })
                    } else {
                        Err(http_response.status().clone().into())
                    }
                }
            }

            impl ruma_api::Endpoint for Request {
                type Response = Response;

                /// Metadata for the `#name` endpoint.
                const METADATA: ruma_api::Metadata = ruma_api::Metadata {
                    description: #description,
                    method: ruma_api::exports::http::Method::#method,
                    name: #name,
                    path: #path,
                    rate_limited: #rate_limited,
                    requires_authentication: #requires_authentication,
                };
            }
        };

        api.to_tokens(tokens);
    }
}

/// Custom keyword macros for syn.
mod kw {
    use syn::custom_keyword;

    custom_keyword!(metadata);
    custom_keyword!(request);
    custom_keyword!(response);
}

/// The entire `ruma_api!` macro structure directly as it appears in the source code..
pub struct RawApi {
    /// The `metadata` section of the macro.
    pub metadata: Vec<FieldValue>,
    /// The `request` section of the macro.
    pub request: Vec<Field>,
    /// The `response` section of the macro.
    pub response: Vec<Field>,
}

impl Parse for RawApi {
    fn parse(input: ParseStream<'_>) -> Result<Self> {
        input.parse::<kw::metadata>()?;
        let metadata;
        braced!(metadata in input);

        input.parse::<kw::request>()?;
        let request;
        braced!(request in input);

        input.parse::<kw::response>()?;
        let response;
        braced!(response in input);

        Ok(Self {
            metadata: metadata
                .parse_terminated::<FieldValue, Token![,]>(FieldValue::parse)?
                .into_iter()
                .collect(),
            request: request
                .parse_terminated::<Field, Token![,]>(Field::parse_named)?
                .into_iter()
                .collect(),
            response: response
                .parse_terminated::<Field, Token![,]>(Field::parse_named)?
                .into_iter()
                .collect(),
        })
    }
}
<|endoftext|>


<commit_before><commit_msg>Add a check-fs-writeable script<commit_after>//! Check that we can write to disk

extern crate rustc_serialize;
extern crate docopt;

use std::error::Error;
use std::fs;
use std::io::Write;
use std::io::ErrorKind;
use std::thread;
use std::path::Path;
use std::process;
use std::sync::mpsc::channel;

use docopt::Docopt;

static USAGE: &'static str = "
Usage:
    check-fs-writeable <filename>
    check-fs-writeable -h | --help

Check that we can write to a filesystem by writing a byte to a file. Does not
try to create the directory, or do anything else. Just writes a single byte to
a file.

Arguments:

    <filename>            The file to write to

Options:
    -h, --help            Show this message and exit
";

#[derive(RustcDecodable)]
struct Args {
    arg_filename: String
}

impl Args {
    fn parse() -> Args {
        Docopt::new(USAGE)
            .and_then(|d| d.decode())
            .unwrap_or_else(|e| e.exit())
    }
}

fn check_file_writeable(filename: String) -> Result<String, String> {
    let (tx, rx) = channel();
    // We spawn a thread because rust doesn't provide a way to close a file, it
    // just panics if issues happen when it goes out of scope.
    let child = thread::spawn(move || {
        let path = Path::new(&filename);
        match fs::File::create(&path) {
            Err(ref e) => {
                match e.kind() {
                    ErrorKind::NotFound => {
                        let dir = path.parent().unwrap_or(Path::new("/"));
                        tx.send(Err(format!(
                            "CRITICAL: directory {} does not exist.",
                            dir.display()))).unwrap();
                    },
                    _ => tx.send(Err(format!(
                        "CRITICAL: unexpected error writing to {}: {}",
                        path.display(), e))).unwrap()
                }
            },
            Ok(mut f) => {
                f.write_all(b"t").unwrap();
                match f.flush() {
                    Ok(()) => {},
                    Err(_) => tx.send(Err(
                        format!("CRITICAL: Couldn't flush bytes to {}", path.display()))).unwrap()
                }
                fs::remove_file(&path).unwrap();
            }
        }
        tx.send(Ok(format!("OK: wrote some bytes to {}", path.display()))).unwrap()
    });

    if let Err(kind) = child.join() {
        return Err(format!("CRITICAL: error writing to file: {:?}", kind));
    }
    match rx.recv() {
        Ok(join_result) => {
            return join_result;
        },
        Err(_) => {
            return Err(format!(
                "UNKNOWN: unexpected status receiving info about file writing."));
        }
    };
}

#[cfg_attr(test, allow(dead_code))]
fn main() {
    let args = Args::parse();
    let mut exit_status = 0;
    match check_file_writeable(args.arg_filename) {
        Ok(msg) => {
            println!("{}", msg);
        }
        Err(msg) => {
            println!("{}", msg);
            exit_status = 2;
        }
    }
    process::exit(exit_status);
}

#[cfg(test)]
mod test {
    use super::{USAGE, Args};
    use docopt::Docopt;

    #[test]
    fn can_parse_args() {
        let _: Args = Docopt::new(USAGE)
            .and_then(|d| d.argv(vec!["arg0", "/tmp"].into_iter()).help(true).decode())
            .unwrap();
    }
}
<|endoftext|>