<commit_before>//! Abstract Syntax Tree, layered on top of untyped `SyntaxNode`s

mod generated;
mod traits;
mod tokens;
mod extensions;
mod expr_extensions;
pub mod edit;
pub mod make;

use std::marker::PhantomData;

use crate::{
    syntax_node::{SyntaxNode, SyntaxNodeChildren, SyntaxToken},
    SmolStr, SyntaxKind,
};

pub use self::{
    expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp},
    extensions::{
        AttrKind, FieldKind, PathSegmentKind, SelfParamKind, SlicePatComponents, StructKind,
        TypeBoundKind, VisibilityKind,
    },
    generated::*,
    tokens::*,
    traits::*,
};

/// The main trait to go from untyped `SyntaxNode`  to a typed ast. The
/// conversion itself has zero runtime cost: ast and syntax nodes have exactly
/// the same representation: a pointer to the tree root and a pointer to the
/// node itself.
pub trait AstNode {
    fn can_cast(kind: SyntaxKind) -> bool
    where
        Self: Sized;

    fn cast(syntax: SyntaxNode) -> Option<Self>
    where
        Self: Sized;

    fn syntax(&self) -> &SyntaxNode;
}

#[test]
fn assert_ast_is_object_safe() {
    fn _f(_: &dyn AstNode, _: &dyn NameOwner) {}
}

/// Like `AstNode`, but wraps tokens rather than interior nodes.
pub trait AstToken {
    fn cast(token: SyntaxToken) -> Option<Self>
    where
        Self: Sized;
    fn syntax(&self) -> &SyntaxToken;
    fn text(&self) -> &SmolStr {
        self.syntax().text()
    }
}

/// An iterator over `SyntaxNode` children of a particular AST type.
#[derive(Debug)]
pub struct AstChildren<N> {
    inner: SyntaxNodeChildren,
    ph: PhantomData<N>,
}

impl<N> AstChildren<N> {
    fn new(parent: &SyntaxNode) -> Self {
        AstChildren { inner: parent.children(), ph: PhantomData }
    }
}

impl<N: AstNode> Iterator for AstChildren<N> {
    type Item = N;
    fn next(&mut self) -> Option<N> {
        self.inner.by_ref().find_map(N::cast)
    }
}

fn child_opt<P: AstNode + ?Sized, C: AstNode>(parent: &P) -> Option<C> {
    children(parent).next()
}

fn children<P: AstNode + ?Sized, C: AstNode>(parent: &P) -> AstChildren<C> {
    AstChildren::new(parent.syntax())
}

