<commit_before>use std::result::Result;
use std::marker::PhantomData;
use std::error::Error;
use serde::*;
use serde_json::*;

use super::super::tool::*;
use super::super::environment::*;

///
/// Represents a tool made from a function
///
pub struct FnTool<TIn: Deserialize, TOut: Serialize, TErr: Serialize> {
    function: Box<Fn(TIn) -> Result<TOut, TErr>>
}

///
/// Creates a Tool from a function that can produce an error but does not use an environment
///
pub fn make_tool<TIn: Deserialize, TOut: Serialize, TErr: Serialize, F: 'static+Fn(TIn) -> Result<TOut, TErr>>(function: F) -> FnTool<TIn, TOut, TErr> {
    FnTool { function: Box::new(function) }
}

///
/// Creates a Tool from a function that cannot produce an error and doesn't use an environment
///
pub fn make_pure_tool<TIn: Deserialize, TOut: Serialize, F: 'static+Fn(TIn) -> TOut>(function: F) -> FnTool<TIn, TOut, ()> {
    make_tool(move |input| { Ok(function(input)) })
}

impl<TIn, TOut, TErr> Tool for FnTool<TIn, TOut, TErr>
where TIn: Deserialize, TOut: Serialize, TErr: Serialize {
    fn invoke_json(&self, input: Value, _environment: &Environment) -> Result<Value, Value> {
        // Decode
        let input_decoded = from_value::<TIn>(input);

        // Chain into the tool itself
        match input_decoded {
            Ok(input_decoded) => {
                // Successfully decoded as the tool's input format
                let result = (self.function)(input_decoded);

                // Encode the error or the result
                // The encoding can go wrong, and we need to handle it slightly differently for the error or the success case
                match result {
                    Ok(res) => {
                        let encoded = to_value(res);
                        match encoded {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "JSON encode failed",
                                "description":  erm.description()
                            }])
                        }
                    },

                    Err(erm) => {
                        let encoded = to_value(erm);
                        match encoded {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "Error encode failed",
                                "description":  erm.description()
                            }])
                        }
                    }
                }
            },

            Err(input_error) => {
                // Input does not match the format expected by the tool
                Err(json![{
                    "error":        "JSON input decode failed",
                    "description":  input_error.description(),
                }])
            }
        }
    }
}

///
/// Represents a tool with Rust types
///
pub struct TypedTool<TIn: Serialize, TOut: Deserialize> {
    param1: PhantomData<TIn>,
    param2: PhantomData<TOut>,
    tool: Box<Tool>

    // tool could be &'a Tool and it would work as Box<Tool> implements Tool - except rust's deref behaviour
    // gets in the way: trying to pass a reference to a box actually passes a reference to the boxed thing, which
    // then doesn't work because the size is unknown
}

impl<TIn: Serialize, TOut: Deserialize> TypedTool<TIn, TOut> {
    ///
    /// Creates an object that can be used to invoke a tool with typed parameters instead of pure JSON
    ///
    pub fn from(tool: Box<Tool>) -> TypedTool<TIn, TOut> {
        TypedTool { param1: PhantomData, param2: PhantomData, tool: tool }
    }

    ///
    /// Invokes this tool
    ///
    pub fn invoke(&self, input: TIn, environment: &Environment) -> Result<TOut, Value> {
        let json_input = to_value(input);

        match json_input {
            Ok(json_input) => {
                let json_output = self.tool.invoke_json(json_input, environment);

                match json_output {
                    Ok(json_output) => {
                        let result = from_value::<TOut>(json_output);
                        match result {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "Result decode failed",
                                "description":  erm.description()
                            }])
                        }
                    },

                    Err(json_error) => {
                        Err(json_error)
                    }
                }
            },

            Err(erm) => {
                Err(json![{
                    "error":        "Input encode failed",
                    "description":  erm.description()
                }])
            }
        }
    }
}

///
/// Extension trait for environments that makes it possible to get a Rust typed tool immediately
///
pub trait TypedEnvironment {
    ///
    /// Retrieves a tool using a Rust interface by name
    ///
    fn get_typed_tool<'a, TIn: Serialize, TOut: Deserialize>(&self, name: &str) -> Result<TypedTool<TIn, TOut>, RetrieveToolError>;
}

impl<TEnv> TypedEnvironment for TEnv where TEnv: Environment {
    ///
    /// Retrieves a tool using a Rust interface by name
    ///
    fn get_typed_tool<'a, TIn: Serialize, TOut: Deserialize>(&self, name: &str) -> Result<TypedTool<TIn, TOut>, RetrieveToolError> {
        self.get_json_tool(name).map(|tool| TypedTool::from(tool))
    }
}

#[cfg(test)]
mod test {
    use super::super::super::*;
    use super::super::*;
    use std::result::Result;
    use serde_json::*;              // Rust says unused, but the json! macro will not work without this

    #[derive(Serialize, Deserialize)]
    struct TestIn {
        input: i32
    }

    #[derive(Serialize, Deserialize, PartialEq, Eq)]
    struct TestOut {
        output: i32
    }

    fn test_tool(x: TestIn) -> Result<TestOut, ()> {
        Ok(TestOut { output: x.input + 1 })
    }

    #[test]
    fn can_call_tool_via_json_interface() {
        let tool        = make_tool(test_tool);
        let environment = EmptyEnvironment::new();
        let result      = tool.invoke_json(json![{ "input": 4 }], &environment);

        assert!(result == Ok(json![{ "output": 5 }]));
    }

    #[test]
    fn can_call_tool_via_typed_interface() {
        let tool        = make_pure_tool(|x: i32| { x+1 });
        let environment = EmptyEnvironment::new();

        let typed_tool  = TypedTool::from(Box::new(tool));
        let result      = typed_tool.invoke(4, &environment);

        assert!(result == Ok(5));
    }
}
<commit_msg>Make it so that functional tools have to implement Send<commit_after>use std::result::Result;
use std::marker::PhantomData;
use std::error::Error;
use serde::*;
use serde_json::*;

use super::super::tool::*;
use super::super::environment::*;

///
/// Represents a tool made from a function
///
pub struct FnTool<TIn: Deserialize, TOut: Serialize, TErr: Serialize> {
    function: Box<Fn(TIn) -> Result<TOut, TErr> + Send>
}

///
/// Creates a Tool from a function that can produce an error but does not use an environment
///
pub fn make_tool<TIn: Deserialize, TOut: Serialize, TErr: Serialize, F: 'static+Send+Fn(TIn) -> Result<TOut, TErr>>(function: F) -> FnTool<TIn, TOut, TErr> {
    FnTool { function: Box::new(function) }
}

///
/// Creates a Tool from a function that cannot produce an error and doesn't use an environment
///
pub fn make_pure_tool<TIn: Deserialize, TOut: Serialize, F: 'static+Send+Fn(TIn) -> TOut>(function: F) -> FnTool<TIn, TOut, ()> {
    make_tool(move |input| { Ok(function(input)) })
}

impl<TIn, TOut, TErr> Tool for FnTool<TIn, TOut, TErr>
where TIn: Deserialize, TOut: Serialize, TErr: Serialize {
    fn invoke_json(&self, input: Value, _environment: &Environment) -> Result<Value, Value> {
        // Decode
        let input_decoded = from_value::<TIn>(input);

        // Chain into the tool itself
        match input_decoded {
            Ok(input_decoded) => {
                // Successfully decoded as the tool's input format
                let result = (self.function)(input_decoded);

                // Encode the error or the result
                // The encoding can go wrong, and we need to handle it slightly differently for the error or the success case
                match result {
                    Ok(res) => {
                        let encoded = to_value(res);
                        match encoded {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "JSON encode failed",
                                "description":  erm.description()
                            }])
                        }
                    },

                    Err(erm) => {
                        let encoded = to_value(erm);
                        match encoded {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "Error encode failed",
                                "description":  erm.description()
                            }])
                        }
                    }
                }
            },

            Err(input_error) => {
                // Input does not match the format expected by the tool
                Err(json![{
                    "error":        "JSON input decode failed",
                    "description":  input_error.description(),
                }])
            }
        }
    }
}

///
/// Represents a tool with Rust types
///
pub struct TypedTool<TIn: Serialize, TOut: Deserialize> {
    param1: PhantomData<TIn>,
    param2: PhantomData<TOut>,
    tool: Box<Tool>

    // tool could be &'a Tool and it would work as Box<Tool> implements Tool - except rust's deref behaviour
    // gets in the way: trying to pass a reference to a box actually passes a reference to the boxed thing, which
    // then doesn't work because the size is unknown
}

impl<TIn: Serialize, TOut: Deserialize> TypedTool<TIn, TOut> {
    ///
    /// Creates an object that can be used to invoke a tool with typed parameters instead of pure JSON
    ///
    pub fn from(tool: Box<Tool>) -> TypedTool<TIn, TOut> {
        TypedTool { param1: PhantomData, param2: PhantomData, tool: tool }
    }

    ///
    /// Invokes this tool
    ///
    pub fn invoke(&self, input: TIn, environment: &Environment) -> Result<TOut, Value> {
        let json_input = to_value(input);

        match json_input {
            Ok(json_input) => {
                let json_output = self.tool.invoke_json(json_input, environment);

                match json_output {
                    Ok(json_output) => {
                        let result = from_value::<TOut>(json_output);
                        match result {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "Result decode failed",
                                "description":  erm.description()
                            }])
                        }
                    },

                    Err(json_error) => {
                        Err(json_error)
                    }
                }
            },

            Err(erm) => {
                Err(json![{
                    "error":        "Input encode failed",
                    "description":  erm.description()
                }])
            }
        }
    }
}

///
/// Extension trait for environments that makes it possible to get a Rust typed tool immediately
///
pub trait TypedEnvironment {
    ///
    /// Retrieves a tool using a Rust interface by name
    ///
    fn get_typed_tool<'a, TIn: Serialize, TOut: Deserialize>(&self, name: &str) -> Result<TypedTool<TIn, TOut>, RetrieveToolError>;
}

impl<TEnv> TypedEnvironment for TEnv where TEnv: Environment {
    ///
    /// Retrieves a tool using a Rust interface by name
    ///
    fn get_typed_tool<'a, TIn: Serialize, TOut: Deserialize>(&self, name: &str) -> Result<TypedTool<TIn, TOut>, RetrieveToolError> {
        self.get_json_tool(name).map(|tool| TypedTool::from(tool))
    }
}

#[cfg(test)]
mod test {
    use super::super::super::*;
    use super::super::*;
    use std::result::Result;
    use serde_json::*;              // Rust says unused, but the json! macro will not work without this

    #[derive(Serialize, Deserialize)]
    struct TestIn {
        input: i32
    }

    #[derive(Serialize, Deserialize, PartialEq, Eq)]
    struct TestOut {
        output: i32
    }

    fn test_tool(x: TestIn) -> Result<TestOut, ()> {
        Ok(TestOut { output: x.input + 1 })
    }

    #[test]
    fn can_call_tool_via_json_interface() {
        let tool        = make_tool(test_tool);
        let environment = EmptyEnvironment::new();
        let result      = tool.invoke_json(json![{ "input": 4 }], &environment);

        assert!(result == Ok(json![{ "output": 5 }]));
    }

    #[test]
    fn can_call_tool_via_typed_interface() {
        let tool        = make_pure_tool(|x: i32| { x+1 });
        let environment = EmptyEnvironment::new();

        let typed_tool  = TypedTool::from(Box::new(tool));
        let result      = typed_tool.invoke(4, &environment);

        assert!(result == Ok(5));
    }
}
<|endoftext|>


<commit_before>use io;
use std::fmt;
use std::vec::Vec;
use super::{
	Deserialize, Serialize, Error, VarUint7, VarInt7, VarUint1, CountedList,
	CountedListWriter, VarUint32,
};

/// Type definition in types section. Currently can be only of the function type.
#[derive(Debug, Clone, PartialEq)]
pub enum Type {
	/// Function type.
	Function(FunctionType),
}