#[test]
fn test_doc_comment_none() {
    let file = SourceFile::parse(
        r#"
        // non-doc
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert!(module.doc_comment_text().is_none());
}

#[test]
fn test_doc_comment_of_items() {
    let file = SourceFile::parse(
        r#"
        //! doc
        // non-doc
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!("doc", module.doc_comment_text().unwrap());
}

#[test]
fn test_doc_comment_of_statics() {
    let file = SourceFile::parse(
        r#"
        /// Number of levels
        static LEVELS: i32 = 0;
        "#,
    )
    .ok()
    .unwrap();
    let st = file.syntax().descendants().find_map(StaticDef::cast).unwrap();
    assert_eq!("Number of levels", st.doc_comment_text().unwrap());
}

#[test]
fn test_doc_comment_preserves_indents() {
    let file = SourceFile::parse(
        r#"
        /// doc1
        /// ```
        /// fn foo() {
        ///     // ...
        /// }
        /// ```
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!("doc1\n```\nfn foo() {\n    // ...\n}\n```", module.doc_comment_text().unwrap());
}

#[test]
fn test_doc_comment_preserves_newlines() {
    let file = SourceFile::parse(
        r#"
        /// this
        /// is
        /// mod
        /// foo
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!("this\nis\nmod\nfoo", module.doc_comment_text().unwrap());
}

#[test]
fn test_doc_comment_single_line_block_strips_suffix() {
    let file = SourceFile::parse(
        r#"
        /** this is mod foo*/
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!("this is mod foo", module.doc_comment_text().unwrap());
}

#[test]
fn test_doc_comment_single_line_block_strips_suffix_whitespace() {
    let file = SourceFile::parse(
        r#"
        /** this is mod foo */
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!("this is mod foo ", module.doc_comment_text().unwrap());
}

#[test]
fn test_doc_comment_multi_line_block_strips_suffix() {
    let file = SourceFile::parse(
        r#"
        /**
        this
        is
        mod foo
        */
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!(
        "        this\n        is\n        mod foo\n        ",
        module.doc_comment_text().unwrap()
    );
}

#[test]
fn test_comments_preserve_trailing_whitespace() {
    let file = SourceFile::parse(
        r#"
/// Representation of a Realm.
/// In the specification these are called Realm Records.
struct Realm {}"#,
    )
    .ok()
    .unwrap();
    let def = file.syntax().descendants().find_map(StructDef::cast).unwrap();
    assert_eq!(
        "Representation of a Realm.   \nIn the specification these are called Realm Records.",
        def.doc_comment_text().unwrap()
    );
}

#[test]
fn test_where_predicates() {
    fn assert_bound(text: &str, bound: Option<TypeBound>) {
        assert_eq!(text, bound.unwrap().syntax().text().to_string());
    }

    let file = SourceFile::parse(
        r#"
fn foo()
where
   T: Clone + Copy + Debug + 'static,
   'a: 'b + 'c,
   Iterator::Item: 'a + Debug,
   Iterator::Item: Debug + 'a,
   <T as Iterator>::Item: Debug + 'a,
   for<'a> F: Fn(&'a str)
{}
        "#,
    )
    .ok()
    .unwrap();
    let where_clause = file.syntax().descendants().find_map(WhereClause::cast).unwrap();

    let mut predicates = where_clause.predicates();

    let pred = predicates.next().unwrap();
    let mut bounds = pred.type_bound_list().unwrap().bounds();

    assert_eq!("T", pred.type_ref().unwrap().syntax().text().to_string());
    assert_bound("Clone", bounds.next());
    assert_bound("Copy", bounds.next());
    assert_bound("Debug", bounds.next());
    assert_bound("'static", bounds.next());

    let pred = predicates.next().unwrap();
    let mut bounds = pred.type_bound_list().unwrap().bounds();

    assert_eq!("'a", pred.lifetime_token().unwrap().text());

    assert_bound("'b", bounds.next());
    assert_bound("'c", bounds.next());

    let pred = predicates.next().unwrap();
    let mut bounds = pred.type_bound_list().unwrap().bounds();

    assert_eq!("Iterator::Item", pred.type_ref().unwrap().syntax().text().to_string());
    assert_bound("'a", bounds.next());

    let pred = predicates.next().unwrap();
    let mut bounds = pred.type_bound_list().unwrap().bounds();

    assert_eq!("Iterator::Item", pred.type_ref().unwrap().syntax().text().to_string());
    assert_bound("Debug", bounds.next());
    assert_bound("'a", bounds.next());

    let pred = predicates.next().unwrap();
    let mut bounds = pred.type_bound_list().unwrap().bounds();

    assert_eq!("<T as Iterator>::Item", pred.type_ref().unwrap().syntax().text().to_string());
    assert_bound("Debug", bounds.next());
    assert_bound("'a", bounds.next());

    let pred = predicates.next().unwrap();
    let mut bounds = pred.type_bound_list().unwrap().bounds();

    assert_eq!("for<'a> F", pred.type_ref().unwrap().syntax().text().to_string());
    assert_bound("Fn(&'a str)", bounds.next());
}
<commit_msg>Fix the trailing whitespace test<commit_after>//! Abstract Syntax Tree, layered on top of untyped `SyntaxNode`s

mod generated;
mod traits;
mod tokens;
mod extensions;
mod expr_extensions;
pub mod edit;
pub mod make;

use std::marker::PhantomData;

use crate::{
    syntax_node::{SyntaxNode, SyntaxNodeChildren, SyntaxToken},
    SmolStr, SyntaxKind,
};

pub use self::{
    expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp},
    extensions::{
        AttrKind, FieldKind, PathSegmentKind, SelfParamKind, SlicePatComponents, StructKind,
        TypeBoundKind, VisibilityKind,
    },
    generated::*,
    tokens::*,
    traits::*,
};

/// The main trait to go from untyped `SyntaxNode`  to a typed ast. The
/// conversion itself has zero runtime cost: ast and syntax nodes have exactly
/// the same representation: a pointer to the tree root and a pointer to the
/// node itself.
pub trait AstNode {
    fn can_cast(kind: SyntaxKind) -> bool
    where
        Self: Sized;

    fn cast(syntax: SyntaxNode) -> Option<Self>
    where
        Self: Sized;

    fn syntax(&self) -> &SyntaxNode;
}

#[test]
fn assert_ast_is_object_safe() {
    fn _f(_: &dyn AstNode, _: &dyn NameOwner) {}
}

/// Like `AstNode`, but wraps tokens rather than interior nodes.
pub trait AstToken {
    fn cast(token: SyntaxToken) -> Option<Self>
    where
        Self: Sized;
    fn syntax(&self) -> &SyntaxToken;
    fn text(&self) -> &SmolStr {
        self.syntax().text()
    }
}

/// An iterator over `SyntaxNode` children of a particular AST type.
#[derive(Debug)]
pub struct AstChildren<N> {
    inner: SyntaxNodeChildren,
    ph: PhantomData<N>,
}

impl<N> AstChildren<N> {
    fn new(parent: &SyntaxNode) -> Self {
        AstChildren { inner: parent.children(), ph: PhantomData }
    }
}

impl<N: AstNode> Iterator for AstChildren<N> {
    type Item = N;
    fn next(&mut self) -> Option<N> {
        self.inner.by_ref().find_map(N::cast)
    }
}

fn child_opt<P: AstNode + ?Sized, C: AstNode>(parent: &P) -> Option<C> {
    children(parent).next()
}

fn children<P: AstNode + ?Sized, C: AstNode>(parent: &P) -> AstChildren<C> {
    AstChildren::new(parent.syntax())
}

#[test]
fn test_doc_comment_none() {
    let file = SourceFile::parse(
        r#"
        // non-doc
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert!(module.doc_comment_text().is_none());
}

#[test]
fn test_doc_comment_of_items() {
    let file = SourceFile::parse(
        r#"
        //! doc
        // non-doc
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!("doc", module.doc_comment_text().unwrap());
}

#[test]
fn test_doc_comment_of_statics() {
    let file = SourceFile::parse(
        r#"
        /// Number of levels
        static LEVELS: i32 = 0;
        "#,
    )
    .ok()
    .unwrap();
    let st = file.syntax().descendants().find_map(StaticDef::cast).unwrap();
    assert_eq!("Number of levels", st.doc_comment_text().unwrap());
}

#[test]
fn test_doc_comment_preserves_indents() {
    let file = SourceFile::parse(
        r#"
        /// doc1
        /// ```
        /// fn foo() {
        ///     // ...
        /// }
        /// ```
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!("doc1\n```\nfn foo() {\n    // ...\n}\n```", module.doc_comment_text().unwrap());
}

#[test]
fn test_doc_comment_preserves_newlines() {
    let file = SourceFile::parse(
        r#"
        /// this
        /// is
        /// mod
        /// foo
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!("this\nis\nmod\nfoo", module.doc_comment_text().unwrap());
}

#[test]
fn test_doc_comment_single_line_block_strips_suffix() {
    let file = SourceFile::parse(
        r#"
        /** this is mod foo*/
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!("this is mod foo", module.doc_comment_text().unwrap());
}

#[test]
fn test_doc_comment_single_line_block_strips_suffix_whitespace() {
    let file = SourceFile::parse(
        r#"
        /** this is mod foo */
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!("this is mod foo ", module.doc_comment_text().unwrap());
}

#[test]
fn test_doc_comment_multi_line_block_strips_suffix() {
    let file = SourceFile::parse(
        r#"
        /**
        this
        is
        mod foo
        */
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!(
        "        this\n        is\n        mod foo\n        ",
        module.doc_comment_text().unwrap()
    );
}

#[test]
fn test_comments_preserve_trailing_whitespace() {
    let file = SourceFile::parse(
        "\n/// Representation of a Realm.   \n/// In the specification these are called Realm Records.\nstruct Realm {}",
    )
    .ok()
    .unwrap();
    let def = file.syntax().descendants().find_map(StructDef::cast).unwrap();
    assert_eq!(
        "Representation of a Realm.   \nIn the specification these are called Realm Records.",
        def.doc_comment_text().unwrap()
    );
}

#[test]
fn test_where_predicates() {
    fn assert_bound(text: &str, bound: Option<TypeBound>) {
        assert_eq!(text, bound.unwrap().syntax().text().to_string());
    }

    let file = SourceFile::parse(
        r#"
fn foo()
where
   T: Clone + Copy + Debug + 'static,
   'a: 'b + 'c,
   Iterator::Item: 'a + Debug,
   Iterator::Item: Debug + 'a,
   <T as Iterator>::Item: Debug + 'a,
   for<'a> F: Fn(&'a str)
{}
        "#,
    )
    .ok()
    .unwrap();
    let where_clause = file.syntax().descendants().find_map(WhereClause::cast).unwrap();

    let mut predicates = where_clause.predicates();

    let pred = predicates.next().unwrap();
    let mut bounds = pred.type_bound_list().unwrap().bounds();

    assert_eq!("T", pred.type_ref().unwrap().syntax().text().to_string());
    assert_bound("Clone", bounds.next());
    assert_bound("Copy", bounds.next());
    assert_bound("Debug", bounds.next());
    assert_bound("'static", bounds.next());

    let pred = predicates.next().unwrap();
    let mut bounds = pred.type_bound_list().unwrap().bounds();

    assert_eq!("'a", pred.lifetime_token().unwrap().text());

    assert_bound("'b", bounds.next());
    assert_bound("'c", bounds.next());

    let pred = predicates.next().unwrap();
    let mut bounds = pred.type_bound_list().unwrap().bounds();

    assert_eq!("Iterator::Item", pred.type_ref().unwrap().syntax().text().to_string());
    assert_bound("'a", bounds.next());

    let pred = predicates.next().unwrap();
    let mut bounds = pred.type_bound_list().unwrap().bounds();

    assert_eq!("Iterator::Item", pred.type_ref().unwrap().syntax().text().to_string());
    assert_bound("Debug", bounds.next());
    assert_bound("'a", bounds.next());

    let pred = predicates.next().unwrap();
    let mut bounds = pred.type_bound_list().unwrap().bounds();

    assert_eq!("<T as Iterator>::Item", pred.type_ref().unwrap().syntax().text().to_string());
    assert_bound("Debug", bounds.next());
    assert_bound("'a", bounds.next());

    let pred = predicates.next().unwrap();
    let mut bounds = pred.type_bound_list().unwrap().bounds();

    assert_eq!("for<'a> F", pred.type_ref().unwrap().syntax().text().to_string());
    assert_bound("Fn(&'a str)", bounds.next());
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// 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. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::error::Error;
use std::sync::Arc;

use command_buffer::cb::AddCommand;
use command_buffer::cb::CommandBufferBuild;
use command_buffer::cb::CommandsList;
use command_buffer::CommandBufferBuilder;
use command_buffer::CommandBufferBuilderBuffered;
use command_buffer::cmd;
use command_buffer::Submit;
use command_buffer::SubmitBuilder;
use device::Device;
use device::DeviceOwned;
use device::Queue;

/// Layer around a command buffer builder or a command buffer that stores the commands and has a
/// buffering mechanism.
///
/// Whenever you add a command (with the `AddCommand` trait), the command is not immediately added
/// to the underlying builder. Pending commands are added for real when you call `flush()` or when
/// you build the builder into a real command buffer.
///
/// The purpose of this buffering mechanism is to allow inserting pipeline barrier commands between
/// commands that are already submitted and commands that are pending thanks to the
/// `add_non_buffered_pipeline_barrier` method.
pub struct BufferedCommandsListLayer<I, L> {
    inner: Option<I>,
    commands: L,
    // Number of commands in the list that haven't been flushed. Since the latest commands appear
    // first in the list, it is more convenient to store the number of commands that haven't been
    // flushed rather than the number of commands that have been flushed.
    non_flushed: u32,
}

/// Helper trait for `BufferedCommandsListLayer`.
///
/// Whenever you manipulate a `BufferedCommandsListLayer<I, L>`, the template parameter `L` should
/// implement `BufferedCommandsListLayerCommands<I>`.
pub unsafe trait BufferedCommandsListLayerCommands<I> {
    /// Sends the `num` last commands of the list to `dest`.
    fn flush(&self, num: u32, dest: I) -> I;
}

unsafe impl<I> BufferedCommandsListLayerCommands<I> for () {
    #[inline]
    fn flush(&self, num: u32, dest: I) -> I {
        debug_assert_eq!(num, 0);
        dest
    }
}

unsafe impl<I, L, C> BufferedCommandsListLayerCommands<I> for (L, C)
    where I: for<'r> AddCommand<&'r C, Out = I>,
          L: BufferedCommandsListLayerCommands<I>,
{
    #[inline]
    fn flush(&self, num: u32, dest: I) -> I {
        if num == 0 {
            dest
        } else {
            self.0.flush(num - 1, dest).add(&self.1)
        }
    }
}

unsafe impl<I, L> CommandsList for BufferedCommandsListLayer<I, L> {
    type List = L;

    #[inline]
    unsafe fn list(&self) -> &L {
        &self.commands
    }
}

impl<I> BufferedCommandsListLayer<I, ()> {
    /// Builds a new `BufferedCommandsListLayer`.
    #[inline]
    pub fn new(inner: I) -> BufferedCommandsListLayer<I, ()> {
        BufferedCommandsListLayer {
            inner: Some(inner),
            commands: (),
            non_flushed: 0,
        }
    }
}

impl<I, L> BufferedCommandsListLayer<I, L>
    where L: BufferedCommandsListLayerCommands<I>,
{
    #[inline]
    fn flush_inner(&mut self) {
        let inner = self.inner.take().unwrap();
        self.inner = Some(self.commands.flush(self.non_flushed, inner));
        self.non_flushed = 0;
    }
}

unsafe impl<I, L> CommandBufferBuilderBuffered for BufferedCommandsListLayer<I, L>
    where I: for<'r> AddCommand<&'r cmd::CmdPipelineBarrier<'r>, Out = I>,
          L: BufferedCommandsListLayerCommands<I>,
{
    #[inline]
    fn add_non_buffered_pipeline_barrier(&mut self, cmd: &cmd::CmdPipelineBarrier) {
        let inner = self.inner.take().unwrap();
        self.inner = Some(inner.add(cmd));
    }

    #[inline]
    fn flush(&mut self) {
        self.flush_inner();
    }
}

unsafe impl<I, L, O> CommandBufferBuild for BufferedCommandsListLayer<I, L>
    where I: CommandBufferBuild<Out = O>,
          L: BufferedCommandsListLayerCommands<I>       // Necessary in order to flush
{
    type Out = BufferedCommandsListLayer<O, L>;

    #[inline]
    fn build(mut self) -> Self::Out {
        self.flush_inner();
        debug_assert_eq!(self.non_flushed, 0);

        let inner = self.inner.take().unwrap().build();

        BufferedCommandsListLayer {
            inner: Some(inner),
            commands: self.commands,
            non_flushed: 0,
        }
    }
}

unsafe impl<I, L> Submit for BufferedCommandsListLayer<I, L> where I: Submit {
    #[inline]
    unsafe fn append_submission<'a>(&'a self, base: SubmitBuilder<'a>, queue: &Arc<Queue>)
                                    -> Result<SubmitBuilder<'a>, Box<Error>>
    {
        self.inner.as_ref().unwrap().append_submission(base, queue)
    }
}

unsafe impl<I, L> DeviceOwned for BufferedCommandsListLayer<I, L> where I: DeviceOwned {
    #[inline]
    fn device(&self) -> &Arc<Device> {
        self.inner.as_ref().unwrap().device()
    }
}

unsafe impl<I, L> CommandBufferBuilder for BufferedCommandsListLayer<I, L> where I: DeviceOwned {
}

macro_rules! pass_through {
    (($($param:ident),*), $cmd:ty) => {
        unsafe impl<'a, I, L $(, $param)*> AddCommand<$cmd> for BufferedCommandsListLayer<I, L>
            where I: for<'r> AddCommand<&'r $cmd, Out = I>
        {
            type Out = BufferedCommandsListLayer<I, (L, $cmd)>;

            #[inline]
            fn add(mut self, command: $cmd) -> Self::Out {
                debug_assert!(self.inner.is_some());
                BufferedCommandsListLayer {
                    inner: self.inner,
                    commands: (self.commands, command),
                    non_flushed: self.non_flushed + 1,
                }
            }
        }
    }
}

pass_through!((Rp, F), cmd::CmdBeginRenderPass<Rp, F>);
pass_through!((S, Pl), cmd::CmdBindDescriptorSets<S, Pl>);
pass_through!((B), cmd::CmdBindIndexBuffer<B>);
pass_through!((Pl), cmd::CmdBindPipeline<Pl>);
pass_through!((V), cmd::CmdBindVertexBuffers<V>);
pass_through!((), cmd::CmdClearAttachments);
pass_through!((S, D), cmd::CmdCopyBuffer<S, D>);
pass_through!((), cmd::CmdDispatchRaw);
pass_through!((), cmd::CmdDrawIndexedRaw);
pass_through!((B), cmd::CmdDrawIndirectRaw<B>);
pass_through!((), cmd::CmdDrawRaw);
pass_through!((), cmd::CmdEndRenderPass);
pass_through!((C), cmd::CmdExecuteCommands<C>);
pass_through!((B), cmd::CmdFillBuffer<B>);
pass_through!((), cmd::CmdNextSubpass);
pass_through!((Pc, Pl), cmd::CmdPushConstants<Pc, Pl>);
pass_through!((), cmd::CmdSetState);
pass_through!((B, D), cmd::CmdUpdateBuffer<'a, B, D>);
<commit_msg>Warning fix<commit_after>// Copyright (c) 2016 The vulkano developers
// 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. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::error::Error;
use std::sync::Arc;

use command_buffer::cb::AddCommand;
use command_buffer::cb::CommandBufferBuild;
use command_buffer::cb::CommandsList;
use command_buffer::CommandBufferBuilder;
use command_buffer::CommandBufferBuilderBuffered;
use command_buffer::cmd;
use command_buffer::Submit;
use command_buffer::SubmitBuilder;
use device::Device;
use device::DeviceOwned;
use device::Queue;

/// Layer around a command buffer builder or a command buffer that stores the commands and has a
/// buffering mechanism.
///
/// Whenever you add a command (with the `AddCommand` trait), the command is not immediately added
/// to the underlying builder. Pending commands are added for real when you call `flush()` or when
/// you build the builder into a real command buffer.
///
/// The purpose of this buffering mechanism is to allow inserting pipeline barrier commands between
/// commands that are already submitted and commands that are pending thanks to the
/// `add_non_buffered_pipeline_barrier` method.
pub struct BufferedCommandsListLayer<I, L> {
    inner: Option<I>,
    commands: L,
    // Number of commands in the list that haven't been flushed. Since the latest commands appear
    // first in the list, it is more convenient to store the number of commands that haven't been
    // flushed rather than the number of commands that have been flushed.
    non_flushed: u32,
}

/// Helper trait for `BufferedCommandsListLayer`.
///
/// Whenever you manipulate a `BufferedCommandsListLayer<I, L>`, the template parameter `L` should
/// implement `BufferedCommandsListLayerCommands<I>`.
pub unsafe trait BufferedCommandsListLayerCommands<I> {
    /// Sends the `num` last commands of the list to `dest`.
    fn flush(&self, num: u32, dest: I) -> I;
}

unsafe impl<I> BufferedCommandsListLayerCommands<I> for () {
    #[inline]
    fn flush(&self, num: u32, dest: I) -> I {
        debug_assert_eq!(num, 0);
        dest
    }
}

unsafe impl<I, L, C> BufferedCommandsListLayerCommands<I> for (L, C)
    where I: for<'r> AddCommand<&'r C, Out = I>,
          L: BufferedCommandsListLayerCommands<I>,
{
    #[inline]
    fn flush(&self, num: u32, dest: I) -> I {
        if num == 0 {
            dest
        } else {
            self.0.flush(num - 1, dest).add(&self.1)
        }
    }
}

unsafe impl<I, L> CommandsList for BufferedCommandsListLayer<I, L> {
    type List = L;

    #[inline]
    unsafe fn list(&self) -> &L {
        &self.commands
    }
}

impl<I> BufferedCommandsListLayer<I, ()> {
    /// Builds a new `BufferedCommandsListLayer`.
    #[inline]
    pub fn new(inner: I) -> BufferedCommandsListLayer<I, ()> {
        BufferedCommandsListLayer {
            inner: Some(inner),
            commands: (),
            non_flushed: 0,
        }
    }
}

impl<I, L> BufferedCommandsListLayer<I, L>
    where L: BufferedCommandsListLayerCommands<I>,
{
    #[inline]
    fn flush_inner(&mut self) {
        let inner = self.inner.take().unwrap();
        self.inner = Some(self.commands.flush(self.non_flushed, inner));
        self.non_flushed = 0;
    }
}

unsafe impl<I, L> CommandBufferBuilderBuffered for BufferedCommandsListLayer<I, L>
    where I: for<'r> AddCommand<&'r cmd::CmdPipelineBarrier<'r>, Out = I>,
          L: BufferedCommandsListLayerCommands<I>,
{
    #[inline]
    fn add_non_buffered_pipeline_barrier(&mut self, cmd: &cmd::CmdPipelineBarrier) {
        let inner = self.inner.take().unwrap();
        self.inner = Some(inner.add(cmd));
    }

    #[inline]
    fn flush(&mut self) {
        self.flush_inner();
    }
}

unsafe impl<I, L, O> CommandBufferBuild for BufferedCommandsListLayer<I, L>
    where I: CommandBufferBuild<Out = O>,
          L: BufferedCommandsListLayerCommands<I>       // Necessary in order to flush
{
    type Out = BufferedCommandsListLayer<O, L>;

    #[inline]
    fn build(mut self) -> Self::Out {
        self.flush_inner();
        debug_assert_eq!(self.non_flushed, 0);

        let inner = self.inner.take().unwrap().build();

        BufferedCommandsListLayer {
            inner: Some(inner),
            commands: self.commands,
            non_flushed: 0,
        }
    }
}

unsafe impl<I, L> Submit for BufferedCommandsListLayer<I, L> where I: Submit {
    #[inline]
    unsafe fn append_submission<'a>(&'a self, base: SubmitBuilder<'a>, queue: &Arc<Queue>)
                                    -> Result<SubmitBuilder<'a>, Box<Error>>
    {
        self.inner.as_ref().unwrap().append_submission(base, queue)
    }
}

unsafe impl<I, L> DeviceOwned for BufferedCommandsListLayer<I, L> where I: DeviceOwned {
    #[inline]
    fn device(&self) -> &Arc<Device> {
        self.inner.as_ref().unwrap().device()
    }
}

unsafe impl<I, L> CommandBufferBuilder for BufferedCommandsListLayer<I, L> where I: DeviceOwned {
}

macro_rules! pass_through {
    (($($param:ident),*), $cmd:ty) => {
        unsafe impl<'a, I, L $(, $param)*> AddCommand<$cmd> for BufferedCommandsListLayer<I, L>
            where I: for<'r> AddCommand<&'r $cmd, Out = I>
        {
            type Out = BufferedCommandsListLayer<I, (L, $cmd)>;

            #[inline]
            fn add(self, command: $cmd) -> Self::Out {
                debug_assert!(self.inner.is_some());
                BufferedCommandsListLayer {
                    inner: self.inner,
                    commands: (self.commands, command),
                    non_flushed: self.non_flushed + 1,
                }
            }
        }
    }
}

pass_through!((Rp, F), cmd::CmdBeginRenderPass<Rp, F>);
pass_through!((S, Pl), cmd::CmdBindDescriptorSets<S, Pl>);
pass_through!((B), cmd::CmdBindIndexBuffer<B>);
pass_through!((Pl), cmd::CmdBindPipeline<Pl>);
pass_through!((V), cmd::CmdBindVertexBuffers<V>);
pass_through!((), cmd::CmdClearAttachments);
pass_through!((S, D), cmd::CmdCopyBuffer<S, D>);
pass_through!((), cmd::CmdDispatchRaw);
pass_through!((), cmd::CmdDrawIndexedRaw);
pass_through!((B), cmd::CmdDrawIndirectRaw<B>);
pass_through!((), cmd::CmdDrawRaw);
pass_through!((), cmd::CmdEndRenderPass);
pass_through!((C), cmd::CmdExecuteCommands<C>);
pass_through!((B), cmd::CmdFillBuffer<B>);
pass_through!((), cmd::CmdNextSubpass);
pass_through!((Pc, Pl), cmd::CmdPushConstants<Pc, Pl>);
pass_through!((), cmd::CmdSetState);
pass_through!((B, D), cmd::CmdUpdateBuffer<'a, B, D>);
<|endoftext|>


<commit_before>
///
/// Systems are a set of event handlers and optionally some associated state.
///
/// The event handlers are invoked with a list of event data and have access to component
/// lists. This access can be immutable or mutable which is made explicit so that
/// the event handler scheduler might parallelize handler invocations.
///
/// To properly enforce this I guess the scheduler would acquire the locks on the component
/// lists.
///
/// So the context of the handler function would consist of:
///
///   The handler state
///   The event data list
///   Immutable components lists
///   Mutable components lists
///
/// For example the following invocation:
///
/// system! my_system {
///   state! { i: i64 }
///   on!(event_name, { positions: Position}, { descriptions: Description }) {
///     state.i += 1;
///     let position = positions[0];
///     descriptions[0].set(format!("Position = {},{}", position.x, position.y));
///   }
/// }
///
/// Expands to:
///
/// pub mod my_system {
///   struct State { i: i64 }
///
///   pub fn event_name(state: &mut State, data: &Vec<event_name::Data>, positions: &Vec<Position>, descriptions: &mut Vec<Description> ) {
///     let position = positions[0];
///     descriptions[0].set(format!("Position = {},{}", position.x, position.y));
///   }
///
///   pub fn register() {
///      events::register_handler(event_name); // etc..
///   }
/// }
///
/// How are we going to register this handler at the event loop?
///
/// Ideally we would have just one 'register' call per system. But to have that the register call should enable
/// all nested handlers. We could also just have one handler per system. That would make things easier..
/// Maybe there's some scheme using recursive macros that we could use to generate a register_system that collects
/// all on! calls. Ok this is definitely a possibility. We could simply match the on! macro in the system! macro
/// and use that to populate the register function.
///
/// How are we going to store/invoke these handlers.
///
/// To invoke the handler it will require the state, the components and the data list passed in the right order.
/// we could have a separate invoker function that invokes event_name with the arguments passed in. Its arguments
/// would be the component lists in two vecs, as well as the event data list.
///
/// This could work!
///

#[macro_export]
macro_rules! system {
	( $system_name:ident { $($contents:tt)* } ) => {
		pub mod $system_name {
			use std::any::Any;
			use shared_mutex::{ SharedMutex };
			#[allow(unused_imports)]
			use $crate::entities::{ ComponentList };
			use shared_mutex::{ MappedSharedMutexReadGuard, MappedSharedMutexWriteGuard };

			system_contents!{ ( $($contents)* ) [ ] }
		}
	}
}

#[macro_export]
macro_rules! system_contents {
	// Consume on! invocations
	(
		(
			on! (
				$event_name:ident, $($event_declaration:tt)*
			) $_self:ident, $_data:ident => $event_body:block $($rest:tt)*
		) [
			$( $saved_decl:tt ),*
		]
	) => (
		on! { ($event_name, $( $event_declaration)* ) $_self , $_data => $event_body }

		system_contents!{
			( $($rest)* )
			[ ( $event_name, $( $event_declaration)* ) $(, $saved_decl)* ]
		}
	);

	(
		(
			state! { $($state_declaration:tt)* } $($rest:tt)*
		) [
			$( $saved_decl:tt ),*
		]
	) => (
		state! { $($state_declaration)* }

		system_contents!{
			( $($rest)* )
			[ $( $saved_decl ),* ]
		}
	);

	// When all content has been consumed emit register macro
	(
		() [ $( $event_declaration:tt ),* ]
	) => (
		system_register!{ $( $event_declaration ),* }
	)
}

#[macro_export]
macro_rules! on {
	( ($event_name:ident, { $( $mut_name:ident : $mut_typ:ty )* } , { $($name:ident : $typ:ty)* } )
		$_self:ident, $_data:ident => $event_body:block ) => (

		impl State {
			#[allow(unused_variables)]
			pub fn $event_name(&mut $_self,
				$_data: &Vec<super::$event_name::Data>,
				$( $name : &MappedSharedMutexReadGuard<ComponentList<$typ>>),*
				$( $mut_name : &MappedSharedMutexWriteGuard<ComponentList<$mut_typ>> ),* ) $event_body

		}

		#[allow(unused_variables)]
		#[allow(unused_mut)]
		pub fn $event_name(
				data: &Vec<super::$event_name::Data>,
				components: Vec<MappedSharedMutexReadGuard<Any>>,
				mut_components: Vec<MappedSharedMutexWriteGuard<Any>>
			) {
			let mut components_iter = components.into_iter();
			let mut mut_components_iter = mut_components.into_iter();

			$(
				let $name : MappedSharedMutexReadGuard<ComponentList<$typ>> = components_iter
					.next().expect("Event components list too short.")
					.map(|v| v.downcast_ref().expect("Event component not of expected type."));
			)*

			$(
				let $mut_name: MappedSharedMutexWriteGuard<ComponentList<$mut_typ>> = mut_components_iter
					.next().expect("Event mut_components list too short.")
					.map(|v| v.downcast_mut().expect("Event component not of expected type."));
			)*

			STATE.write().expect("Event state corrupted").$event_name(
				data,
				$(&$name),*
				$(&$mut_name),*
			);
		}
	)
}

#[macro_export]
macro_rules! state {
	( $( $name:ident : $field:ty ),* ) => (
		#[derive(Default)]
		pub struct State {
			$(pub $name : $field),*
		}

		lazy_static! {
			pub static ref STATE: SharedMutex<State> = SharedMutex::new(State::default());
		}
	)
}

#[macro_export]
macro_rules! system_register {
	( $( (
			$event_name:ident,
			{ $( $mut_name:ident : $mut_typ:ty )* } ,
			{ $( $name:ident : $typ:ty )* }
		)
	),* ) => (
		pub fn register() {
			use std::any::TypeId;
			$(
				let mut_ts = vec![ $( TypeId::of::< $mut_typ >() ),* ];
				let ts = vec![ $( TypeId::of::< $typ >() ),* ];
				super::$event_name::register_handler($event_name, ts, mut_ts);
			)*
		}
	)
}
<commit_msg>allow any item to be in system definition<commit_after>
///
/// Systems are a set of event handlers and optionally some associated state.
///
/// The event handlers are invoked with a list of event data and have access to component
/// lists. This access can be immutable or mutable which is made explicit so that
/// the event handler scheduler might parallelize handler invocations.
///
/// To properly enforce this I guess the scheduler would acquire the locks on the component
/// lists.
///
/// So the context of the handler function would consist of:
///
///   The handler state
///   The event data list
///   Immutable components lists
///   Mutable components lists
///
/// For example the following invocation:
///
/// system! my_system {
///   state! { i: i64 }
///   on!(event_name, { positions: Position}, { descriptions: Description }) {
///     state.i += 1;
///     let position = positions[0];
///     descriptions[0].set(format!("Position = {},{}", position.x, position.y));
///   }
/// }
///
/// Expands to:
///
/// pub mod my_system {
///   struct State { i: i64 }
///
///   pub fn event_name(state: &mut State, data: &Vec<event_name::Data>, positions: &Vec<Position>, descriptions: &mut Vec<Description> ) {
///     let position = positions[0];
///     descriptions[0].set(format!("Position = {},{}", position.x, position.y));
///   }
///
///   pub fn register() {
///      events::register_handler(event_name); // etc..
///   }
/// }
///
/// How are we going to register this handler at the event loop?
///
/// Ideally we would have just one 'register' call per system. But to have that the register call should enable
/// all nested handlers. We could also just have one handler per system. That would make things easier..
/// Maybe there's some scheme using recursive macros that we could use to generate a register_system that collects
/// all on! calls. Ok this is definitely a possibility. We could simply match the on! macro in the system! macro
/// and use that to populate the register function.
///
/// How are we going to store/invoke these handlers.
///
/// To invoke the handler it will require the state, the components and the data list passed in the right order.
/// we could have a separate invoker function that invokes event_name with the arguments passed in. Its arguments
/// would be the component lists in two vecs, as well as the event data list.
///
/// This could work!
///

#[macro_export]
macro_rules! system {
	( $system_name:ident { $($contents:tt)* } ) => {
		pub mod $system_name {
			use std::any::Any;
			use shared_mutex::{ SharedMutex };
			#[allow(unused_imports)]
			use $crate::entities::{ ComponentList };
			use shared_mutex::{ MappedSharedMutexReadGuard, MappedSharedMutexWriteGuard };

			system_contents!{ ( $($contents)* ) [ ] }
		}
	}
}

#[macro_export]
macro_rules! system_contents {
	// Consume on! invocations
	(
		(
			on! (
				$event_name:ident, $($event_declaration:tt)*
			) $_self:ident, $_data:ident => $event_body:block $($rest:tt)*
		) [
			$( $saved_decl:tt ),*
		]
	) => (
		on! { ($event_name, $( $event_declaration)* ) $_self , $_data => $event_body }

		system_contents!{
			( $($rest)* )
			[ ( $event_name, $( $event_declaration)* ) $(, $saved_decl)* ]
		}
	);

	(
		(
			state! { $($state_declaration:tt)* } $($rest:tt)*
		) [
			$( $saved_decl:tt ),*
		]
	) => (
		state! { $($state_declaration)* }

		system_contents!{
			( $($rest)* )
			[ $( $saved_decl ),* ]
		}
	);

	(
		(
			$token_tree:item $($rest:tt)*
		) [
			$( $saved_decl:tt ),*
		]
	) => (
		$token_tree

		system_contents!{
			( $($rest)* )
			[ $( $saved_decl ),* ]
		}

	);

	// When all content has been consumed emit register macro
	(
		() [ $( $event_declaration:tt ),* ]
	) => (
		system_register!{ $( $event_declaration ),* }
	)
}

#[macro_export]
macro_rules! on {
	( ($event_name:ident, { $( $mut_name:ident : $mut_typ:ty )* } , { $($name:ident : $typ:ty)* } )
		$_self:ident, $_data:ident => $event_body:block ) => (

		impl State {
			#[allow(unused_variables)]
			pub fn $event_name(&mut $_self,
				$_data: &Vec<super::$event_name::Data>,
				$( $name : &MappedSharedMutexReadGuard<ComponentList<$typ>>),*
				$( $mut_name : &MappedSharedMutexWriteGuard<ComponentList<$mut_typ>> ),* ) $event_body

		}

		#[allow(unused_variables)]
		#[allow(unused_mut)]
		pub fn $event_name(
				data: &Vec<super::$event_name::Data>,
				components: Vec<MappedSharedMutexReadGuard<Any>>,
				mut_components: Vec<MappedSharedMutexWriteGuard<Any>>
			) {
			let mut components_iter = components.into_iter();
			let mut mut_components_iter = mut_components.into_iter();

			$(
				let $name : MappedSharedMutexReadGuard<ComponentList<$typ>> = components_iter
					.next().expect("Event components list too short.")
					.map(|v| v.downcast_ref().expect("Event component not of expected type."));
			)*

			$(
				let $mut_name: MappedSharedMutexWriteGuard<ComponentList<$mut_typ>> = mut_components_iter
					.next().expect("Event mut_components list too short.")
					.map(|v| v.downcast_mut().expect("Event component not of expected type."));
			)*

			STATE.write().expect("Event state corrupted").$event_name(
				data,
				$(&$name),*
				$(&$mut_name),*
			);
		}
	)
}

#[macro_export]
macro_rules! state {
	( $( $name:ident : $field:ty ),* ) => (
		#[derive(Default)]
		pub struct State {
			$(pub $name : $field),*
		}

		lazy_static! {
			pub static ref STATE: SharedMutex<State> = SharedMutex::new(State::default());
		}
	)
}

#[macro_export]
macro_rules! system_register {
	( $( (
			$event_name:ident,
			{ $( $mut_name:ident : $mut_typ:ty )* } ,
			{ $( $name:ident : $typ:ty )* }
		)
	),* ) => (
		pub fn register() {
			#[allow(unused_imports)]
			use std::any::TypeId;
			$(
				let mut_ts = vec![ $( TypeId::of::< $mut_typ >() ),* ];
				let ts = vec![ $( TypeId::of::< $typ >() ),* ];
				super::$event_name::register_handler($event_name, ts, mut_ts);
			)*
		}
	)
}
<|endoftext|>


<commit_before>import std::getopts;

export output_format;
export output_style;
export config;
export default_config;
export parse_config;
export usage;

#[doc = "The type of document to output"]
enum output_format {
    #[doc = "Markdown"]
    markdown,
    #[doc = "HTML, via markdown and pandoc"]
    pandoc_html
}

#[doc = "How to organize the output"]
enum output_style {
    #[doc = "All in a single document"]
    doc_per_crate,
    #[doc = "Each module in its own document"]
    doc_per_mod
}

#[doc = "The configuration for a rustdoc session"]
type config = {
    input_crate: str,
    output_dir: str,
    output_format: output_format,
    output_style: output_style,
    pandoc_cmd: option<str>
};

fn opt_output_dir() -> str { "output-dir" }
fn opt_output_format() -> str { "output-format" }
fn opt_output_style() -> str { "output-style" }
fn opt_pandoc_cmd() -> str { "pandoc-cmd" }
fn opt_help() -> str { "h" }

fn opts() -> [(getopts::opt, str)] {
    [
        (getopts::optopt(opt_output_dir()),
         "--output-dir <val>     put documents here"),
        (getopts::optopt(opt_output_format()),
         "--output-format <val>  either 'markdown' or 'html'"),
        (getopts::optopt(opt_output_style()),
         "--output-style <val>   either 'doc-per-crate' or 'doc-per-mod'"),
        (getopts::optopt(opt_pandoc_cmd()),
         "--pandoc-cmd <val>     the command for running pandoc"),
        (getopts::optflag(opt_help()),
         "-h                     print help")
    ]
}

fn usage() {
    import std::io::println;

    println("Usage: rustdoc [options] <cratefile>\n");
    println("Options:\n");
    for opt in opts() {
        println(#fmt("    %s", tuple::second(opt)));
    }
    println("");
}

fn default_config(input_crate: str) -> config {
    {
        input_crate: input_crate,
        output_dir: ".",
        output_format: pandoc_html,
        output_style: doc_per_mod,
        pandoc_cmd: none
    }
}

fn parse_config(args: [str]) -> result::t<config, str> {
    let args = vec::tail(args);
    let opts = tuple::first(vec::unzip(opts()));
    alt getopts::getopts(args, opts) {
        result::ok(match) {
            if vec::len(match.free) == 1u {
                let input_crate = vec::head(match.free);
                config_from_opts(input_crate, match)
            } else if vec::is_empty(match.free) {
                result::err("no crates specified")
            } else {
                result::err("multiple crates specified")
            }
        }
        result::err(f) {
            result::err(getopts::fail_str(f))
        }
    }
}

fn config_from_opts(
    input_crate: str,
    match: getopts::match
) -> result::t<config, str> {

    let config = default_config(input_crate);
    let result = result::ok(config);
    let result = result::chain(result) {|config|
        let output_dir = getopts::opt_maybe_str(match, opt_output_dir());
        result::ok({
            output_dir: option::from_maybe(config.output_dir, output_dir)
            with config
        })
    };
    let result = result::chain(result) {|config|
        let output_format = getopts::opt_maybe_str(
            match, opt_output_format());
        option::maybe(result::ok(config), output_format) {|output_format|
            result::chain(parse_output_format(output_format)) {|output_format|
                result::ok({
                    output_format: output_format
                    with config
                })
            }
        }
    };
    let result = result::chain(result) {|config|
        let output_style = getopts::opt_maybe_str(match, opt_output_style());
        option::maybe(result::ok(config), output_style) {|output_style|
            result::chain(parse_output_style(output_style)) {|output_style|
                result::ok({
                    output_style: output_style
                    with config
                })
            }
        }
    };
    let result = result::chain(result) {|config|
        let pandoc_cmd = getopts::opt_maybe_str(match, opt_pandoc_cmd());
        let pandoc_cmd = maybe_find_pandoc(config, pandoc_cmd);
        result::chain(pandoc_cmd) {|pandoc_cmd|
            result::ok({
                pandoc_cmd: pandoc_cmd
                with config
            })
        }
    };
    ret result;
}

fn parse_output_format(output_format: str) -> result::t<output_format, str> {
    alt output_format {
      "markdown" { result::ok(markdown) }
      "html" { result::ok(pandoc_html) }
      _ { result::err(#fmt("unknown output format '%s'", output_format)) }
    }
}

fn parse_output_style(output_style: str) -> result::t<output_style, str> {
    alt output_style {
      "doc-per-crate" { result::ok(doc_per_crate) }
      "doc-per-mod" { result::ok(doc_per_mod) }
      _ { result::err(#fmt("unknown output style '%s'", output_style)) }
    }
}

fn maybe_find_pandoc(
    _config: config,
    maybe_pandoc_cmd: option<str>
) -> result::t<option<str>, str> {
    // FIXME: When we actually need pandoc then look for it first
    // on the path, then in cabal; test to make sure pandoc works
    alt maybe_pandoc_cmd {
      some(pandoc_cmd) { result::ok(some(pandoc_cmd)) }
      none { result::ok(some("pandoc")) }
    }
}

#[test]
fn should_error_with_no_crates() {
    let config = parse_config(["rustdoc"]);
    assert result::get_err(config) == "no crates specified";
}

#[test]
fn should_error_with_multiple_crates() {
    let config = parse_config(["rustdoc", "crate1.rc", "crate2.rc"]);
    assert result::get_err(config) == "multiple crates specified";
}

#[test]
fn should_set_output_dir_to_cwd_if_not_provided() {
    let config = parse_config(["rustdoc", "crate.rc"]);
    assert result::get(config).output_dir == ".";
}

#[test]
fn should_set_output_dir_if_provided() {
    let config = parse_config([
        "rustdoc", "crate.rc", "--output-dir", "snuggles"
    ]);
    assert result::get(config).output_dir == "snuggles";
}

#[test]
fn should_set_output_format_to_pandoc_html_if_not_provided() {
    let config = parse_config(["rustdoc", "crate.rc"]);
    assert result::get(config).output_format == pandoc_html;
}

#[test]
fn should_set_output_format_to_markdown_if_requested() {
    let config = parse_config([
        "rustdoc", "crate.rc", "--output-format", "markdown"
    ]);
    assert result::get(config).output_format == markdown;
}

#[test]
fn should_set_output_format_to_pandoc_html_if_requested() {
    let config = parse_config([
        "rustdoc", "crate.rc", "--output-format", "html"
    ]);
    assert result::get(config).output_format == pandoc_html;
}

#[test]
fn should_error_on_bogus_format() {
    let config = parse_config([
        "rustdoc", "crate.rc", "--output-format", "bogus"
    ]);
    assert result::get_err(config) == "unknown output format 'bogus'";
}

#[test]
fn should_set_output_style_to_doc_per_mod_by_default() {
    let config = parse_config(["rustdoc", "crate.rc"]);
    assert result::get(config).output_style == doc_per_mod;
}

#[test]
fn should_set_output_style_to_one_doc_if_requested() {
    let config = parse_config([
        "rustdoc", "crate.rc", "--output-style", "doc-per-crate"
    ]);
    assert result::get(config).output_style == doc_per_crate;
}

#[test]
fn should_set_output_style_to_doc_per_mod_if_requested() {
    let config = parse_config([
        "rustdoc", "crate.rc", "--output-style", "doc-per-mod"
    ]);
    assert result::get(config).output_style == doc_per_mod;
}

#[test]
fn should_error_on_bogus_output_style() {
    let config = parse_config([
        "rustdoc", "crate.rc", "--output-style", "bogus"
    ]);
    assert result::get_err(config) == "unknown output style 'bogus'";
}

#[test]
fn should_set_pandoc_command_if_requested() {
    let config = parse_config([
        "rustdoc", "crate.rc", "--pandoc-cmd", "panda-bear-doc"
    ]);
    assert result::get(config).pandoc_cmd == some("panda-bear-doc");
}

#[test]
fn should_set_pandoc_command_when_using_pandoc() {
    let config = parse_config(["rustdoc", "crate.rc"]);
    assert result::get(config).pandoc_cmd == some("pandoc");
}<commit_msg>rustdoc: Detect when pandoc isn't available<commit_after>import std::getopts;

export output_format;
export output_style;
export config;
export default_config;
export parse_config;
export usage;

#[doc = "The type of document to output"]
enum output_format {
    #[doc = "Markdown"]
    markdown,
    #[doc = "HTML, via markdown and pandoc"]
    pandoc_html
}

#[doc = "How to organize the output"]
enum output_style {
    #[doc = "All in a single document"]
    doc_per_crate,
    #[doc = "Each module in its own document"]
    doc_per_mod
}

#[doc = "The configuration for a rustdoc session"]
type config = {
    input_crate: str,
    output_dir: str,
    output_format: output_format,
    output_style: output_style,
    pandoc_cmd: option<str>
};

fn opt_output_dir() -> str { "output-dir" }
fn opt_output_format() -> str { "output-format" }
fn opt_output_style() -> str { "output-style" }
fn opt_pandoc_cmd() -> str { "pandoc-cmd" }
fn opt_help() -> str { "h" }

fn opts() -> [(getopts::opt, str)] {
    [
        (getopts::optopt(opt_output_dir()),
         "--output-dir <val>     put documents here"),
        (getopts::optopt(opt_output_format()),
         "--output-format <val>  either 'markdown' or 'html'"),
        (getopts::optopt(opt_output_style()),
         "--output-style <val>   either 'doc-per-crate' or 'doc-per-mod'"),
        (getopts::optopt(opt_pandoc_cmd()),
         "--pandoc-cmd <val>     the command for running pandoc"),
        (getopts::optflag(opt_help()),
         "-h                     print help")
    ]
}

fn usage() {
    import std::io::println;

    println("Usage: rustdoc [options] <cratefile>\n");
    println("Options:\n");
    for opt in opts() {
        println(#fmt("    %s", tuple::second(opt)));
    }
    println("");
}

fn default_config(input_crate: str) -> config {
    {
        input_crate: input_crate,
        output_dir: ".",
        output_format: pandoc_html,
        output_style: doc_per_mod,
        pandoc_cmd: none
    }
}

type program_output = fn~(str, [str]) -> {status: int, out: str, err: str};

fn mock_program_output(_prog: str, _args: [str]) -> {
    status: int, out: str, err: str
} {
    {
        status: 0,
        out: "",
        err: ""
    }
}

fn parse_config(args: [str]) -> result::t<config, str> {
    parse_config_(args, std::run::program_output)
}

fn parse_config_(
    args: [str],
    program_output: program_output
) -> result::t<config, str> {
    let args = vec::tail(args);
    let opts = tuple::first(vec::unzip(opts()));
    alt getopts::getopts(args, opts) {
        result::ok(match) {
            if vec::len(match.free) == 1u {
                let input_crate = vec::head(match.free);
                config_from_opts(input_crate, match, program_output)
            } else if vec::is_empty(match.free) {
                result::err("no crates specified")
            } else {
                result::err("multiple crates specified")
            }
        }
        result::err(f) {
            result::err(getopts::fail_str(f))
        }
    }
}

fn config_from_opts(
    input_crate: str,
    match: getopts::match,
    program_output: program_output
) -> result::t<config, str> {

    let config = default_config(input_crate);
    let result = result::ok(config);
    let result = result::chain(result) {|config|
        let output_dir = getopts::opt_maybe_str(match, opt_output_dir());
        result::ok({
            output_dir: option::from_maybe(config.output_dir, output_dir)
            with config
        })
    };
    let result = result::chain(result) {|config|
        let output_format = getopts::opt_maybe_str(
            match, opt_output_format());
        option::maybe(result::ok(config), output_format) {|output_format|
            result::chain(parse_output_format(output_format)) {|output_format|
                result::ok({
                    output_format: output_format
                    with config
                })
            }
        }
    };
    let result = result::chain(result) {|config|
        let output_style = getopts::opt_maybe_str(match, opt_output_style());
        option::maybe(result::ok(config), output_style) {|output_style|
            result::chain(parse_output_style(output_style)) {|output_style|
                result::ok({
                    output_style: output_style
                    with config
                })
            }
        }
    };
    let result = result::chain(result) {|config|
        let pandoc_cmd = getopts::opt_maybe_str(match, opt_pandoc_cmd());
        let pandoc_cmd = maybe_find_pandoc(
            config, pandoc_cmd, program_output);
        result::chain(pandoc_cmd) {|pandoc_cmd|
            result::ok({
                pandoc_cmd: pandoc_cmd
                with config
            })
        }
    };
    ret result;
}

fn parse_output_format(output_format: str) -> result::t<output_format, str> {
    alt output_format {
      "markdown" { result::ok(markdown) }
      "html" { result::ok(pandoc_html) }
      _ { result::err(#fmt("unknown output format '%s'", output_format)) }
    }
}

fn parse_output_style(output_style: str) -> result::t<output_style, str> {
    alt output_style {
      "doc-per-crate" { result::ok(doc_per_crate) }
      "doc-per-mod" { result::ok(doc_per_mod) }
      _ { result::err(#fmt("unknown output style '%s'", output_style)) }
    }
}

fn maybe_find_pandoc(
    config: config,
    maybe_pandoc_cmd: option<str>,
    program_output: program_output
) -> result::t<option<str>, str> {
    if config.output_format != pandoc_html {
        ret result::ok(maybe_pandoc_cmd);
    }

    let possible_pandocs = alt maybe_pandoc_cmd {
      some(pandoc_cmd) { [pandoc_cmd] }
      none {
        // FIXME (1936): Need to be able to expand ~ to look in .cabal
        ["pandoc"]
      }
    };

    let pandoc = vec::find(possible_pandocs) {|pandoc|
        let output = program_output(pandoc, ["--version"]);
        #debug("testing pandoc cmd %s: %?", pandoc, output);
        output.status == 0
    };

    if option::is_some(pandoc) {
        result::ok(pandoc)
    } else {
        result::err("couldn't find pandoc")
    }
}

#[test]
fn should_find_pandoc() {
    let config = {
        output_format: pandoc_html
        with default_config("test")
    };
    let mock_program_output = fn~(_prog: str, _args: [str]) -> {
        status: int, out: str, err: str
    } {
        {
            status: 0, out: "pandoc 1.8.2.1", err: ""
        }
    };
    let result = maybe_find_pandoc(config, none, mock_program_output);
    assert result == result::ok(some("pandoc"));
}

#[test]
fn should_error_with_no_pandoc() {
    let config = {
        output_format: pandoc_html
        with default_config("test")
    };
    let mock_program_output = fn~(_prog: str, _args: [str]) -> {
        status: int, out: str, err: str
    } {
        {
            status: 1, out: "", err: ""
        }
    };
    let result = maybe_find_pandoc(config, none, mock_program_output);
    assert result == result::err("couldn't find pandoc");
}

#[cfg(test)]
mod test {
    fn parse_config(args: [str]) -> result::t<config, str> {
        parse_config_(args, mock_program_output)
    }
}

#[test]
fn should_error_with_no_crates() {
    let config = test::parse_config(["rustdoc"]);
    assert result::get_err(config) == "no crates specified";
}

#[test]
fn should_error_with_multiple_crates() {
    let config = test::parse_config(["rustdoc", "crate1.rc", "crate2.rc"]);
    assert result::get_err(config) == "multiple crates specified";
}

#[test]
fn should_set_output_dir_to_cwd_if_not_provided() {
    let config = test::parse_config(["rustdoc", "crate.rc"]);
    assert result::get(config).output_dir == ".";
}

#[test]
fn should_set_output_dir_if_provided() {
    let config = test::parse_config([
        "rustdoc", "crate.rc", "--output-dir", "snuggles"
    ]);
    assert result::get(config).output_dir == "snuggles";
}

#[test]
fn should_set_output_format_to_pandoc_html_if_not_provided() {
    let config = test::parse_config(["rustdoc", "crate.rc"]);
    assert result::get(config).output_format == pandoc_html;
}

#[test]
fn should_set_output_format_to_markdown_if_requested() {
    let config = test::parse_config([
        "rustdoc", "crate.rc", "--output-format", "markdown"
    ]);
    assert result::get(config).output_format == markdown;
}

#[test]
fn should_set_output_format_to_pandoc_html_if_requested() {
    let config = test::parse_config([
        "rustdoc", "crate.rc", "--output-format", "html"
    ]);
    assert result::get(config).output_format == pandoc_html;
}

#[test]
fn should_error_on_bogus_format() {
    let config = test::parse_config([
        "rustdoc", "crate.rc", "--output-format", "bogus"
    ]);
    assert result::get_err(config) == "unknown output format 'bogus'";
}

#[test]
fn should_set_output_style_to_doc_per_mod_by_default() {
    let config = test::parse_config(["rustdoc", "crate.rc"]);
    assert result::get(config).output_style == doc_per_mod;
}

#[test]
fn should_set_output_style_to_one_doc_if_requested() {
    let config = test::parse_config([
        "rustdoc", "crate.rc", "--output-style", "doc-per-crate"
    ]);
    assert result::get(config).output_style == doc_per_crate;
}

#[test]
fn should_set_output_style_to_doc_per_mod_if_requested() {
    let config = test::parse_config([
        "rustdoc", "crate.rc", "--output-style", "doc-per-mod"
    ]);
    assert result::get(config).output_style == doc_per_mod;
}

#[test]
fn should_error_on_bogus_output_style() {
    let config = test::parse_config([
        "rustdoc", "crate.rc", "--output-style", "bogus"
    ]);
    assert result::get_err(config) == "unknown output style 'bogus'";
}

#[test]
fn should_set_pandoc_command_if_requested() {
    let config = test::parse_config([
        "rustdoc", "crate.rc", "--pandoc-cmd", "panda-bear-doc"
    ]);
    assert result::get(config).pandoc_cmd == some("panda-bear-doc");
}

#[test]
fn should_set_pandoc_command_when_using_pandoc() {
    let config = test::parse_config(["rustdoc", "crate.rc"]);
    assert result::get(config).pandoc_cmd == some("pandoc");
}<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::io::net::ip::Port;
use std::io::net::tcp::{TcpListener,TcpStream};
use std::io::timer;
use std::io::{Acceptor, Listener};
use std::str;
use std::time::Duration;

use self::Op::{SendBytes, ReceiveBytes, Wait, Shutdown};

// Global handle to the running test HTTP server
local_data_key!(handle: Handle)

// Setup an op sequence with the test HTTP server
pub fn setup(ops: OpSequence) -> OpSequenceResult {
    // If the server si not started
    ensure_server_started();

    // Setup a channel to receive the response on
    let (tx, rx) = channel();

    // Send the op sequence to the server task
    handle.get().unwrap().send(ops, tx);

    OpSequenceResult::new(rx)
}

pub fn url(path: &str) -> String {
    format!("http://localhost:{}{}", port(), path)
}

fn port() -> uint {
    ensure_server_started();
    handle.get().unwrap().port()
}

/* Handle to the running HTTP server task. Communication with the server
 * happesn over channels.
 */
struct Handle {
    port: Port,
    tx: Sender<(OpSequence, Sender<Result<(),String>>)>
}

/* Operations for the test server to perform:
 * - Send some bytes
 * - Expect to receive bytes
 * - Wait for a certain amount of time
 * - Shutdown the server (allows a clean exit at the end of the tests)
 */
#[deriving(Clone,PartialEq,Show)]
pub enum Op {
    SendBytes(&'static [u8]),
    ReceiveBytes(&'static [u8]),
    Wait(uint),
    Shutdown
}

/* An ordered sequence of operations for the HTTP server to perform
*/
#[deriving(Show)]
pub struct OpSequence {
    ops: Vec<Op>
}

/* Represents the completion of the of the op sequence by the HTTP
 * server.
 */
pub struct OpSequenceResult {
    rx: Receiver<Result<(),String>>
}

impl OpSequence {
    pub fn new(op: Op) -> OpSequence {
        OpSequence { ops: vec!(op) }
    }

    pub fn concat(op: Op, seq: OpSequence) -> OpSequence {
        let mut ops = vec!(op);
        ops.push_all(seq.ops.as_slice());
        OpSequence { ops: ops }
    }

    pub fn is_shutdown(&self) -> bool {
        self.ops.len() == 1 && self.ops[0] == Shutdown
    }

    pub fn apply(&self, sock: &mut TcpStream, port: uint) -> Result<(), String> {
        for op in self.ops.iter() {
            match op {
                &SendBytes(b) => {
                    let b = insert_port(b, port);
                    match sock.write(b.as_slice()) {
                        Ok(_) => {}
                        Err(e) => return Err(e.desc.to_string())
                    }
                }
                &ReceiveBytes(b) => {
                    let b = insert_port(b, port);
                    let mut rem = b.len();
                    let mut act = Vec::from_elem(rem, 0u8);

                    while rem > 0 {
                        match sock.read(act.slice_from_mut(b.len() - rem)) {
                            Ok(i) => rem = rem - i,
                            Err(e) => {
                                debug!("aborting due to error; error={}; remaining={}; bytes=\n{}",
                                       e.desc.to_string(), rem, to_debug_str(act.as_slice()));
                                return Err(e.desc.to_string())
                            }
                        }
                    }
                    debug!("server received bytes; bytes=\n{}", to_debug_str(act.as_slice()));

                    let req1 = parse_request(b.as_slice());
                    let req2 = parse_request(act.as_slice());

                    if req1 != req2 {
                        return Err(format!(
                                "received unexpected byte sequence.\n\nExpected:\n{}\n\nReceived:\n{}",
                                to_debug_str(b.as_slice()), to_debug_str(act.as_slice())));
                    }
                }
                &Wait(ms) => { timer::sleep(Duration::milliseconds(ms as i64)) }
                &Shutdown => return Err("Shutdown must be sent on its own".to_string())
            }
        }

        return Ok(());

        fn insert_port(bytes: &'static [u8], port: uint) -> Vec<u8> {
            let s = str::from_utf8(bytes).unwrap();
            let p = port.to_string();
            str::replace(s, "{PORT}", p.as_slice()).into_bytes()
        }

        fn parse_request<'a>(req: &'a [u8]) -> (&'a [u8],
                                                HashSet<&'a [u8]>,
                                                &'a [u8]) {
            let mut start = None;
            let mut headers = HashSet::new();
            let mut taken = 0;

            for part in req.split(|a| *a == b'\n') {
                taken += part.len() + 1;

                if start.is_none() {
                    start = Some(part);
                } else if part.len() == 1 {
                    break;
                } else {
                    headers.insert(part);
                }
            }

            if taken > req.len() {
                taken = req.len();
            }

            (start.unwrap(), headers, req.slice_from(taken))
        }
    }
}

fn to_debug_str(bytes: &[u8]) -> String {
    let mut ret = String::new();

    for b in bytes.iter() {
        let b = *b as char;

        if b >= ' ' && b <= '~' {
            ret.push(b);
        }
        else if b == '\n' {
            ret.push_str("\\n\n");
        }
        else if b == '\r' {
            ret.push_str("\\r");
        }
        else {
            ret.push('?');
        }
    }

    ret
}

impl Handle {
    fn new(tx: Sender<(OpSequence, Sender<Result<(),String>>)>, port: Port) -> Handle {
        Handle { tx: tx, port: port }
    }

    fn send(&self, ops: OpSequence, resp: Sender<Result<(),String>>) {
        self.tx.send((ops, resp));
    }

    fn port(&self) -> uint {
        self.port as uint
    }
}

impl Drop for Handle {
    fn drop(&mut self) {
        let (tx, rx) = channel();
        self.send(OpSequence::new(Shutdown), tx);
        rx.recv().unwrap();
    }
}

impl OpSequenceResult {
    pub fn new(rx: Receiver<Result<(),String>>) -> OpSequenceResult {
        OpSequenceResult { rx: rx }
    }

    pub fn assert(&self) {
        match self.rx.recv() {
            Ok(_) => {}
            Err(e) => panic!("http exchange did not proceed as expected: {}", e)
        }
    }
}

fn ensure_server_started() {
    if handle.get().is_none() {
        handle.replace(Some(start_server()));
    }
}

fn start_server() -> Handle {
    let (ops_tx, ops_rx) = channel();
    let (ini_tx, ini_rx) = channel();

    let mut listener = TcpListener::bind("127.0.0.1:0").unwrap();
    let port = listener.socket_name().unwrap().port;

    spawn(proc() {
        let listener = listener;
        let mut srv = listener.listen().unwrap();

        ini_tx.send(true);

        loop {
            let (ops, resp_tx): (OpSequence, Sender<Result<(),String>>) = ops_rx.recv();

            if ops.is_shutdown() {
                resp_tx.send(Ok(()));
                return;
            }

            let mut sock = match srv.accept() {
                Ok(s) => s,
                Err(e) => {
                    resp_tx.send(Err(format!("server accept err: {}", e)));
                    return;
                }
            };

            sock.set_timeout(Some(100));

            resp_tx.send(ops.apply(&mut sock, port as uint));
        }
    });

    // Wait until the server is listening
    ini_rx.recv();

    Handle::new(ops_tx, port)
}
<commit_msg>Change usage of local_data_key! to thread_local!<commit_after>use std::collections::HashSet;
use std::io::net::ip::Port;
use std::io::net::tcp::{TcpListener,TcpStream};
use std::io::timer;
use std::io::{Acceptor, Listener};
use std::str;
use std::time::Duration;

use self::Op::{SendBytes, ReceiveBytes, Wait, Shutdown};

// Global handle to the running test HTTP server
thread_local!(static HANDLE: Handle = start_server())

// Setup an op sequence with the test HTTP server
pub fn setup(ops: OpSequence) -> OpSequenceResult {
    // Setup a channel to receive the response on
    let (tx, rx) = channel();

    // Send the op sequence to the server task
    HANDLE.with(|h| {
        h.send(ops, tx);
    });

    OpSequenceResult::new(rx)
}

pub fn url(path: &str) -> String {
    format!("http://localhost:{}{}", port(), path)
}

fn port() -> uint {
    HANDLE.with(|h| {
        h.port()
    })
}

/* Handle to the running HTTP server task. Communication with the server
 * happens over channels.
 */
struct Handle {
    port: Port,
    tx: Sender<(OpSequence, Sender<Result<(),String>>)>
}

/* Operations for the test server to perform:
 * - Send some bytes
 * - Expect to receive bytes
 * - Wait for a certain amount of time
 * - Shutdown the server (allows a clean exit at the end of the tests)
 */
#[deriving(Clone,PartialEq,Show)]
pub enum Op {
    SendBytes(&'static [u8]),
    ReceiveBytes(&'static [u8]),
    Wait(uint),
    Shutdown
}

/* An ordered sequence of operations for the HTTP server to perform
*/
#[deriving(Show)]
pub struct OpSequence {
    ops: Vec<Op>
}

/* Represents the completion of the of the op sequence by the HTTP
 * server.
 */
pub struct OpSequenceResult {
    rx: Receiver<Result<(),String>>
}

impl OpSequence {
    pub fn new(op: Op) -> OpSequence {
        OpSequence { ops: vec!(op) }
    }

    pub fn concat(op: Op, seq: OpSequence) -> OpSequence {
        let mut ops = vec!(op);
        ops.push_all(seq.ops.as_slice());
        OpSequence { ops: ops }
    }

    pub fn is_shutdown(&self) -> bool {
        self.ops.len() == 1 && self.ops[0] == Shutdown
    }

    pub fn apply(&self, sock: &mut TcpStream, port: uint) -> Result<(), String> {
        for op in self.ops.iter() {
            match op {
                &SendBytes(b) => {
                    let b = insert_port(b, port);
                    match sock.write(b.as_slice()) {
                        Ok(_) => {}
                        Err(e) => return Err(e.desc.to_string())
                    }
                }
                &ReceiveBytes(b) => {
                    let b = insert_port(b, port);
                    let mut rem = b.len();
                    let mut act = Vec::from_elem(rem, 0u8);

                    while rem > 0 {
                        match sock.read(act.slice_from_mut(b.len() - rem)) {
                            Ok(i) => rem = rem - i,
                            Err(e) => {
                                debug!("aborting due to error; error={}; remaining={}; bytes=\n{}",
                                       e.desc.to_string(), rem, to_debug_str(act.as_slice()));
                                return Err(e.desc.to_string())
                            }
                        }
                    }
                    debug!("server received bytes; bytes=\n{}", to_debug_str(act.as_slice()));

                    let req1 = parse_request(b.as_slice());
                    let req2 = parse_request(act.as_slice());

                    if req1 != req2 {
                        return Err(format!(
                                "received unexpected byte sequence.\n\nExpected:\n{}\n\nReceived:\n{}",
                                to_debug_str(b.as_slice()), to_debug_str(act.as_slice())));
                    }
                }
                &Wait(ms) => { timer::sleep(Duration::milliseconds(ms as i64)) }
                &Shutdown => return Err("Shutdown must be sent on its own".to_string())
            }
        }

        return Ok(());

        fn insert_port(bytes: &'static [u8], port: uint) -> Vec<u8> {
            let s = str::from_utf8(bytes).unwrap();
            let p = port.to_string();
            str::replace(s, "{PORT}", p.as_slice()).into_bytes()
        }

        fn parse_request<'a>(req: &'a [u8]) -> (&'a [u8],
                                                HashSet<&'a [u8]>,
                                                &'a [u8]) {
            let mut start = None;
            let mut headers = HashSet::new();
            let mut taken = 0;

            for part in req.split(|a| *a == b'\n') {
                taken += part.len() + 1;

                if start.is_none() {
                    start = Some(part);
                } else if part.len() == 1 {
                    break;
                } else {
                    headers.insert(part);
                }
            }

            if taken > req.len() {
                taken = req.len();
            }

            (start.unwrap(), headers, req.slice_from(taken))
        }
    }
}

fn to_debug_str(bytes: &[u8]) -> String {
    let mut ret = String::new();

    for b in bytes.iter() {
        let b = *b as char;

        if b >= ' ' && b <= '~' {
            ret.push(b);
        }
        else if b == '\n' {
            ret.push_str("\\n\n");
        }
        else if b == '\r' {
            ret.push_str("\\r");
        }
        else {
            ret.push('?');
        }
    }

    ret
}

impl Handle {
    fn new(tx: Sender<(OpSequence, Sender<Result<(),String>>)>, port: Port) -> Handle {
        Handle { tx: tx, port: port }
    }

    fn send(&self, ops: OpSequence, resp: Sender<Result<(),String>>) {
        self.tx.send((ops, resp));
    }

    fn port(&self) -> uint {
        self.port as uint
    }
}

impl Drop for Handle {
    fn drop(&mut self) {
        let (tx, rx) = channel();
        self.send(OpSequence::new(Shutdown), tx);
        rx.recv().unwrap();
    }
}

impl OpSequenceResult {
    pub fn new(rx: Receiver<Result<(),String>>) -> OpSequenceResult {
        OpSequenceResult { rx: rx }
    }

    pub fn assert(&self) {
        match self.rx.recv() {
            Ok(_) => {}
            Err(e) => panic!("http exchange did not proceed as expected: {}", e)
        }
    }
}

fn start_server() -> Handle {
    let (ops_tx, ops_rx) = channel();
    let (ini_tx, ini_rx) = channel();

    let mut listener = TcpListener::bind("127.0.0.1:0").unwrap();
    let port = listener.socket_name().unwrap().port;

    spawn(proc() {
        let listener = listener;
        let mut srv = listener.listen().unwrap();

        ini_tx.send(true);

        loop {
            let (ops, resp_tx): (OpSequence, Sender<Result<(),String>>) = ops_rx.recv();

            if ops.is_shutdown() {
                resp_tx.send(Ok(()));
                return;
            }

            let mut sock = match srv.accept() {
                Ok(s) => s,
                Err(e) => {
                    resp_tx.send(Err(format!("server accept err: {}", e)));
                    return;
                }
            };

            sock.set_timeout(Some(100));

            resp_tx.send(ops.apply(&mut sock, port as uint));
        }
    });

    // Wait until the server is listening
    ini_rx.recv();

    Handle::new(ops_tx, port)
}
<|endoftext|>