impl Deserialize for Type {
	type Error = Error;

	fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, Self::Error> {
		Ok(Type::Function(FunctionType::deserialize(reader)?))
	}
}

impl Serialize for Type {
	type Error = Error;

	fn serialize<W: io::Write>(self, writer: &mut W) -> Result<(), Self::Error> {
		match self {
			Type::Function(fn_type) => fn_type.serialize(writer)
		}
	}
}

/// Value type.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ValueType {
	/// 32-bit signed integer
	I32,
	/// 64-bit signed integer
	I64,
	/// 32-bit float
	F32,
	/// 64-bit float
	F64,
	/// 128-bit SIMD register
	V128,
}

impl Deserialize for ValueType {
	type Error = Error;

	fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, Self::Error> {
		let val = VarInt7::deserialize(reader)?;

		match val.into() {
			-0x01 => Ok(ValueType::I32),
			-0x02 => Ok(ValueType::I64),
			-0x03 => Ok(ValueType::F32),
			-0x04 => Ok(ValueType::F64),
			-0x05 => Ok(ValueType::V128),
			_ => Err(Error::UnknownValueType(val.into())),
		}
	}
}

impl Serialize for ValueType {
	type Error = Error;

	fn serialize<W: io::Write>(self, writer: &mut W) -> Result<(), Self::Error> {
		let val: VarInt7 = match self {
			ValueType::I32 => -0x01,
			ValueType::I64 => -0x02,
			ValueType::F32 => -0x03,
			ValueType::F64 => -0x04,
			ValueType::V128 => -0x05,
		}.into();
		val.serialize(writer)?;
		Ok(())
	}
}

impl fmt::Display for ValueType {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		match *self {
			ValueType::I32 => write!(f, "i32"),
			ValueType::I64 => write!(f, "i64"),
			ValueType::F32 => write!(f, "f32"),
			ValueType::F64 => write!(f, "f64"),
			ValueType::V128 => write!(f, "v128"),
		}
	}
}

/// Block type which is basically `ValueType` + NoResult (to define blocks that have no return type)
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum BlockType {
	/// Value-type specified block type
	Value(ValueType),
	/// No specified block type
	NoResult,
}

impl Deserialize for BlockType {
	type Error = Error;

	fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, Self::Error> {
		let val = VarInt7::deserialize(reader)?;

		match val.into() {
			-0x01 => Ok(BlockType::Value(ValueType::I32)),
			-0x02 => Ok(BlockType::Value(ValueType::I64)),
			-0x03 => Ok(BlockType::Value(ValueType::F32)),
			-0x04 => Ok(BlockType::Value(ValueType::F64)),
			0x7b => Ok(BlockType::Value(ValueType::V128)),
			-0x40 => Ok(BlockType::NoResult),
			_ => Err(Error::UnknownValueType(val.into())),
		}
	}
}

impl Serialize for BlockType {
	type Error = Error;

	fn serialize<W: io::Write>(self, writer: &mut W) -> Result<(), Self::Error> {
		let val: VarInt7 = match self {
			BlockType::NoResult => -0x40i8,
			BlockType::Value(ValueType::I32) => -0x01,
			BlockType::Value(ValueType::I64) => -0x02,
			BlockType::Value(ValueType::F32) => -0x03,
			BlockType::Value(ValueType::F64) => -0x04,
			BlockType::Value(ValueType::V128) => 0x7b,
		}.into();
		val.serialize(writer)?;
		Ok(())
	}
}

/// Function signature type.
#[derive(Debug, Clone, PartialEq)]
pub struct FunctionType {
	form: u8,
	params: Vec<ValueType>,
	return_type: Option<ValueType>,
}

impl Default for FunctionType {
	fn default() -> Self {
		FunctionType {
			form: 0x60,
			params: Vec::new(),
			return_type: None,
		}
	}
}

impl FunctionType {
	/// New function type given the signature in-params(`params`) and return type (`return_type`)
	pub fn new(params: Vec<ValueType>, return_type: Option<ValueType>) -> Self {
		FunctionType {
			params: params,
			return_type: return_type,
			..Default::default()
		}
	}
	/// Function form (currently only valid value is `0x60`)
	pub fn form(&self) -> u8 { self.form }
	/// Parameters in the function signature.
	pub fn params(&self) -> &[ValueType] { &self.params }
	/// Mutable parameters in the function signature.
	pub fn params_mut(&mut self) -> &mut Vec<ValueType> { &mut self.params }
	/// Return type in the function signature, if any.
	pub fn return_type(&self) -> Option<ValueType> { self.return_type }
	/// Mutable type in the function signature, if any.
	pub fn return_type_mut(&mut self) -> &mut Option<ValueType> { &mut self.return_type }
}

impl Deserialize for FunctionType {
	type Error = Error;

	fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, Self::Error> {
		let form: u8 = VarUint7::deserialize(reader)?.into();

		if form != 0x60 {
			return Err(Error::UnknownFunctionForm(form));
		}

		let params: Vec<ValueType> = CountedList::deserialize(reader)?.into_inner();

		let return_types: u32 = VarUint32::deserialize(reader)?.into();

		let return_type = if return_types == 1 {
			Some(ValueType::deserialize(reader)?)
		} else if return_types == 0 {
			None
		} else {
			return Err(Error::Other("Return types length should be 0 or 1"));
		};

		Ok(FunctionType {
			form: form,
			params: params,
			return_type: return_type,
		})
	}
}

impl Serialize for FunctionType {
	type Error = Error;

	fn serialize<W: io::Write>(self, writer: &mut W) -> Result<(), Self::Error> {
		VarUint7::from(self.form).serialize(writer)?;

		let data = self.params;
		let counted_list = CountedListWriter::<ValueType, _>(
			data.len(),
			data.into_iter().map(Into::into),
		);
		counted_list.serialize(writer)?;

		if let Some(return_type) = self.return_type {
			VarUint1::from(true).serialize(writer)?;
			return_type.serialize(writer)?;
		} else {
			VarUint1::from(false).serialize(writer)?;
		}

		Ok(())
	}
}

/// Table element type.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum TableElementType {
	/// A reference to a function with any signature.
	AnyFunc,
}

impl Deserialize for TableElementType {
	type Error = Error;

	fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, Self::Error> {
		let val = VarInt7::deserialize(reader)?;

		match val.into() {
			-0x10 => Ok(TableElementType::AnyFunc),
			_ => Err(Error::UnknownTableElementType(val.into())),
		}
	}
}

impl Serialize for TableElementType {
	type Error = Error;

	fn serialize<W: io::Write>(self, writer: &mut W) -> Result<(), Self::Error> {
		let val: VarInt7 = match self {
			TableElementType::AnyFunc => -0x10,
		}.into();
		val.serialize(writer)?;
		Ok(())
	}
}
<commit_msg>Enable using `Type` in a `HashMap`<commit_after>use io;
use std::fmt;
use std::vec::Vec;
use super::{
	Deserialize, Serialize, Error, VarUint7, VarInt7, VarUint1, CountedList,
	CountedListWriter, VarUint32,
};

/// Type definition in types section. Currently can be only of the function type.
#[derive(Debug, Clone, PartialEq, Hash, Eq)]
pub enum Type {
	/// Function type.
	Function(FunctionType),
}

impl Deserialize for Type {
	type Error = Error;

	fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, Self::Error> {
		Ok(Type::Function(FunctionType::deserialize(reader)?))
	}
}

impl Serialize for Type {
	type Error = Error;

	fn serialize<W: io::Write>(self, writer: &mut W) -> Result<(), Self::Error> {
		match self {
			Type::Function(fn_type) => fn_type.serialize(writer)
		}
	}
}

/// Value type.
#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)]
pub enum ValueType {
	/// 32-bit signed integer
	I32,
	/// 64-bit signed integer
	I64,
	/// 32-bit float
	F32,
	/// 64-bit float
	F64,
	/// 128-bit SIMD register
	V128,
}

impl Deserialize for ValueType {
	type Error = Error;

	fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, Self::Error> {
		let val = VarInt7::deserialize(reader)?;

		match val.into() {
			-0x01 => Ok(ValueType::I32),
			-0x02 => Ok(ValueType::I64),
			-0x03 => Ok(ValueType::F32),
			-0x04 => Ok(ValueType::F64),
			-0x05 => Ok(ValueType::V128),
			_ => Err(Error::UnknownValueType(val.into())),
		}
	}
}

impl Serialize for ValueType {
	type Error = Error;

	fn serialize<W: io::Write>(self, writer: &mut W) -> Result<(), Self::Error> {
		let val: VarInt7 = match self {
			ValueType::I32 => -0x01,
			ValueType::I64 => -0x02,
			ValueType::F32 => -0x03,
			ValueType::F64 => -0x04,
			ValueType::V128 => -0x05,
		}.into();
		val.serialize(writer)?;
		Ok(())
	}
}

impl fmt::Display for ValueType {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		match *self {
			ValueType::I32 => write!(f, "i32"),
			ValueType::I64 => write!(f, "i64"),
			ValueType::F32 => write!(f, "f32"),
			ValueType::F64 => write!(f, "f64"),
			ValueType::V128 => write!(f, "v128"),
		}
	}
}

/// Block type which is basically `ValueType` + NoResult (to define blocks that have no return type)
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum BlockType {
	/// Value-type specified block type
	Value(ValueType),
	/// No specified block type
	NoResult,
}

impl Deserialize for BlockType {
	type Error = Error;

	fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, Self::Error> {
		let val = VarInt7::deserialize(reader)?;

		match val.into() {
			-0x01 => Ok(BlockType::Value(ValueType::I32)),
			-0x02 => Ok(BlockType::Value(ValueType::I64)),
			-0x03 => Ok(BlockType::Value(ValueType::F32)),
			-0x04 => Ok(BlockType::Value(ValueType::F64)),
			0x7b => Ok(BlockType::Value(ValueType::V128)),
			-0x40 => Ok(BlockType::NoResult),
			_ => Err(Error::UnknownValueType(val.into())),
		}
	}
}

impl Serialize for BlockType {
	type Error = Error;

	fn serialize<W: io::Write>(self, writer: &mut W) -> Result<(), Self::Error> {
		let val: VarInt7 = match self {
			BlockType::NoResult => -0x40i8,
			BlockType::Value(ValueType::I32) => -0x01,
			BlockType::Value(ValueType::I64) => -0x02,
			BlockType::Value(ValueType::F32) => -0x03,
			BlockType::Value(ValueType::F64) => -0x04,
			BlockType::Value(ValueType::V128) => 0x7b,
		}.into();
		val.serialize(writer)?;
		Ok(())
	}
}

/// Function signature type.
#[derive(Debug, Clone, PartialEq, Hash, Eq)]
pub struct FunctionType {
	form: u8,
	params: Vec<ValueType>,
	return_type: Option<ValueType>,
}

impl Default for FunctionType {
	fn default() -> Self {
		FunctionType {
			form: 0x60,
			params: Vec::new(),
			return_type: None,
		}
	}
}

impl FunctionType {
	/// New function type given the signature in-params(`params`) and return type (`return_type`)
	pub fn new(params: Vec<ValueType>, return_type: Option<ValueType>) -> Self {
		FunctionType {
			params: params,
			return_type: return_type,
			..Default::default()
		}
	}
	/// Function form (currently only valid value is `0x60`)
	pub fn form(&self) -> u8 { self.form }
	/// Parameters in the function signature.
	pub fn params(&self) -> &[ValueType] { &self.params }
	/// Mutable parameters in the function signature.
	pub fn params_mut(&mut self) -> &mut Vec<ValueType> { &mut self.params }
	/// Return type in the function signature, if any.
	pub fn return_type(&self) -> Option<ValueType> { self.return_type }
	/// Mutable type in the function signature, if any.
	pub fn return_type_mut(&mut self) -> &mut Option<ValueType> { &mut self.return_type }
}

impl Deserialize for FunctionType {
	type Error = Error;

	fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, Self::Error> {
		let form: u8 = VarUint7::deserialize(reader)?.into();

		if form != 0x60 {
			return Err(Error::UnknownFunctionForm(form));
		}

		let params: Vec<ValueType> = CountedList::deserialize(reader)?.into_inner();

		let return_types: u32 = VarUint32::deserialize(reader)?.into();

		let return_type = if return_types == 1 {
			Some(ValueType::deserialize(reader)?)
		} else if return_types == 0 {
			None
		} else {
			return Err(Error::Other("Return types length should be 0 or 1"));
		};

		Ok(FunctionType {
			form: form,
			params: params,
			return_type: return_type,
		})
	}
}

impl Serialize for FunctionType {
	type Error = Error;

	fn serialize<W: io::Write>(self, writer: &mut W) -> Result<(), Self::Error> {
		VarUint7::from(self.form).serialize(writer)?;

		let data = self.params;
		let counted_list = CountedListWriter::<ValueType, _>(
			data.len(),
			data.into_iter().map(Into::into),
		);
		counted_list.serialize(writer)?;

		if let Some(return_type) = self.return_type {
			VarUint1::from(true).serialize(writer)?;
			return_type.serialize(writer)?;
		} else {
			VarUint1::from(false).serialize(writer)?;
		}

		Ok(())
	}
}

/// Table element type.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum TableElementType {
	/// A reference to a function with any signature.
	AnyFunc,
}

impl Deserialize for TableElementType {
	type Error = Error;

	fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, Self::Error> {
		let val = VarInt7::deserialize(reader)?;

		match val.into() {
			-0x10 => Ok(TableElementType::AnyFunc),
			_ => Err(Error::UnknownTableElementType(val.into())),
		}
	}
}

impl Serialize for TableElementType {
	type Error = Error;

	fn serialize<W: io::Write>(self, writer: &mut W) -> Result<(), Self::Error> {
		let val: VarInt7 = match self {
			TableElementType::AnyFunc => -0x10,
		}.into();
		val.serialize(writer)?;
		Ok(())
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[cfg(gl)] pub use self::gl::Device;
#[cfg(gl)] pub use dev = self::gl;
// #[cfg(d3d11)] ... // TODO

use std::comm;
use std::comm::DuplexStream;
use std::kinds::marker;

use GraphicsContext;

pub mod shade;
#[cfg(gl)] mod gl;

pub type Color = [f32, ..4];
pub type VertexCount = u16;

pub enum Request {
    // Requests that require a reply:
    CallNewBuffer(Vec<f32>),
    CallNewArrayBuffer,
    CallNewShader(shade::Stage, Vec<u8>),
    CallNewProgram(Vec<dev::Shader>),
    // Requests that don't expect a reply:
    CastClear(Color),
    CastBindProgram(dev::Program),
    CastBindArrayBuffer(dev::ArrayBuffer),
    CastBindAttribute(u8, dev::Buffer, VertexCount, u32, u32),
    CastBindFrameBuffer(dev::FrameBuffer),
    CastDraw(VertexCount, VertexCount),
    CastSwapBuffers,
}

pub enum Reply {
    ReplyNewBuffer(dev::Buffer),
    ReplyNewArrayBuffer(dev::ArrayBuffer),
    ReplyNewShader(dev::Shader),
    ReplyNewProgram(dev::Program),
}

pub struct Client {
    stream: DuplexStream<Request, Reply>,
}

impl Client {
    pub fn clear(&self, color: Color) {
        self.stream.send(CastClear(color));
    }

    pub fn bind_program(&self, prog: dev::Program) {
        self.stream.send(CastBindProgram(prog));
    }

    pub fn bind_array_buffer(&self, abuf: dev::ArrayBuffer) {
        self.stream.send(CastBindArrayBuffer(abuf));
    }

    pub fn bind_attribute(&self, index: u8, buf: dev::Buffer, count: VertexCount, offset: u32, stride: u32) {
        self.stream.send(CastBindAttribute(index, buf, count, offset, stride));
    }

    pub fn bind_frame_buffer(&self, fbo: dev::FrameBuffer) {
        self.stream.send(CastBindFrameBuffer(fbo));
    }

    pub fn draw(&self, offset: VertexCount, count: VertexCount) {
        self.stream.send(CastDraw(offset, count));
    }

    pub fn end_frame(&self) {
        self.stream.send(CastSwapBuffers);
    }

    pub fn new_shader(&self, stage: shade::Stage, code: Vec<u8>) -> dev::Shader {
        self.stream.send(CallNewShader(stage, code));
        match self.stream.recv() {
            ReplyNewShader(name) => name,
            _ => fail!("unexpected device reply")
        }
    }

    pub fn new_program(&self, shaders: Vec<dev::Shader>) -> dev::Program {
        self.stream.send(CallNewProgram(shaders));
        match self.stream.recv() {
            ReplyNewProgram(name) => name,
            _ => fail!("unexpected device reply")
        }
    }

    pub fn new_buffer(&self, data: Vec<f32>) -> dev::Buffer {
        self.stream.send(CallNewBuffer(data));
        match self.stream.recv() {
            ReplyNewBuffer(name) => name,
            _ => fail!("unexpected device reply")
        }
    }

    pub fn new_array_buffer(&self) -> dev::ArrayBuffer {
        self.stream.send(CallNewArrayBuffer);
        match self.stream.recv() {
            ReplyNewArrayBuffer(name) => name,
            _ => fail!("unexpected device reply")
        }
    }
}

pub struct Server<P> {
    no_send: marker::NoSend,
    no_share: marker::NoShare,
    stream: DuplexStream<Reply, Request>,
    graphics_context: P,
    device: Device,
}

impl<Api, P: GraphicsContext<Api>> Server<P> {
    pub fn make_current(&self) {
        self.graphics_context.make_current();
    }

    /// Update the platform. The client must manually update this on the main
    /// thread.
    pub fn update(&mut self) -> bool {
        // Get updates from the renderer and pass on results
        loop {
            match self.stream.try_recv() {
                Ok(CastClear(color)) => {
                    self.device.clear(color.as_slice());
                },
                Ok(CastBindProgram(prog)) => {
                    self.device.bind_program(prog);
                },
                Ok(CastBindArrayBuffer(abuf)) => {
                    self.device.bind_array_buffer(abuf);
                },
                Ok(CastBindAttribute(index, buf, count, offset, stride)) => {
                    self.device.bind_attribute(index, count as u32, offset, stride);
                },
                Ok(CastBindFrameBuffer(fbo)) => {
                    self.device.bind_frame_buffer(fbo);
                },
                Ok(CastDraw(offset, count)) => {
                    self.device.draw(offset as u32, count as u32);
                },
                Ok(CastSwapBuffers) => {
                    break;
                },
                Ok(CallNewBuffer(data)) => {
                    let name = self.device.create_buffer(data.as_slice());
                    self.stream.send(ReplyNewBuffer(name));
                },
                Ok(CallNewArrayBuffer) => {
                    let name = self.device.create_array_buffer();
                    self.stream.send(ReplyNewArrayBuffer(name));
                },
                Ok(CallNewShader(stage, code)) => {
                    let name = self.device.create_shader(stage, code.as_slice());
                    self.stream.send(ReplyNewShader(name));
                },
                Ok(CallNewProgram(code)) => {
                    let name = self.device.create_program(code.as_slice());
                    self.stream.send(ReplyNewProgram(name));
                },
                Err(comm::Empty) => break,
                Err(comm::Disconnected) => return false,
            }
        }
        self.graphics_context.swap_buffers();
        true
    }
}

#[deriving(Show)]
pub enum InitError {}

pub fn init<Api, P: GraphicsContext<Api>>(graphics_context: P, options: super::Options)
        -> Result<(Client, Server<P>), InitError> {
    let (client_stream, server_stream) = comm::duplex();

    let client = Client {
        stream: client_stream,
    };
    let dev = Device::new(options);
    let server = Server {
        no_send: marker::NoSend,
        no_share: marker::NoShare,
        stream: server_stream,
        graphics_context: graphics_context,
        device: dev,
    };

    Ok((client, server))
}
<commit_msg>remove NoSend markers to allow the server to be sent<commit_after>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[cfg(gl)] pub use self::gl::Device;
#[cfg(gl)] pub use dev = self::gl;
// #[cfg(d3d11)] ... // TODO

use std::comm;
use std::comm::DuplexStream;
use std::kinds::marker;

use GraphicsContext;

pub mod shade;
#[cfg(gl)] mod gl;

pub type Color = [f32, ..4];
pub type VertexCount = u16;

pub enum Request {
    // Requests that require a reply:
    CallNewBuffer(Vec<f32>),
    CallNewArrayBuffer,
    CallNewShader(shade::Stage, Vec<u8>),
    CallNewProgram(Vec<dev::Shader>),
    // Requests that don't expect a reply:
    CastClear(Color),
    CastBindProgram(dev::Program),
    CastBindArrayBuffer(dev::ArrayBuffer),
    CastBindAttribute(u8, dev::Buffer, VertexCount, u32, u32),
    CastBindFrameBuffer(dev::FrameBuffer),
    CastDraw(VertexCount, VertexCount),
    CastSwapBuffers,
}

pub enum Reply {
    ReplyNewBuffer(dev::Buffer),
    ReplyNewArrayBuffer(dev::ArrayBuffer),
    ReplyNewShader(dev::Shader),
    ReplyNewProgram(dev::Program),
}

pub struct Client {
    stream: DuplexStream<Request, Reply>,
}

impl Client {
    pub fn clear(&self, color: Color) {
        self.stream.send(CastClear(color));
    }

    pub fn bind_program(&self, prog: dev::Program) {
        self.stream.send(CastBindProgram(prog));
    }

    pub fn bind_array_buffer(&self, abuf: dev::ArrayBuffer) {
        self.stream.send(CastBindArrayBuffer(abuf));
    }

    pub fn bind_attribute(&self, index: u8, buf: dev::Buffer, count: VertexCount, offset: u32, stride: u32) {
        self.stream.send(CastBindAttribute(index, buf, count, offset, stride));
    }

    pub fn bind_frame_buffer(&self, fbo: dev::FrameBuffer) {
        self.stream.send(CastBindFrameBuffer(fbo));
    }

    pub fn draw(&self, offset: VertexCount, count: VertexCount) {
        self.stream.send(CastDraw(offset, count));
    }

    pub fn end_frame(&self) {
        self.stream.send(CastSwapBuffers);
    }

    pub fn new_shader(&self, stage: shade::Stage, code: Vec<u8>) -> dev::Shader {
        self.stream.send(CallNewShader(stage, code));
        match self.stream.recv() {
            ReplyNewShader(name) => name,
            _ => fail!("unexpected device reply")
        }
    }

    pub fn new_program(&self, shaders: Vec<dev::Shader>) -> dev::Program {
        self.stream.send(CallNewProgram(shaders));
        match self.stream.recv() {
            ReplyNewProgram(name) => name,
            _ => fail!("unexpected device reply")
        }
    }

    pub fn new_buffer(&self, data: Vec<f32>) -> dev::Buffer {
        self.stream.send(CallNewBuffer(data));
        match self.stream.recv() {
            ReplyNewBuffer(name) => name,
            _ => fail!("unexpected device reply")
        }
    }

    pub fn new_array_buffer(&self) -> dev::ArrayBuffer {
        self.stream.send(CallNewArrayBuffer);
        match self.stream.recv() {
            ReplyNewArrayBuffer(name) => name,
            _ => fail!("unexpected device reply")
        }
    }
}

pub struct Server<P> {
    no_share: marker::NoShare,
    stream: DuplexStream<Reply, Request>,
    graphics_context: P,
    device: Device,
}

impl<Api, P: GraphicsContext<Api>> Server<P> {
    pub fn make_current(&self) {
        self.graphics_context.make_current();
    }

    /// Update the platform. The client must manually update this on the main
    /// thread.
    pub fn update(&mut self) -> bool {
        // Get updates from the renderer and pass on results
        loop {
            match self.stream.try_recv() {
                Ok(CastClear(color)) => {
                    self.device.clear(color.as_slice());
                },
                Ok(CastBindProgram(prog)) => {
                    self.device.bind_program(prog);
                },
                Ok(CastBindArrayBuffer(abuf)) => {
                    self.device.bind_array_buffer(abuf);
                },
                Ok(CastBindAttribute(index, buf, count, offset, stride)) => {
                    self.device.bind_attribute(index, count as u32, offset, stride);
                },
                Ok(CastBindFrameBuffer(fbo)) => {
                    self.device.bind_frame_buffer(fbo);
                },
                Ok(CastDraw(offset, count)) => {
                    self.device.draw(offset as u32, count as u32);
                },
                Ok(CastSwapBuffers) => {
                    break;
                },
                Ok(CallNewBuffer(data)) => {
                    let name = self.device.create_buffer(data.as_slice());
                    self.stream.send(ReplyNewBuffer(name));
                },
                Ok(CallNewArrayBuffer) => {
                    let name = self.device.create_array_buffer();
                    self.stream.send(ReplyNewArrayBuffer(name));
                },
                Ok(CallNewShader(stage, code)) => {
                    let name = self.device.create_shader(stage, code.as_slice());
                    self.stream.send(ReplyNewShader(name));
                },
                Ok(CallNewProgram(code)) => {
                    let name = self.device.create_program(code.as_slice());
                    self.stream.send(ReplyNewProgram(name));
                },
                Err(comm::Empty) => break,
                Err(comm::Disconnected) => return false,
            }
        }
        self.graphics_context.swap_buffers();
        true
    }
}

#[deriving(Show)]
pub enum InitError {}

pub fn init<Api, P: GraphicsContext<Api>>(graphics_context: P, options: super::Options)
        -> Result<(Client, Server<P>), InitError> {
    let (client_stream, server_stream) = comm::duplex();

    let client = Client {
        stream: client_stream,
    };
    let dev = Device::new(options);
    let server = Server {
        no_share: marker::NoShare,
        stream: server_stream,
        graphics_context: graphics_context,
        device: dev,
    };

    Ok((client, server))
}
<|endoftext|>


<commit_before>// Zinc, the bare metal stack for rust.
// Copyright 2014 Lionel Flandrin <lionel@svkt.org>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! SPI configuration

/// Enables SPI interface on any of the 4 SSI (Synchronous Serial Interface) modules in TM4C microcontrollers

use core::intrinsics::abort;
use hal::tiva_c::sysctl;
use util::support::get_reg_ref;

#[path="../../util/ioreg.rs"]
#[macro_use] mod ioreg;
#[path="../../util/wait_for.rs"]
#[macro_use] mod wait_for;

/// There are 4 SSI instances an SPI interface can use
/// See the TM4C123GH6PM datasheet page 954 for detailed signal to pin mappings
#[allow(missing_docs)]
#[derive(Clone, Copy)]
pub enum SpiId {
  /// SPI over SSI0, uses pins PA2:5
  Spi0,

  /// SPI over SSI1, uses pins PF0:3 or PD0:3
  Spi1,

  /// SPI over SSI2, uses pins PB4:7
  Spi2,

  /// SPI over SSI3, uses pins PD0:3
  Spi3,
}

/**
SPI configuration object

Configuration object used when instantiating an SPI instance

Note: The SPI GPIO pins must be set up correctly before using SPI. For example, for a master-only TX and CK config on SSI0,
the platformtree section might look like the following (taken from a Tiva C configuration):

```
gpio {
  a {
    spi_ck@2 {
      direction = "out";
      function  = 2;
    }

    spi_tx@5 {
      direction = "out";
      function  = 2;
    }
  }
}
```
*/
pub struct SpiConf {
  /// Which SPI peripheral to use. The Tiva C has 4 SSI interfaces that can be used for SPI
  pub peripheral: SpiId,

  /// Bus frequency in Hz. Peripheral clock scaling is calculated from this value to a possibly non-exact match
  pub frequency: u32,
}

/**
Structure describing a single SPI interface

# Examples

Create an SPI interface on SSI0 with a run frequency of 4MHz

```
#![crate_type = "staticlib"]
#![feature(plugin, start, core_intrinsics)]
#![no_std]
#![plugin(macro_platformtree)]

extern crate zinc;

use zinc::hal::spi::Spi;
use zinc::hal::tiva_c::spi;

platformtree!(
  tiva_c@mcu {
    // Tiva C ends up with an 80MHz clock from 16MHz external xtal and x5 PLL
    clock {
      source = "MOSC";
      xtal   = "X16_0MHz";
      pll    = true;
      div    = 5;
    }

    gpio {
      a {
        spi_ck@2 {
          direction = "out";
          function  = 2;
        }

        spi_tx@5 {
          direction = "out";
          function  = 2;
        }
      }
    }
  }

  os {
    single_task {
      loop = "run";
    }
  }
);

fn run() {
  let spi = spi::Spi::new(spi::SpiConf {
    peripheral: spi::SpiId::Spi0,
    frequency: 4_000_000
  });

  loop {
    spi.write('a' as u8);
  }
}
```
*/
#[derive(Clone, Copy)]
pub struct Spi {
  /// SSI registers
  regs: &'static reg::Ssi,
}

impl Spi {
  /// Create and setup an SPI interface.
  pub fn new(config: SpiConf) -> Spi {

    let (periph, regs) = match config.peripheral {
      SpiId::Spi0 => (sysctl::periph::ssi::SSI_0, reg::SSI_0),
      SpiId::Spi1 => (sysctl::periph::ssi::SSI_1, reg::SSI_1),
      SpiId::Spi2 => (sysctl::periph::ssi::SSI_2, reg::SSI_2),
      SpiId::Spi3 => (sysctl::periph::ssi::SSI_3, reg::SSI_3),
    };

    let spi = Spi { regs: get_reg_ref(regs) };

    // Make sure peripheral clock gating is enabled
    periph.ensure_enabled();

    spi.configure(config);

    spi
  }

  /// Configure the SSI into SPI mode. Currently hard coded at 3.2MHz for 16MHz PIOSC
  fn configure(&self, config: SpiConf) {
    // Disable peripheral so we can configure it
    self.regs.ssicr1.set_sse(false);

    self.regs.ssicr1
      .set_sse(false)
      .set_ms(false)
      .set_lbm(false);

    // Set clock rate
    self.set_frequency(config.frequency);

    self.regs.ssicr0
      .set_sph(false)
      .set_spo(false)
      .set_frf(0)     // Put SSI into SPI mode
      .set_dss(0x7);  // 8 bit frames

    // Turn on peripheral now that we've configured it
    self.regs.ssicr1.set_sse(true);
  }

  /// Set SPI frequency
  ///
  /// This function computes the divisor and exponent (for lack of a better name). These are stored in `ssicpsr.cpsdvsr` and `ssicr0.scr` respectively.
  /// The clock rate formula (taken from the datasheet is) `ClockRate = SysClk / (CPSDVSR * (1 + SCR))` where SysClk is the system clock in Hz.
  fn set_frequency(&self, freq: u32) {
    let sysclk = sysctl::clock::sysclk_get() as u32;

    let mut divisor: u32 = 2;

    while divisor <= 254 {
      let prescale_hz = sysclk / divisor;

      // Calculate exponent
      let scr = ((prescale_hz as f32 / freq as f32) + 0.5f32) as u32;

      // Check we can support the divider
      if scr < 256 {
        self.regs.ssicpsr.set_cpsdvsr(divisor as u16);

        self.regs.ssicr0.set_scr((scr - 1) as u16);

        return
      }

      divisor += 2;
    }

    // TODO(jamwaffles): Return a Result from here as well as the calling configure method
    unsafe { abort() };
  }

  /// Wait for SSI TX FIFO to be ready
  /// This checks the busy flag (0 = not busy) and the "transmit FIFO not full" flag (1 = not full)
  fn writeable(&self) -> bool {
    !self.regs.ssisr.bsy() && self.regs.ssisr.tnf()
  }

  /// Wait for SSI data buffer to be readable
  fn readable(&self) -> bool {
    !self.regs.ssisr.bsy()
  }
}

impl ::hal::spi::Spi for Spi {
  fn write(&self, value: u8) {
    wait_for!(self.writeable());

    self.regs.ssidr.set_data(value as u16);
  }

  fn read(&self) -> u8 {
    wait_for!(self.readable());

    self.regs.ssidr.data() as u8
  }
}

#[allow(missing_docs)]
pub mod reg {
  //! SSI registers definition
  use volatile_cell::VolatileCell;
  use core::ops::Drop;

  ioregs!(Ssi = {
    /// SSI control register 0
    0x000 => reg16 ssicr0 {
      0..3 => dss: rw,    //= Data size select, 0x7 = 8 bits, see datasheet page 970 for other values
      4..5 => frf: rw,    //= Frame format, 0 = SPI 1 = TI SSF 2 Microwire
      6    => spo: rw,    //= SSI polarity, 0 = steady staate low on SSInCLK 1 = steady state high
      7    => sph: rw,    //= SSI clock phase, 1 = first edge 0 = second edge
      15..8 => scr: rw,   //= SSI clock rate, calculated from formula `SysClk / (CPSDVSR * (1 + SCR))`
    },

    /// SSI control register 1
    0x004 => reg16 ssicr1 {
      0 => lbm: rw,       //= Loopback enable, 1 = enabled 0 = normal operation
      1 => sse: rw,       //= Serial port enable 1 = enabled 0 = disabled. Must be cleared before SSI can be configured
      2 => ms: rw,        //= Master/slave select 0 = master 1 = slave
      4 => eot: rw,       //= End of transmission. Not sure what this is
    },

    /// SSI send/receive register
    /// When the SSIDR register is read, the entry in the receive FIFO that is pointed to by the current FIFO read pointer is accessed. When a data value is removed by the SSI receive logic from the incoming data frame, it is placed into the entry in the receive FIFO pointed to by the current FIFO write pointer.
    /// When the SSIDR register is written to, the entry in the transmit FIFO that is pointed to by the write pointer is written to. Data values are removed from the transmit FIFO one value at a time by the transmit logic. Each data value is loaded into the transmit serial shifter, then serially shifted out onto the SSInTx pin at the programmed bit rate.
    /// See page 973 in datasheet for more details
    0x008 => reg16 ssidr {
      0..15 => data: rw   //= Receive/transmit data
    },

    /// SSI status register
    0x00c => reg16 ssisr {
      0 => tfe: ro,       //= TX FIFO empty 1 = empty 0 = not empty
      1 => tnf: ro,       //= TX FIFO full 1 = not full 0 = full
      2 => rne: ro,       //= RX FIFO 1 = not empty 0 = empty
      3 => rff: ro,       //= RX FIFO full 0 = not full 1 = full
      4 => bsy: ro,       //= SSI busy 0 = idle, can send data 1 = transmitting, can't send data yet
    },

    /// SSI clock prescale register
    0x010 => reg16 ssicpsr {
      0..7 => cpsdvsr: rw //= SSI clock prescale divisor, 2 – 254 inclusive
    },

    /// SSI clock source configuration
    0xfc8 => reg16 ssicc {
      0..3 => cs: rw,     //= SSI baud clock source, 0x0 = system clock, 0x5 = PIOSC
    },
  });

  pub const SSI_0: *const Ssi = 0x40008000 as *const Ssi;
  pub const SSI_1: *const Ssi = 0x40009000 as *const Ssi;
  pub const SSI_2: *const Ssi = 0x4000A000 as *const Ssi;
  pub const SSI_3: *const Ssi = 0x4000B000 as *const Ssi;
}<commit_msg>Do integer division because floating point crashes<commit_after>// Zinc, the bare metal stack for rust.
// Copyright 2014 Lionel Flandrin <lionel@svkt.org>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! SPI configuration

/// Enables SPI interface on any of the 4 SSI (Synchronous Serial Interface) modules in TM4C microcontrollers

use core::intrinsics::abort;
use hal::tiva_c::sysctl;
use util::support::get_reg_ref;

#[path="../../util/ioreg.rs"]
#[macro_use] mod ioreg;
#[path="../../util/wait_for.rs"]
#[macro_use] mod wait_for;

/// There are 4 SSI instances an SPI interface can use
/// See the TM4C123GH6PM datasheet page 954 for detailed signal to pin mappings
#[allow(missing_docs)]
#[derive(Clone, Copy)]
pub enum SpiId {
  /// SPI over SSI0, uses pins PA2:5
  Spi0,

  /// SPI over SSI1, uses pins PF0:3 or PD0:3
  Spi1,

  /// SPI over SSI2, uses pins PB4:7
  Spi2,

  /// SPI over SSI3, uses pins PD0:3
  Spi3,
}

/**
SPI configuration object

Configuration object used when instantiating an SPI instance

Note: The SPI GPIO pins must be set up correctly before using SPI. For example, for a master-only TX and CK config on SSI0,
the platformtree section might look like the following (taken from a Tiva C configuration):

```
gpio {
  a {
    spi_ck@2 {
      direction = "out";
      function  = 2;
    }

    spi_tx@5 {
      direction = "out";
      function  = 2;
    }
  }
}
```
*/
pub struct SpiConf {
  /// Which SPI peripheral to use. The Tiva C has 4 SSI interfaces that can be used for SPI
  pub peripheral: SpiId,

  /// Bus frequency in Hz. Peripheral clock scaling is calculated from this value to a possibly non-exact match
  pub frequency: u32,
}

/**
Structure describing a single SPI interface

# Examples

Create an SPI interface on SSI0 with a run frequency of 4MHz

```
#![crate_type = "staticlib"]
#![feature(plugin, start, core_intrinsics)]
#![no_std]
#![plugin(macro_platformtree)]

extern crate zinc;

use zinc::hal::spi::Spi;
use zinc::hal::tiva_c::spi;

platformtree!(
  tiva_c@mcu {
    // Tiva C ends up with an 80MHz clock from 16MHz external xtal and x5 PLL
    clock {
      source = "MOSC";
      xtal   = "X16_0MHz";
      pll    = true;
      div    = 5;
    }

    gpio {
      a {
        spi_ck@2 {
          direction = "out";
          function  = 2;
        }

        spi_tx@5 {
          direction = "out";
          function  = 2;
        }
      }
    }
  }

  os {
    single_task {
      loop = "run";
    }
  }
);

fn run() {
  let spi = spi::Spi::new(spi::SpiConf {
    peripheral: spi::SpiId::Spi0,
    frequency: 4_000_000
  });

  loop {
    spi.write('a' as u8);
  }
}
```
*/
#[derive(Clone, Copy)]
pub struct Spi {
  /// SSI registers
  regs: &'static reg::Ssi,
}

impl Spi {
  /// Create and setup an SPI interface.
  pub fn new(config: SpiConf) -> Spi {

    let (periph, regs) = match config.peripheral {
      SpiId::Spi0 => (sysctl::periph::ssi::SSI_0, reg::SSI_0),
      SpiId::Spi1 => (sysctl::periph::ssi::SSI_1, reg::SSI_1),
      SpiId::Spi2 => (sysctl::periph::ssi::SSI_2, reg::SSI_2),
      SpiId::Spi3 => (sysctl::periph::ssi::SSI_3, reg::SSI_3),
    };

    let spi = Spi { regs: get_reg_ref(regs) };

    // Make sure peripheral clock gating is enabled
    periph.ensure_enabled();

    spi.configure(config);

    spi
  }

  /// Configure the SSI into SPI mode. Currently hard coded at 3.2MHz for 16MHz PIOSC
  fn configure(&self, config: SpiConf) {
    // Disable peripheral so we can configure it
    self.regs.ssicr1.set_sse(false);

    self.regs.ssicr1
      .set_sse(false)
      .set_ms(false)
      .set_lbm(false);

    // Set clock rate
    self.set_frequency(config.frequency);

    self.regs.ssicr0
      .set_sph(false)
      .set_spo(false)
      .set_frf(0)     // Put SSI into SPI mode
      .set_dss(0x7);  // 8 bit frames

    // Turn on peripheral now that we've configured it
    self.regs.ssicr1.set_sse(true);
  }

  /// Set SPI frequency
  ///
  /// This function computes the divisor and exponent (for lack of a better name). These are stored in `ssicpsr.cpsdvsr` and `ssicr0.scr` respectively.
  /// The clock rate formula (taken from the datasheet is) `ClockRate = SysClk / (CPSDVSR * (1 + SCR))` where SysClk is the system clock in Hz.
  fn set_frequency(&self, freq: u32) {
    let sysclk = sysctl::clock::sysclk_get() as u32;

    let mut divisor: u32 = 2;

    while divisor <= 254 {
      let prescale_hz = sysclk / divisor;

      // Calculate exponent
      // TODO(jamwaffles) The below line crashes my Tiva, presumably because floating point is broken?
      // let scr = ((prescale_hz as f32 / freq as f32) + 0.5f32) as u32;

      let scr = prescale_hz / freq;

      // Check we can support the divider
      if scr < 256 {
        self.regs.ssicpsr.set_cpsdvsr(divisor as u16);

        self.regs.ssicr0.set_scr((scr - 1) as u16);

        return
      }

      divisor += 2;
    }

    // TODO(jamwaffles): Return a Result from here as well as the calling configure method
    unsafe { abort() };
  }

  /// Wait for SSI TX FIFO to be ready
  /// This checks the busy flag (0 = not busy) and the "transmit FIFO not full" flag (1 = not full)
  fn writeable(&self) -> bool {
    !self.regs.ssisr.bsy() && self.regs.ssisr.tnf()
  }

  /// Wait for SSI data buffer to be readable
  fn readable(&self) -> bool {
    !self.regs.ssisr.bsy()
  }
}

impl ::hal::spi::Spi for Spi {
  fn write(&self, value: u8) {
    wait_for!(self.writeable());

    self.regs.ssidr.set_data(value as u16);
  }

  fn read(&self) -> u8 {
    wait_for!(self.readable());

    self.regs.ssidr.data() as u8
  }
}

#[allow(missing_docs)]
pub mod reg {
  //! SSI registers definition
  use volatile_cell::VolatileCell;
  use core::ops::Drop;

  ioregs!(Ssi = {
    /// SSI control register 0
    0x000 => reg16 ssicr0 {
      0..3 => dss: rw,    //= Data size select, 0x7 = 8 bits, see datasheet page 970 for other values
      4..5 => frf: rw,    //= Frame format, 0 = SPI 1 = TI SSF 2 Microwire
      6    => spo: rw,    //= SSI polarity, 0 = steady staate low on SSInCLK 1 = steady state high
      7    => sph: rw,    //= SSI clock phase, 1 = first edge 0 = second edge
      15..8 => scr: rw,   //= SSI clock rate, calculated from formula `SysClk / (CPSDVSR * (1 + SCR))`
    },

    /// SSI control register 1
    0x004 => reg16 ssicr1 {
      0 => lbm: rw,       //= Loopback enable, 1 = enabled 0 = normal operation
      1 => sse: rw,       //= Serial port enable 1 = enabled 0 = disabled. Must be cleared before SSI can be configured
      2 => ms: rw,        //= Master/slave select 0 = master 1 = slave
      4 => eot: rw,       //= End of transmission. Not sure what this is
    },

    /// SSI send/receive register
    /// When the SSIDR register is read, the entry in the receive FIFO that is pointed to by the current FIFO read pointer is accessed. When a data value is removed by the SSI receive logic from the incoming data frame, it is placed into the entry in the receive FIFO pointed to by the current FIFO write pointer.
    /// When the SSIDR register is written to, the entry in the transmit FIFO that is pointed to by the write pointer is written to. Data values are removed from the transmit FIFO one value at a time by the transmit logic. Each data value is loaded into the transmit serial shifter, then serially shifted out onto the SSInTx pin at the programmed bit rate.
    /// See page 973 in datasheet for more details
    0x008 => reg16 ssidr {
      0..15 => data: rw   //= Receive/transmit data
    },

    /// SSI status register
    0x00c => reg16 ssisr {
      0 => tfe: ro,       //= TX FIFO empty 1 = empty 0 = not empty
      1 => tnf: ro,       //= TX FIFO full 1 = not full 0 = full
      2 => rne: ro,       //= RX FIFO 1 = not empty 0 = empty
      3 => rff: ro,       //= RX FIFO full 0 = not full 1 = full
      4 => bsy: ro,       //= SSI busy 0 = idle, can send data 1 = transmitting, can't send data yet
    },

    /// SSI clock prescale register
    0x010 => reg16 ssicpsr {
      0..7 => cpsdvsr: rw //= SSI clock prescale divisor, 2 – 254 inclusive
    },

    /// SSI clock source configuration
    0xfc8 => reg16 ssicc {
      0..3 => cs: rw,     //= SSI baud clock source, 0x0 = system clock, 0x5 = PIOSC
    },
  });

  pub const SSI_0: *const Ssi = 0x40008000 as *const Ssi;
  pub const SSI_1: *const Ssi = 0x40009000 as *const Ssi;
  pub const SSI_2: *const Ssi = 0x4000A000 as *const Ssi;
  pub const SSI_3: *const Ssi = 0x4000B000 as *const Ssi;
}<|endoftext|>


<commit_before>//
// Copyright:: Copyright (c) 2015 Chef Software, Inc.
// License:: Apache License, Version 2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

use utils::{self, walk_tree_for_path, mkdir_recursive};
use utils::path_ext::is_dir;
use errors::{DeliveryError, Kind};
use types::DeliveryResult;
use std::path::{Path, PathBuf};
use http::APIClient;
use git::{self, ReviewResult};
use std::process::{Output, Command};
use std::fs;
use std::fs::File;
use std::io::prelude::*;

// README with a brief description of delivery and how to use it. This is added
// to a new project by `delivery init` so we have something to submit as the
// first change.
//
// We load this up as bytes since that's what std::io::Write takes for
// arguments.
static DELIVERY_DOT_MD_CONTENT: &'static [u8] = include_bytes!("DELIVERY.md");

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Type {
    Bitbucket,
    Github
}

#[derive(Debug, Clone)]
pub struct SourceCodeProvider {
    pub repo_name: String,
    pub organization: String,
    pub branch: String,
    pub verify_ssl: bool,
    pub kind: Type,
}

impl SourceCodeProvider {
    // Create a new `SourceCodeProvider`. Returns an error result if
    // required configuration values are missing. Expects to find
    // `scp`, `repository`, `scp-organization`, `branch`, and `ssl`.
    pub fn new(scp: &str, repo: &str, org: &str, branch: &str,
               no_ssl: bool) -> DeliveryResult<SourceCodeProvider> {
        let scp_kind = match scp {
            "github" => Type::Github,
            "bitbucket" => Type::Bitbucket,
            _ => return Err(DeliveryError{ kind: Kind::UnknownProjectType, detail:None })
        };
        if repo.to_string().is_empty()
            || org.to_string().is_empty()
            || branch.to_string().is_empty() {
            match scp_kind {
                Type::Github => return Err(
                    DeliveryError{
                        kind: Kind::OptionConstraint,
                        detail: Some(format!("Missing Github Source Code Provider attributes, specify: \
                                              repo-name, org-name and pipeline(default: master)"))
                    }
                ),
                Type::Bitbucket => return Err(
                    DeliveryError{
                        kind: Kind::OptionConstraint,
                        detail: Some(format!("Missing Bitbucket Source Code Provider attributes, specify: \
                                              repo-name, project-key and pipeline(default: master)"))
                    }
                ),
            }
        }
        Ok(SourceCodeProvider {
            kind: scp_kind,
            repo_name: repo.to_string(),
            organization: org.to_string(),
            branch: branch.to_string(),
            verify_ssl: !no_ssl,
        })
    }

    // Verify if the SCP is configured on the Delivery Server
    pub fn verify_server_config(&self, client: &APIClient) -> DeliveryResult<()> {
        match self.kind {
            Type::Github => {
                let scp_config = try!(client.get_github_server_config());
                if scp_config.is_empty() {
                    return Err(DeliveryError{ kind: Kind::NoGithubSCPConfig, detail: None })
                }
            },
            Type::Bitbucket => {
                let scp_config = try!(client.get_bitbucket_server_config());
                if scp_config.is_empty() {
                    return Err(DeliveryError{ kind: Kind::NoBitbucketSCPConfig, detail: None })
                }
            }
        }
        Ok(())
    }
}

// Create a Delivery Pipeline.
// Returns true if created, returns false if already exists.
pub fn create_delivery_pipeline(client: &APIClient, org: &String,
                                proj: &String, pipe: &String) -> DeliveryResult<bool> {
    if client.pipeline_exists(org, proj, pipe) {
        return Ok(false)
    } else {
        try!(client.create_pipeline(org, proj, pipe, Some(pipe)));
        return Ok(true)
    }
}

// Create a Delivery Project with Delivery as SCP (default).
// If the project is created, return true.
// If the project already exists, return false
pub fn create_delivery_project(client: &APIClient,
                               org: &String,
                               proj: &String) -> DeliveryResult<bool> {
    if client.project_exists(org, proj) {
        return Ok(false)
    } else {
        try!(client.create_delivery_project(org, proj));
        return Ok(true)
    }
}

// Push local content to the Delivery Server if no upstream commits.
// Returns true if commits pushed, returns false if upstream commits found.
pub fn push_project_content_to_delivery(pipeline: &str) -> DeliveryResult<bool> {
    if try!(git::server_content(pipeline)) {
        Ok(false)
    } else {
        try!(git::git_push(pipeline));
        Ok(true)
    }
}

// Create delivery remote if it doesn't exist. Returns true if created.
pub fn create_delivery_remote_if_missing(
      delivery_git_ssh_url: &String) -> DeliveryResult<bool> {
    if try!(git::config_repo(delivery_git_ssh_url, &project_path())) {
        return Ok(true)
    } else {
        return Ok(false)
    }
}

// Check to see if the origin remote is set up.
pub fn missing_github_remote() -> DeliveryResult<bool> {
    let git_remote_result = git::git_command(&["remote"], &project_path());
    match git_remote_result {
        Ok(git_result) => Ok(!git_result.stdout.contains("origin")),
        Err(e) => return Err(e)
    }
}

/// Search for the project root directory
///
/// We will walk through the provided path tree until we find the
/// git config (`.git/config`) annd then we will extract the root
/// directory.
///
/// # Examples
///
/// Having this directory tree:
/// /delivery-cli
///  ├── .git
///  │   └── config
///  ├── src
///  │   └── delivery
///  └── features
///
/// ```
/// use std::env;
/// use delivery::project::root_dir;
///
/// let root = env::current_dir().unwrap();
///
/// //Stepping into `delivery-cli/src/delivery`
/// let mut delivery_src = env::current_dir().unwrap();
/// delivery_src.push("src/delivery");
///
/// assert_eq!(root, root_dir(&delivery_src.as_path()).unwrap());
/// ```
pub fn root_dir(dir: &Path) -> DeliveryResult<PathBuf> {
    match walk_tree_for_path(&PathBuf::from(&dir), ".git/config") {
        Some(p) => {
           let git_d = p.parent().unwrap();
           let root_d = git_d.parent().unwrap();
           Ok(PathBuf::from(root_d))
        },
        None => Err(DeliveryError{kind: Kind::NoGitConfig,
                                  detail: None})
    }
}

pub fn project_path() -> PathBuf {
    root_dir(&utils::cwd()).unwrap()
}

// Return the project name from the current path
pub fn project_from_cwd() -> DeliveryResult<String> {
    let cwd = try!(self::root_dir(&utils::cwd()));
    Ok(cwd.file_name().unwrap().to_str().unwrap().to_string())
}

// Return the project name or try to extract it from the current path
pub fn project_or_from_cwd(proj: &str) -> DeliveryResult<String> {
    if proj.is_empty() {
        project_from_cwd()
    } else {
        Ok(proj.to_string())
    }
}

// Create the feature branch `add-delivery-config`
//
// This branch is created to start modifying the project repository
// In the case of a failure, we could roll back fearly easy by checking
// out master and deleting this feature branch.
//
// If feature branch created, return true, else return false.
pub fn create_feature_branch_if_missing(project_path: &PathBuf, branch_name: &str) -> DeliveryResult<bool> {
    match git::git_command(&["checkout", "-b", branch_name], project_path) {
        Ok(_) => {
            return Ok(true);
        },
        Err(e) => {
            match e.detail.clone() {
                Some(msg) => {
                    if msg.contains(&format!("A branch named '{}' already exists", branch_name)) {
                       try!(git::git_command(&["checkout", branch_name], project_path));
                        return Ok(false)
                    } else {
                        return Err(e)
                    }
                },
                // Unexpected error, raise.
                None => Err(e)
            }
        }
    }
}

// Add and commit the generated build_cookbook
pub fn add_commit_build_cookbook(custom_config_passed: &bool) -> DeliveryResult<bool> {
    // .delivery is probably not yet under version control, so we have to add
    // the whole folder instead of .delivery/build_cookbook.
    try!(git::git_command(&["add", ".delivery"], &project_path()));

    let mut commit_msg = "Adds Delivery build cookbook".to_string();
    if *custom_config_passed {
        commit_msg = commit_msg + " and config";
    }

    // Commit the changes made in .delivery but detect if nothing has changed,
    // if that is the case, we are Ok() to continue
    match git::git_commit(&commit_msg) {
      Ok(_) => Ok(true),
      Err(DeliveryError{ kind: Kind::EmptyGitCommit, .. }) => Ok(false),
      Err(e) => Err(e)
    }
}

// Create the delivery readme if it doesn't exist already.
pub fn create_delivery_readme() -> DeliveryResult<bool> {
    // NOTE: this isn't guaranteed to be in the project root; however it is only invoked via
    // `delivery init` which makes some assumptions elsewhere that the CWD is the project root.
    if !PathBuf::from("DELIVERY.md").exists() {
        let mut f = try!(File::create("DELIVERY.md"));
        try!(f.write_all(&DELIVERY_DOT_MD_CONTENT));
        Ok(true)
    } else {
        Ok(false)
    }
}

pub fn commit_delivery_readme() -> DeliveryResult<()> {
    try!(git::git_command(&["add", "DELIVERY.md"], &project_path()));
    let commit_msg = "New pipeline verification commit".to_string();
    try!(git::git_command(&["commit", "-m", &commit_msg], &project_path()));
    Ok(())
}

pub fn create_dot_delivery() -> &'static Path {
    // TODO: should we be doing some relative pathing here?
    let dot_delivery = Path::new(".delivery");
    fs::create_dir_all(dot_delivery).unwrap();
    dot_delivery
}

pub fn create_default_build_cookbook(pipeline: &str) -> DeliveryResult<Command> {
    let mut command = utils::make_command("chef");
    command.arg("generate")
        .arg("build-cookbook")
        .arg(".delivery/build_cookbook")
        .arg("--pipeline")
        .arg(pipeline)
        .current_dir(&project_path());
    let output = try!(command.output());
    try!(handle_chef_generate_cookbook_cmd(output));
    Ok(command)
}

#[derive(Debug)]
pub enum CustomCookbookSource {
    Cached,
    Disk,
    Git
}

// Custom build_cookbook generation
//
// This method handles a custom generator which could be:
// 1) A local path
// 2) Or a git repo URL
// TODO) From Supermarket
pub fn download_or_mv_custom_build_cookbook_generator(
        generator: &Path,
        cache_path: &Path) -> DeliveryResult<CustomCookbookSource> {
    try!(mkdir_recursive(cache_path));
    if generator.has_root() {
        try!(utils::copy_recursive(&generator, &cache_path));
        return Ok(CustomCookbookSource::Disk)
    } else {
        let mut cache_generator_path: PathBuf = cache_path.to_path_buf();
        cache_generator_path.push(generator.file_name().unwrap());
        if is_dir(&cache_generator_path) {
            return Ok(CustomCookbookSource::Cached)
        } else {
            let cache_path_str = &cache_generator_path.to_string_lossy();
            let generator_str  = &generator.to_string_lossy();
            try!(git::clone(&cache_path_str, &generator_str));
            return Ok(CustomCookbookSource::Git)
        }
    }
}

// Generate the build_cookbook using ChefDK generate
pub fn chef_generate_build_cookbook_from_generator(
      generator: &Path, project_path: &Path) -> DeliveryResult<Command> {
    let mut command = utils::make_command("chef");
    command.arg("generate")
        .arg("build-cookbook")
        .arg(".")
        .arg("-g")
        .arg(generator)
        .current_dir(&project_path);

    let output = try!(command.output());
    try!(handle_chef_generate_cookbook_cmd(output));
    Ok(command)
}

// Default cookbooks generator cache path
pub fn generator_cache_path() -> DeliveryResult<PathBuf> {
    utils::home_dir(&[".delivery/cache/generator-cookbooks"])
}

fn handle_chef_generate_cookbook_cmd(output: Output) -> DeliveryResult<()> {
    if !output.status.success() {
        return Err(
            DeliveryError {
                kind: Kind::FailedToExecute,
                detail: Some(format!(
                            "Failed to execute chef generate:\n\
                            STDOUT: {}\nSTDERR: {}",
                            String::from_utf8_lossy(&output.stdout),
                            String::from_utf8_lossy(&output.stderr)
                        ))
            }
        )
    }
    Ok(())
}

pub fn review(target: &String, head: &String) -> DeliveryResult<ReviewResult> {
    if target == head {
        Err(DeliveryError{ kind: Kind::CannotReviewSameBranch, detail: None })
    } else {
        Ok(try!(git::git_push_review(head, target)))
    }
}

pub fn handle_review_result(review: &ReviewResult,
                            no_open: &bool) -> DeliveryResult<Option<String>> {
    match review.url {
        Some(ref url) => {
            if !no_open {
                try!(utils::open::item(&url));
            }
            Ok(Some(url.clone()))
        },
        None => Ok(None)
    }
}

#[cfg(test)]
mod tests {
    use std::path::Path;
    use super::root_dir;

    #[test]
    fn detect_error_if_root_project_is_not_a_git_repo() {
        // This path doesn't even exist
        // So we will expect to throw an Err(_)
        let lib_path = Path::new("/project/src/libraries");
        match root_dir(&lib_path) {
            Ok(_) => assert!(false),
            Err(_) => assert!(true)
        }
    }
}
<commit_msg>[nit] Do not use String, instead use str<commit_after>//
// Copyright:: Copyright (c) 2015 Chef Software, Inc.
// License:: Apache License, Version 2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

use utils::{self, walk_tree_for_path, mkdir_recursive};
use utils::path_ext::is_dir;
use errors::{DeliveryError, Kind};
use types::DeliveryResult;
use std::path::{Path, PathBuf};
use http::APIClient;
use git::{self, ReviewResult};
use std::process::{Output, Command};
use std::fs;
use std::fs::File;
use std::io::prelude::*;

// README with a brief description of delivery and how to use it. This is added
// to a new project by `delivery init` so we have something to submit as the
// first change.
//
// We load this up as bytes since that's what std::io::Write takes for
// arguments.
static DELIVERY_DOT_MD_CONTENT: &'static [u8] = include_bytes!("DELIVERY.md");

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Type {
    Bitbucket,
    Github
}

#[derive(Debug, Clone)]
pub struct SourceCodeProvider {
    pub repo_name: String,
    pub organization: String,
    pub branch: String,
    pub verify_ssl: bool,
    pub kind: Type,
}

impl SourceCodeProvider {
    // Create a new `SourceCodeProvider`. Returns an error result if
    // required configuration values are missing. Expects to find
    // `scp`, `repository`, `scp-organization`, `branch`, and `ssl`.
    pub fn new(scp: &str, repo: &str, org: &str, branch: &str,
               no_ssl: bool) -> DeliveryResult<SourceCodeProvider> {
        let scp_kind = match scp {
            "github" => Type::Github,
            "bitbucket" => Type::Bitbucket,
            _ => return Err(DeliveryError{ kind: Kind::UnknownProjectType, detail:None })
        };
        if repo.to_string().is_empty()
            || org.to_string().is_empty()
            || branch.to_string().is_empty() {
            match scp_kind {
                Type::Github => return Err(
                    DeliveryError{
                        kind: Kind::OptionConstraint,
                        detail: Some(format!("Missing Github Source Code Provider attributes, specify: \
                                              repo-name, org-name and pipeline(default: master)"))
                    }
                ),
                Type::Bitbucket => return Err(
                    DeliveryError{
                        kind: Kind::OptionConstraint,
                        detail: Some(format!("Missing Bitbucket Source Code Provider attributes, specify: \
                                              repo-name, project-key and pipeline(default: master)"))
                    }
                ),
            }
        }
        Ok(SourceCodeProvider {
            kind: scp_kind,
            repo_name: repo.to_string(),
            organization: org.to_string(),
            branch: branch.to_string(),
            verify_ssl: !no_ssl,
        })
    }

    // Transform the kind Type to str
    pub fn kind_to_fancy_str(&self) -> DeliveryResult<&str> {
        match self.kind {
            Type::Github => Ok("GitHub"),
            Type::Bitbucket => Ok("Bitbucket")
        }
    }

    // Verify if the SCP is configured on the Delivery Server
    pub fn verify_server_config(&self, client: &APIClient) -> DeliveryResult<()> {
        match self.kind {
            Type::Github => {
                let scp_config = try!(client.get_github_server_config());
                if scp_config.is_empty() {
                    return Err(DeliveryError{ kind: Kind::NoGithubSCPConfig, detail: None })
                }
            },
            Type::Bitbucket => {
                let scp_config = try!(client.get_bitbucket_server_config());
                if scp_config.is_empty() {
                    return Err(DeliveryError{ kind: Kind::NoBitbucketSCPConfig, detail: None })
                }
            }
        }
        Ok(())
    }
}

// Create a Delivery Pipeline.
// Returns true if created, returns false if already exists.
pub fn create_delivery_pipeline(client: &APIClient, org: &str,
                                proj: &str, pipe: &str) -> DeliveryResult<bool> {
    if client.pipeline_exists(org, proj, pipe) {
        return Ok(false)
    } else {
        try!(client.create_pipeline(org, proj, pipe, Some(pipe)));
        return Ok(true)
    }
}

// Create a Delivery Project with Delivery as SCP (default).
// If the project is created, return true.
// If the project already exists, return false
pub fn create_delivery_project(client: &APIClient, org: &str,
                               proj: &str) -> DeliveryResult<bool> {
    if client.project_exists(org, proj) {
        return Ok(false)
    } else {
        try!(client.create_delivery_project(org, proj));
        return Ok(true)
    }
}

// Push local content to the Delivery Server if no upstream commits.
// Returns true if commits pushed, returns false if upstream commits found.
pub fn push_project_content_to_delivery(pipeline: &str) -> DeliveryResult<bool> {
    if try!(git::server_content(pipeline)) {
        Ok(false)
    } else {
        try!(git::git_push(pipeline));
        Ok(true)
    }
}

// Create delivery remote if it doesn't exist. Returns true if created.
pub fn create_delivery_remote_if_missing(
      delivery_git_ssh_url: &str) -> DeliveryResult<bool> {
    if try!(git::config_repo(delivery_git_ssh_url, &project_path())) {
        return Ok(true)
    } else {
        return Ok(false)
    }
}

// Check to see if the origin remote is set up.
pub fn missing_github_remote() -> DeliveryResult<bool> {
    let git_remote_result = git::git_command(&["remote"], &project_path());
    match git_remote_result {
        Ok(git_result) => Ok(!git_result.stdout.contains("origin")),
        Err(e) => return Err(e)
    }
}

/// Search for the project root directory
///
/// We will walk through the provided path tree until we find the
/// git config (`.git/config`) annd then we will extract the root
/// directory.
///
/// # Examples
///
/// Having this directory tree:
/// /delivery-cli
///  ├── .git
///  │   └── config
///  ├── src
///  │   └── delivery
///  └── features
///
/// ```
/// use std::env;
/// use delivery::project::root_dir;
///
/// let root = env::current_dir().unwrap();
///
/// //Stepping into `delivery-cli/src/delivery`
/// let mut delivery_src = env::current_dir().unwrap();
/// delivery_src.push("src/delivery");
///
/// assert_eq!(root, root_dir(&delivery_src.as_path()).unwrap());
/// ```
pub fn root_dir(dir: &Path) -> DeliveryResult<PathBuf> {
    match walk_tree_for_path(&PathBuf::from(&dir), ".git/config") {
        Some(p) => {
           let git_d = p.parent().unwrap();
           let root_d = git_d.parent().unwrap();
           Ok(PathBuf::from(root_d))
        },
        None => Err(DeliveryError{kind: Kind::NoGitConfig,
                                  detail: None})
    }
}

pub fn project_path() -> PathBuf {
    root_dir(&utils::cwd()).unwrap()
}

// Return the project name from the current path
pub fn project_from_cwd() -> DeliveryResult<String> {
    let cwd = try!(self::root_dir(&utils::cwd()));
    Ok(cwd.file_name().unwrap().to_str().unwrap().to_string())
}

// Return the project name or try to extract it from the current path
pub fn project_or_from_cwd(proj: &str) -> DeliveryResult<String> {
    if proj.is_empty() {
        project_from_cwd()
    } else {
        Ok(proj.to_string())
    }
}

// Create the feature branch `add-delivery-config`
//
// This branch is created to start modifying the project repository
// In the case of a failure, we could roll back fearly easy by checking
// out master and deleting this feature branch.
//
// If feature branch created, return true, else return false.
pub fn create_feature_branch_if_missing(project_path: &PathBuf, branch_name: &str) -> DeliveryResult<bool> {
    match git::git_command(&["checkout", "-b", branch_name], project_path) {
        Ok(_) => {
            return Ok(true);
        },
        Err(e) => {
            match e.detail.clone() {
                Some(msg) => {
                    if msg.contains(&format!("A branch named '{}' already exists", branch_name)) {
                       try!(git::git_command(&["checkout", branch_name], project_path));
                        return Ok(false)
                    } else {
                        return Err(e)
                    }
                },
                // Unexpected error, raise.
                None => Err(e)
            }
        }
    }
}

// Add and commit the generated build_cookbook
pub fn add_commit_build_cookbook(custom_config_passed: &bool) -> DeliveryResult<bool> {
    // .delivery is probably not yet under version control, so we have to add
    // the whole folder instead of .delivery/build_cookbook.
    try!(git::git_command(&["add", ".delivery"], &project_path()));

    let mut commit_msg = "Adds Delivery build cookbook".to_string();
    if *custom_config_passed {
        commit_msg = commit_msg + " and config";
    }

    // Commit the changes made in .delivery but detect if nothing has changed,
    // if that is the case, we are Ok() to continue
    match git::git_commit(&commit_msg) {
      Ok(_) => Ok(true),
      Err(DeliveryError{ kind: Kind::EmptyGitCommit, .. }) => Ok(false),
      Err(e) => Err(e)
    }
}

// Create the delivery readme if it doesn't exist already.
pub fn create_delivery_readme() -> DeliveryResult<bool> {
    // NOTE: this isn't guaranteed to be in the project root; however it is only invoked via
    // `delivery init` which makes some assumptions elsewhere that the CWD is the project root.
    if !PathBuf::from("DELIVERY.md").exists() {
        let mut f = try!(File::create("DELIVERY.md"));
        try!(f.write_all(&DELIVERY_DOT_MD_CONTENT));
        Ok(true)
    } else {
        Ok(false)
    }
}

pub fn commit_delivery_readme() -> DeliveryResult<()> {
    try!(git::git_command(&["add", "DELIVERY.md"], &project_path()));
    let commit_msg = "New pipeline verification commit".to_string();
    try!(git::git_command(&["commit", "-m", &commit_msg], &project_path()));
    Ok(())
}

pub fn create_dot_delivery() -> &'static Path {
    // TODO: should we be doing some relative pathing here?
    let dot_delivery = Path::new(".delivery");
    fs::create_dir_all(dot_delivery).unwrap();
    dot_delivery
}

pub fn create_default_build_cookbook(pipeline: &str) -> DeliveryResult<Command> {
    let mut command = utils::make_command("chef");
    command.arg("generate")
        .arg("build-cookbook")
        .arg(".delivery/build_cookbook")
        .arg("--pipeline")
        .arg(pipeline)
        .current_dir(&project_path());
    let output = try!(command.output());
    try!(handle_chef_generate_cookbook_cmd(output));
    Ok(command)
}

#[derive(Debug)]
pub enum CustomCookbookSource {
    Cached,
    Disk,
    Git
}

// Custom build_cookbook generation
//
// This method handles a custom generator which could be:
// 1) A local path
// 2) Or a git repo URL
// TODO) From Supermarket
pub fn download_or_mv_custom_build_cookbook_generator(
        generator: &Path,
        cache_path: &Path) -> DeliveryResult<CustomCookbookSource> {
    try!(mkdir_recursive(cache_path));
    if generator.has_root() {
        try!(utils::copy_recursive(&generator, &cache_path));
        return Ok(CustomCookbookSource::Disk)
    } else {
        let mut cache_generator_path: PathBuf = cache_path.to_path_buf();
        cache_generator_path.push(generator.file_name().unwrap());
        if is_dir(&cache_generator_path) {
            return Ok(CustomCookbookSource::Cached)
        } else {
            let cache_path_str = &cache_generator_path.to_string_lossy();
            let generator_str  = &generator.to_string_lossy();
            try!(git::clone(&cache_path_str, &generator_str));
            return Ok(CustomCookbookSource::Git)
        }
    }
}

// Generate the build_cookbook using ChefDK generate
pub fn chef_generate_build_cookbook_from_generator(
      generator: &Path, project_path: &Path) -> DeliveryResult<Command> {
    let mut command = utils::make_command("chef");
    command.arg("generate")
        .arg("build-cookbook")
        .arg(".")
        .arg("-g")
        .arg(generator)
        .current_dir(&project_path);

    let output = try!(command.output());
    try!(handle_chef_generate_cookbook_cmd(output));
    Ok(command)
}

// Default cookbooks generator cache path
pub fn generator_cache_path() -> DeliveryResult<PathBuf> {
    utils::home_dir(&[".delivery/cache/generator-cookbooks"])
}

fn handle_chef_generate_cookbook_cmd(output: Output) -> DeliveryResult<()> {
    if !output.status.success() {
        return Err(
            DeliveryError {
                kind: Kind::FailedToExecute,
                detail: Some(format!(
                            "Failed to execute chef generate:\n\
                            STDOUT: {}\nSTDERR: {}",
                            String::from_utf8_lossy(&output.stdout),
                            String::from_utf8_lossy(&output.stderr)
                        ))
            }
        )
    }
    Ok(())
}

pub fn review(target: &str, head: &str) -> DeliveryResult<ReviewResult> {
    if target == head {
        Err(DeliveryError{ kind: Kind::CannotReviewSameBranch, detail: None })
    } else {
        Ok(try!(git::git_push_review(head, target)))
    }
}

pub fn handle_review_result(review: &ReviewResult,
                            no_open: &bool) -> DeliveryResult<Option<String>> {
    match review.url {
        Some(ref url) => {
            if !no_open {
                try!(utils::open::item(&url));
            }
            Ok(Some(url.clone()))
        },
        None => Ok(None)
    }
}

#[cfg(test)]
mod tests {
    use std::path::Path;
    use super::root_dir;

    #[test]
    fn detect_error_if_root_project_is_not_a_git_repo() {
        // This path doesn't even exist
        // So we will expect to throw an Err(_)
        let lib_path = Path::new("/project/src/libraries");
        match root_dir(&lib_path) {
            Ok(_) => assert!(false),
            Err(_) => assert!(true)
        }
    }
}
<|endoftext|>


<commit_before>// Portion that builds and runs tests.  Assumes that this can be
// done via a call to `make build` and `make test`.  Compiler failure
// is assumed to be communicated by return value.  Tests are assumed
// to have the following output format:
//
// some test name: <PASS|FAIL>
//
// ...where each test result is on its own line.  If multiple tests
// have the same name, then only the last test is recorded.

use std::io::{BufferedReader, RefReader, IoResult, IoError, OtherIoError};
use std::io::pipe::PipeStream;
use std::io::process::{Command, Process, ExitStatus, ExitSignal,
                       ProcessExit};
use std::collections::HashMap;

struct BuilderState<A>(A);

struct InitialState;
struct EnvDone;
struct BuildDone;
struct TestDone;

fn spawn_with_timeout(c: &Command, timeout: Option<u64>) -> IoResult<Process> {
    match c.spawn() {
        Ok(mut p) => {
            p.set_timeout(timeout);
            Ok(p)
        },
        Err(e) => Err(e)
    }
}

/// Runs the given command with the given timeout, ignoring the output.
/// If it returns non-zero, then it's a failure, as with a signal.
/// Takes what it should return on success.
fn run_command<A>(c: &Command, timeout: Option<u64>, on_success: A) -> IoResult<A> {
    // would like to use `and_then` here, but we get problems with capturing
    // on_success, seemingly because the compiler cannot enforce that
    // the closure passed to `and_then` only calls it once
    match spawn_with_timeout(c, timeout) {
        Ok(mut p) => {
            match p.wait() {
                Ok(mut res) => res.if_ok(on_success),
                Err(e) => Err(e)
            }
        },
        Err(e) => Err(e)
    }
}

trait ErrorSimplifier {
    fn if_ok<A>(&self, ret_this: A) -> IoResult<A>;
}

impl ErrorSimplifier for ProcessExit {
    fn if_ok<A>(&self, ret_this: A) -> IoResult<A> {
        match *self {
            ExitStatus(0) => Ok(ret_this),
            ExitStatus(x) => Err(
                IoError {
                    kind: OtherIoError,
                    desc: "Non-zero exit code",
                    detail: Some(x.to_string())
                }),
            ExitSignal(x) => Err(
                IoError {
                    kind: OtherIoError,
                    desc: "Exit signal",
                    detail: Some(x.to_string())
                })
        }
    }
}

trait EnvSetup {
    fn timeout(&self) -> Option<u64>;

    fn command(&self) -> Command;

    /// Gets everything in order for testing to be performed.
    /// After calling this, it is assumed that we are ready
    /// to call make
    fn setup_env(&self) -> IoResult<BuilderState<EnvDone>> {
        run_command(&self.command(), self.timeout(),
                    BuilderState(EnvDone))
    }
}

trait BuildSetup {
    fn timeout(&self) -> Option<u64>;

    fn command(&self) -> Command;

    fn do_build(&self) -> IoResult<BuilderState<BuildDone>> {
        run_command(&self.command(), self.timeout(),
                    BuilderState(BuildDone))
    }
}

enum TestResult {
    Pass,
    Fail
}

struct ProcessReader {
    p: Process
}

impl ProcessReader {
    fn new(cmd: &Command) -> IoResult<ProcessReader> {
        cmd.spawn().map(|p| ProcessReader { p: p })
    }

    fn output_reader<'a>(&'a mut self) -> BufferedReader<RefReader<'a, PipeStream>> {
        // unwrap should be ok - the documentation says `Some` is the default,
        // and we are not messing with any of the defaults
        BufferedReader::new(
            Reader::by_ref(
                self.p.stdout.as_mut().unwrap()))
    }
}

trait Tester {
    fn parse_test_result(line: &str) -> IoResult<TestResult> {
        match line {
            "PASS" => Ok(Pass),
            "FAIL" => Ok(Fail),
            _ => Err(
                IoError {
                    kind: OtherIoError,
                    desc: "Malformed test result",
                    detail: Some(line.to_string())
                })
        }
    }

    fn parse_line(line: &str) -> IoResult<(String, TestResult)> {
        let results: Vec<&str> = line.split_str(":").collect();
        if results.len() == 2 {
            Tester::parse_test_result(results[1]).map(|res| {
                (results[0].to_string(), res)
            })
        } else {
            Err(
                IoError {
                    kind: OtherIoError,
                    desc: "Malformed test string",
                    detail: Some(line.to_string())
                })
        }
    }

    fn test_command(&self) -> Command;

    fn do_testing(&self) -> IoResult<HashMap<String, TestResult>> {
        // TODO: without do syntax this becomes nightmarish in
        // functional style, and I had issues with closures capturing
        // too much
        match ProcessReader::new(&self.test_command()) {
            Ok(mut reader) => {
                let mut map = HashMap::new();
                for op_line in reader.output_reader().lines() {
                    match op_line {
                        Ok(line) => {
                            match Tester::parse_line(line.as_slice()) {
                                Ok((k, v)) => {
                                    map.insert(k, v);
                                }
                                Err(s) => { return Err(s); }
                            }
                        }
                        Err(s) => { return Err(s); }
                    }
                }
                Ok(map)

            }
            Err(s) => { return Err(s); }
        }
    }
}

struct TestingRequest {
    dir: String, // directory where the build is to be performed
    makefile_loc: String // where the makefile is located
}
<commit_msg>Believe everything is in place for a simple test run.<commit_after>// Portion that builds and runs tests.  Assumes that this can be
// done via a call to `make build` and `make test`.  Compiler failure
// is assumed to be communicated by return value.  Tests are assumed
// to have the following output format:
//
// some test name: <PASS|FAIL>
//
// ...where each test result is on its own line.  If multiple tests
// have the same name, then only the last test is recorded.

use std::collections::HashMap;
use std::io::{BufferedReader, RefReader, IoResult, IoError, OtherIoError};
use std::io::pipe::PipeStream;
use std::io::process::{Command, Process, ExitStatus, ExitSignal,
                       ProcessExit};
use std::path::posix::Path;

fn spawn_with_timeout(c: &Command, timeout: Option<u64>) -> IoResult<Process> {
    match c.spawn() {
        Ok(mut p) => {
            p.set_timeout(timeout);
            Ok(p)
        },
        Err(e) => Err(e)
    }
}

/// Runs the given command with the given timeout, ignoring the output.
/// If it returns non-zero, then it's a failure, as with a signal.
/// Takes what it should return on success.
fn run_command<A>(c: &Command, timeout: Option<u64>, on_success: A) -> IoResult<A> {
    // would like to use `and_then` here, but we get problems with capturing
    // on_success, seemingly because the compiler cannot enforce that
    // the closure passed to `and_then` only calls it once
    match spawn_with_timeout(c, timeout) {
        Ok(mut p) => {
            match p.wait() {
                Ok(res) => res.if_ok(on_success),
                Err(e) => Err(e)
            }
        },
        Err(e) => Err(e)
    }
}

trait ErrorSimplifier {
    fn if_ok<A>(&self, ret_this: A) -> IoResult<A>;
}

impl ErrorSimplifier for ProcessExit {
    fn if_ok<A>(&self, ret_this: A) -> IoResult<A> {
        match *self {
            ExitStatus(0) => Ok(ret_this),
            ExitStatus(x) => Err(
                IoError {
                    kind: OtherIoError,
                    desc: "Non-zero exit code",
                    detail: Some(x.to_string())
                }),
            ExitSignal(x) => Err(
                IoError {
                    kind: OtherIoError,
                    desc: "Exit signal",
                    detail: Some(x.to_string())
                })
        }
    }
}

trait EnvSetup {
    fn env_timeout(&self) -> Option<u64>;

    fn env_command(&self) -> Command;

    /// Gets everything in order for testing to be performed.
    /// After calling this, it is assumed that we are ready
    /// to call make
    fn setup_env(&self) -> IoResult<()> {
        run_command(&self.env_command(), self.env_timeout(), ())
    }
}

trait BuildSetup {
    fn build_timeout(&self) -> Option<u64>;

    fn build_command(&self) -> Command;

    fn do_build(&self) -> IoResult<()> {
        run_command(&self.build_command(), self.build_timeout(), ())
    }
}

enum TestResult {
    Pass,
    Fail
}

struct ProcessReader {
    p: Process
}

impl ProcessReader {
    fn new(cmd: &Command, timeout: Option<u64>) -> IoResult<ProcessReader> {
        spawn_with_timeout(cmd, timeout).map(|p| ProcessReader { p: p })
    }

    fn output_reader<'a>(&'a mut self) -> BufferedReader<RefReader<'a, PipeStream>> {
        // unwrap should be ok - the documentation says `Some` is the default,
        // and we are not messing with any of the defaults
        BufferedReader::new(
            Reader::by_ref(
                self.p.stdout.as_mut().unwrap()))
    }
}

fn parse_test_result(line: &str) -> IoResult<TestResult> {
    match line {
        "PASS" => Ok(Pass),
        "FAIL" => Ok(Fail),
        _ => Err(
            IoError {
                kind: OtherIoError,
                desc: "Malformed test result",
                detail: Some(line.to_string())
            })
    }
}

fn parse_line(line: &str) -> IoResult<(String, TestResult)> {
    let results: Vec<&str> = line.split_str(":").collect();
    if results.len() == 2 {
        parse_test_result(results[1]).map(|res| {
            (results[0].to_string(), res)
        })
    } else {
        Err(
            IoError {
                kind: OtherIoError,
                desc: "Malformed test string",
                detail: Some(line.to_string())
            })
    }
}

trait Tester {
    fn test_timeout(&self) -> Option<u64>;

    fn test_command(&self) -> Command;

    fn do_testing(&self) -> IoResult<HashMap<String, TestResult>> {
        // TODO: without do syntax this becomes nightmarish in
        // functional style, and I had issues with closures capturing
        // too much
        match ProcessReader::new(&self.test_command(), self.test_timeout()) {
            Ok(mut reader) => {
                let mut map = HashMap::new();
                for op_line in reader.output_reader().lines() {
                    match op_line {
                        Ok(line) => {
                            match parse_line(line.as_slice()) {
                                Ok((k, v)) => {
                                    map.insert(k, v);
                                }
                                Err(s) => { return Err(s); }
                            }
                        }
                        Err(s) => { return Err(s); }
                    }
                }
                Ok(map)

            }
            Err(s) => { return Err(s); }
        }
    }
}

struct TestingRequest {
    dir: Path, // directory where the build is to be performed
    makefile_loc: Path // where the makefile is located
}

impl TestingRequest {
    fn make_with_arg<A : ToCStr>(&self, arg: A) -> Command {
        let mut c = Command::new("make");
        c.arg(arg).cwd(&self.dir);
        c
    }
}

impl EnvSetup for TestingRequest {
    fn env_timeout(&self) -> Option<u64> { None }

    fn env_command(&self) -> Command {
        let mut c = Command::new("cp");
        c.arg(self.makefile_loc.as_str().unwrap());
        c.arg(self.dir.as_str().unwrap());
        c
    }
}

impl BuildSetup for TestingRequest {
    fn build_timeout(&self) -> Option<u64> { None }

    fn build_command(&self) -> Command {
        self.make_with_arg("build")
    }
}

impl Tester for TestingRequest {
    fn test_timeout(&self) -> Option<u64> { None }

    fn test_command(&self) -> Command {
        self.make_with_arg("test")
    }
}
<|endoftext|>