<commit_before>//! Models about OAuth2 applications.

use super::{
    id::UserId,
    user::User,
    utils::default_true
};

/// Information about a user's application. An application does not necessarily
/// have an associated bot user.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ApplicationInfo {
    /// The bot user associated with the application. See [`BotApplication`] for
    /// more information.
    ///
    /// [`BotApplication`]: struct.BotApplication.html
    pub bot: Option<BotApplication>,
    /// Indicator of whether the bot is public.
    ///
    /// If a bot is public, anyone may invite it to their [`Guild`]. While a bot
    /// is private, only the owner may add it to a guild.
    ///
    /// [`Guild`]: ../guild/struct.Guild.html
    #[serde(default = "default_true")]
    pub bot_public: bool,
    /// Indicator of whether the bot requires an OAuth2 code grant.
    pub bot_require_code_grant: bool,
    /// A description of the application, assigned by the application owner.
    pub description: String,
    /// A set of bitflags assigned to the application, which represent gated
    /// feature flags that have been enabled for the application.
    pub flags: Option<u64>,
    /// A hash pointing to the application's icon.
    ///
    /// This is not necessarily equivalent to the bot user's avatar.
    pub icon: Option<String>,
    /// The unique numeric Id of the application.
    pub id: UserId,
    /// The name assigned to the application by the application owner.
    pub name: String,
    /// A list of redirect URIs assigned to the application.
    pub redirect_uris: Vec<String>,
    /// A list of RPC Origins assigned to the application.
    pub rpc_origins: Vec<String>,
    /// The given secret to the application.
    ///
    /// This is not equivalent to the application's bot user's token.
    pub secret: String,
    #[serde(skip)]
    pub(crate) _nonexhaustive: (),
}

/// Information about an application with an application's bot user.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct BotApplication {
    /// The unique Id of the bot user.
    pub id: UserId,
    /// A hash of the avatar, if one is assigned.
    ///
    /// Can be used to generate a full URL to the avatar.
    pub avatar: Option<String>,
    /// Indicator of whether it is a bot.
    #[serde(default)]
    pub bot: bool,
    /// The discriminator assigned to the bot user.
    ///
    /// While discriminators are not unique, the `username#discriminator` pair
    /// is.
    pub discriminator: u16,
    /// The bot user's username.
    pub name: String,
    /// The token used to authenticate as the bot user.
    ///
    /// **Note**: Keep this information private, as untrusted sources can use it
    /// to perform any action with a bot user.
    pub token: String,
    #[serde(skip)]
    pub(crate) _nonexhaustive: (),
}

/// Information about the current application and its owner.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct CurrentApplicationInfo {
    pub description: String,
    pub icon: Option<String>,
    pub id: UserId,
    pub name: String,
    pub owner: User,
    #[serde(default)] pub rpc_origins: Vec<String>,
    pub bot_public: bool,
    pub bot_require_code_grant: bool,
    #[serde(skip)]
    pub(crate) _nonexhaustive: (),
}
<commit_msg>Add teams support (#913)<commit_after>//! Models about OAuth2 applications.

use super::{
    id::UserId,
    user::User,
    utils::*,
};

/// Information about a user's application. An application does not necessarily
/// have an associated bot user.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ApplicationInfo {
    /// The bot user associated with the application. See [`BotApplication`] for
    /// more information.
    ///
    /// [`BotApplication`]: struct.BotApplication.html
    pub bot: Option<BotApplication>,
    /// Indicator of whether the bot is public.
    ///
    /// If a bot is public, anyone may invite it to their [`Guild`]. While a bot
    /// is private, only the owner may add it to a guild.
    ///
    /// [`Guild`]: ../guild/struct.Guild.html
    #[serde(default = "default_true")]
    pub bot_public: bool,
    /// Indicator of whether the bot requires an OAuth2 code grant.
    pub bot_require_code_grant: bool,
    /// A description of the application, assigned by the application owner.
    pub description: String,
    /// A set of bitflags assigned to the application, which represent gated
    /// feature flags that have been enabled for the application.
    pub flags: Option<u64>,
    /// A hash pointing to the application's icon.
    ///
    /// This is not necessarily equivalent to the bot user's avatar.
    pub icon: Option<String>,
    /// The unique numeric Id of the application.
    pub id: UserId,
    /// The name assigned to the application by the application owner.
    pub name: String,
    /// A list of redirect URIs assigned to the application.
    pub redirect_uris: Vec<String>,
    /// A list of RPC Origins assigned to the application.
    pub rpc_origins: Vec<String>,
    /// The application team group.
    pub team: Option<Vec<Team>>,
    /// The given secret to the application.
    ///
    /// This is not equivalent to the application's bot user's token.
    pub secret: String,
    #[serde(skip)]
    pub(crate) _nonexhaustive: (),
}

/// Information about an application with an application's bot user.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct BotApplication {
    /// The unique Id of the bot user.
    pub id: UserId,
    /// A hash of the avatar, if one is assigned.
    ///
    /// Can be used to generate a full URL to the avatar.
    pub avatar: Option<String>,
    /// Indicator of whether it is a bot.
    #[serde(default)]
    pub bot: bool,
    /// The discriminator assigned to the bot user.
    ///
    /// While discriminators are not unique, the `username#discriminator` pair
    /// is.
    pub discriminator: u16,
    /// The bot user's username.
    pub name: String,
    /// The token used to authenticate as the bot user.
    ///
    /// **Note**: Keep this information private, as untrusted sources can use it
    /// to perform any action with a bot user.
    pub token: String,
    #[serde(skip)]
    pub(crate) _nonexhaustive: (),
}

/// Information about the current application and its owner.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct CurrentApplicationInfo {
    pub description: String,
    pub icon: Option<String>,
    pub id: UserId,
    pub name: String,
    pub owner: User,
    #[serde(default)] pub rpc_origins: Vec<String>,
    pub bot_public: bool,
    pub bot_require_code_grant: bool,
    pub team: Option<Team>,
    #[serde(skip)]
    pub(crate) _nonexhaustive: (),
}

/// Information about the Team group of the application.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Team {
    /// The icon of the team.
    pub icon: Option<String>,
    /// The snowflake ID of the team.
    #[serde(deserialize_with = "deserialize_u64")]
    pub id: u64,
    /// The members of the team
    pub members: Vec<TeamMember>,
    /// The user id of the team owner.
    pub owner_user_id: UserId,
}

/// Infromation about a Member on a Team.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct TeamMember {
    /// The member's membership state.
    pub membership_state: MembershipState,
    /// The list of permissions of the member on the team.
    ///
    /// NOTE: Will always be ["*"] for now.
    pub permissions: Vec<String>,
    /// The ID of the team they are a member of.
    #[serde(deserialize_with = "deserialize_u64")]
    pub team_id: u64,
    /// The user type of the team member.
    pub user: User,
}

#[derive(Clone, Debug, Copy, PartialEq)]
pub enum MembershipState {
    Invited = 1,
    Accepted = 2,
}

enum_number!(
    MembershipState {
        Invited,
        Accepted,
    }
);
<|endoftext|>


<commit_before>//! The Tessellated Intelligence System is a
//!
//! > massively parallel computer architecture comprised of non-uniformly
//! > interconnected heterogeneous nodes.
//!
//! The manual can be found [online](https://www.vidarholen.net/contents/junk/files/TIS-100%20Reference%20Manual.pdf).
//! Another useful resource is the unofficial [TIS-100 Hackers Guide](https://alandesmet.github.io/TIS-100-Hackers-Guide/).

/// A `Node` models the basic execution node in TIS-100. You change a node state
/// by executing a `Instruction` on it.
#[derive(Debug)]
pub struct Node {
    pub acc: i32,
    bac: i32
}

impl PartialEq for Node {
    /// Node are equal when they have the same internal state
    fn eq(&self, other: &Node) -> bool {
        self.acc == other.acc && self.bac == other.bac
    }
}

impl Eq for Node {}

/// `Instruction`s are executed by a `Node`.
pub enum Instruction {
    NOP,
    MOV(Source, Destination),
    SWP,
    SAV,
    ADD(Source),
    SUB(Source),
}

/// Source are either ports, registers or literals
pub enum Source {
    NIL,
    ACC,
    Literal(i32)
}

/// Destination are either ports or registers
pub enum Destination {
    NIL,
    ACC
}

impl Node {
    pub fn new() -> Node {
        Node { acc: 0, bac: 0 }
    }

    pub fn with(acc: i32, bac: i32) -> Node {
        Node { acc: acc, bac: bac }
    }

    pub fn execute(&self, instruction: Instruction) -> Node {
        match instruction {
            Instruction::NOP => self.nop(),
            Instruction::MOV(source, destination) => self.mov(source, destination) ,
            Instruction::SWP => self.swap(),
            Instruction::SAV => self.save(),
            Instruction::ADD(source) => self.add(source),
            Instruction::SUB(source) => self.subtract(source),
        }
    }

    pub fn nop(&self) -> Node {
        Node { acc: self.acc, bac: self.bac }
    }

    pub fn mov(&self, source: Source, destination: Destination) -> Node {
        let value: i32 = match source {
            Source::NIL => 0,
            Source::ACC => self.acc,
            Source::Literal(value) => value
        };

        self.move_value(value, destination)
    }

    pub fn move_value(&self, value: i32, destination: Destination) -> Node {
        match destination {
            Destination::ACC => Node { acc: value, bac: self.bac },
            _ => self.nop()
        }
    }

    pub fn swap(&self) -> Node {
        Node { acc: self.bac, bac: self.acc }
    }

    pub fn save(&self) -> Node {
        Node { acc: self.acc, bac: self.acc }
    }

    pub fn add(&self, source: Source) -> Node{
        let value: i32 = match source {
            Source::NIL => 0,
            Source::ACC => self.acc,
            Source::Literal(value) => value
        };

        self.add_value(value)
    }

    pub fn add_value(&self, value: i32) -> Node {
        Node { acc: self.acc + value, bac: self.bac }
    }

    pub fn subtract(&self, source: Source) -> Node {
        let value: i32 = match source {
            Source::NIL => 0,
            Source::ACC => self.acc,
            Source::Literal(value) => value
        };

        self.subtract_value(value)
    }

    pub fn subtract_value(&self, value: i32) -> Node {
        Node { acc: self.acc - value, bac: self.bac }
    }
}

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

    #[test]
    fn node_should_start_with_accumulator_zero() {
        let node: Node = Node::new();

        assert_eq!(0, node.acc);
    }

    #[test]
    fn node_should_start_with_backup_zero() {
        let node: Node = Node::new();
        let instruction: Instruction = Instruction::SWP;

        let next: Node = node.execute(instruction);

        assert_eq!(0, next.acc);
    }

    #[test]
    fn node_should_execute_NOP_correctly() {
        let node: Node = Node::with(1, 0);
        let instruction: Instruction = Instruction::NOP;

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 0), next);
    }

    #[test]
    fn node_should_execute_MOV_from_NIL_to_NIL_correctly() {
        let node: Node = Node::with(1, 0);
        let instruction: Instruction = Instruction::MOV(Source::NIL,
                                                        Destination::NIL);

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 0), next);
    }

    #[test]
    fn node_should_execute_MOV_from_ACC_to_ACC_correctly() {
        let node: Node = Node::with(2, 1);
        let instruction: Instruction = Instruction::MOV(Source::ACC,
                                                        Destination::ACC);

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(2, 1), next);
    }

    #[test]
    fn node_should_execute_MOV_from_Literal_to_ACC_correctly() {
        let node: Node = Node::new();
        let instruction: Instruction = Instruction::MOV(Source::Literal(1),
                                                        Destination::ACC);

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 0), next);
    }

    #[test]
    fn node_should_execute_SWP_correctly() {
        let node: Node = Node::with(1, 0);
        let instruction: Instruction = Instruction::SWP;

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(0, 1), next);
    }

    #[test]
    fn node_should_execute_SAV_correctly() {
        let node: Node = Node::with(1,0);
        let instruction: Instruction = Instruction::SAV;

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 1), next);
    }

    #[test]
    fn node_should_execute_ADD_from_Literal_correctly() {
        let node: Node = Node::with(1, 0);
        let instruction: Instruction = Instruction::ADD(Source::Literal(1));

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(2, 0), next);
    }

    #[test]
    fn node_should_execute_SUB_from_Literal_correctly() {
        let node: Node = Node::with(2, 0);
        let instruction: Instruction = Instruction::SUB(Source::Literal(1));

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 0), next);
    }
}

<commit_msg>Reduce visibility of helper methods in Node<commit_after>//! The Tessellated Intelligence System is a
//!
//! > massively parallel computer architecture comprised of non-uniformly
//! > interconnected heterogeneous nodes.
//!
//! The manual can be found [online](https://www.vidarholen.net/contents/junk/files/TIS-100%20Reference%20Manual.pdf).
//! Another useful resource is the unofficial [TIS-100 Hackers Guide](https://alandesmet.github.io/TIS-100-Hackers-Guide/).

/// A `Node` models the basic execution node in TIS-100. You change a node state
/// by executing a `Instruction` on it.
#[derive(Debug)]
pub struct Node {
    pub acc: i32,
    bac: i32
}

impl PartialEq for Node {
    /// Node are equal when they have the same internal state
    fn eq(&self, other: &Node) -> bool {
        self.acc == other.acc && self.bac == other.bac
    }
}

impl Eq for Node {}

/// `Instruction`s are executed by a `Node`.
pub enum Instruction {
    NOP,
    MOV(Source, Destination),
    SWP,
    SAV,
    ADD(Source),
    SUB(Source),
}

/// Source are either ports, registers or literals
pub enum Source {
    NIL,
    ACC,
    Literal(i32)
}

/// Destination are either ports or registers
pub enum Destination {
    NIL,
    ACC
}

impl Node {
    pub fn new() -> Node {
        Node { acc: 0, bac: 0 }
    }

    pub fn with(acc: i32, bac: i32) -> Node {
        Node { acc: acc, bac: bac }
    }

    pub fn execute(&self, instruction: Instruction) -> Node {
        match instruction {
            Instruction::NOP => self.nop(),
            Instruction::MOV(source, destination) => self.mov(source, destination) ,
            Instruction::SWP => self.swap(),
            Instruction::SAV => self.save(),
            Instruction::ADD(source) => self.add(source),
            Instruction::SUB(source) => self.subtract(source),
        }
    }

    fn nop(&self) -> Node {
        Node { acc: self.acc, bac: self.bac }
    }

    fn mov(&self, source: Source, destination: Destination) -> Node {
        let value: i32 = match source {
            Source::NIL => 0,
            Source::ACC => self.acc,
            Source::Literal(value) => value
        };

        self.move_value(value, destination)
    }

    fn move_value(&self, value: i32, destination: Destination) -> Node {
        match destination {
            Destination::ACC => Node { acc: value, bac: self.bac },
            _ => self.nop()
        }
    }

    fn swap(&self) -> Node {
        Node { acc: self.bac, bac: self.acc }
    }

    fn save(&self) -> Node {
        Node { acc: self.acc, bac: self.acc }
    }

    fn add(&self, source: Source) -> Node{
        let value: i32 = match source {
            Source::NIL => 0,
            Source::ACC => self.acc,
            Source::Literal(value) => value
        };

        self.add_value(value)
    }

    fn add_value(&self, value: i32) -> Node {
        Node { acc: self.acc + value, bac: self.bac }
    }

    fn subtract(&self, source: Source) -> Node {
        let value: i32 = match source {
            Source::NIL => 0,
            Source::ACC => self.acc,
            Source::Literal(value) => value
        };

        self.subtract_value(value)
    }

    fn subtract_value(&self, value: i32) -> Node {
        Node { acc: self.acc - value, bac: self.bac }
    }
}

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

    #[test]
    fn node_should_start_with_accumulator_zero() {
        let node: Node = Node::new();

        assert_eq!(0, node.acc);
    }

    #[test]
    fn node_should_start_with_backup_zero() {
        let node: Node = Node::new();
        let instruction: Instruction = Instruction::SWP;

        let next: Node = node.execute(instruction);

        assert_eq!(0, next.acc);
    }

    #[test]
    fn node_should_execute_NOP_correctly() {
        let node: Node = Node::with(1, 0);
        let instruction: Instruction = Instruction::NOP;

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 0), next);
    }

    #[test]
    fn node_should_execute_MOV_from_NIL_to_NIL_correctly() {
        let node: Node = Node::with(1, 0);
        let instruction: Instruction = Instruction::MOV(Source::NIL,
                                                        Destination::NIL);

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 0), next);
    }

    #[test]
    fn node_should_execute_MOV_from_ACC_to_ACC_correctly() {
        let node: Node = Node::with(2, 1);
        let instruction: Instruction = Instruction::MOV(Source::ACC,
                                                        Destination::ACC);

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(2, 1), next);
    }

    #[test]
    fn node_should_execute_MOV_from_Literal_to_ACC_correctly() {
        let node: Node = Node::new();
        let instruction: Instruction = Instruction::MOV(Source::Literal(1),
                                                        Destination::ACC);

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 0), next);
    }

    #[test]
    fn node_should_execute_SWP_correctly() {
        let node: Node = Node::with(1, 0);
        let instruction: Instruction = Instruction::SWP;

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(0, 1), next);
    }

    #[test]
    fn node_should_execute_SAV_correctly() {
        let node: Node = Node::with(1,0);
        let instruction: Instruction = Instruction::SAV;

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 1), next);
    }

    #[test]
    fn node_should_execute_ADD_from_Literal_correctly() {
        let node: Node = Node::with(1, 0);
        let instruction: Instruction = Instruction::ADD(Source::Literal(1));

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(2, 0), next);
    }

    #[test]
    fn node_should_execute_SUB_from_Literal_correctly() {
        let node: Node = Node::with(2, 0);
        let instruction: Instruction = Instruction::SUB(Source::Literal(1));

        let next: Node = node.execute(instruction);

        assert_eq!(Node::with(1, 0), next);
    }
}

<|endoftext|>


<commit_before>use ::data::messages::{BookUpdate, RecordUpdate, BookRecord};
use ::data::trade::TradeBook;
use std::str::FromStr;
use super::Actor;
use std::sync::{Arc, Mutex};
use ::error::PoloError;

#[derive(Clone)]
pub struct Accountant {
  tb: Arc<Mutex<TradeBook>>,
}

impl Accountant {
  pub fn new(tb: &Arc<Mutex<TradeBook>>) -> Accountant {
    Accountant {
      tb: tb.clone()
    }
  }
}

impl Actor for Accountant {
  fn process_message(&mut self, msg: String) -> Result<(),PoloError> {
    let err = |title| PoloError::wrong_data(format!("{} {:?}", title, msg));

    let update = BookUpdate::from_str(&msg)?;
    for rec in update.records {
      let mut tb = self.tb.lock().unwrap();
      match rec {
        RecordUpdate::Initial(book) => {
          tb.add_book(book, update.book_id);
        },
        RecordUpdate::SellTotal(BookRecord {rate, amount}) => {
          let book = tb.book_by_id(&update.book_id)
            .ok_or_else(|| err("book not initialized"))?;
          book.update_sell(rate, amount);
        },
        RecordUpdate::BuyTotal(BookRecord {rate, amount}) => {
          let book = tb.book_by_id(&update.book_id)
            .ok_or_else(|| err("book not initialized"))?;
          book.update_buy(rate, amount);
        },
        RecordUpdate::Sell(deal) => {
          let book = tb.book_by_id(&update.book_id)
            .ok_or_else(|| err("book not initialized"))?;
          book.new_deal(deal.id, deal.rate, -deal.amount)?;
        },
        RecordUpdate::Buy(deal) => {
          let book = tb.book_by_id(&update.book_id)
            .ok_or_else(|| err("book not initialized"))?;
          book.new_deal(deal.id, deal.rate, deal.amount)?;
        },
      }
    };
    Ok(())
  }
}

#[cfg(test)]
mod tests {
  use super::Accountant;
  use bus::Bus;
  use ::actors::Actor;
  use ::data::trade::TradeBook;
  use ::data::messages::{BookUpdate, RecordUpdate};
  use std::str::FromStr;
  use std::sync::{Arc, Mutex};
  use std::thread;

  #[test]
  fn initial_order() {
    let tb = Arc::new(Mutex::new(TradeBook::new()));
    let mut actor = Accountant::new(&tb);
    let order = String::from(r#"[189, 5130995, [["i", {"currencyPair": "BTC_BCH", "orderBook": [{"0.13161901": 0.23709568, "0.13164313": "0.17328089"}, {"0.13169621": 0.2331}]}]]]"#);

    let mut bus = Bus::new(1);
    let ch1 = bus.add_rx();
    let th = thread::spawn(move || actor.listen(ch1));
    bus.broadcast(Some(order.clone()));
    bus.broadcast(None);
    println!("{:?}", th.join());

    let mut tb_mut = tb.lock().unwrap();
    let actor_book = tb_mut.book_by_id(&189).unwrap().book_ref();
    match BookUpdate::from_str(&order).unwrap().records[0] {
      RecordUpdate::Initial(ref book) => assert_eq!(*book, *actor_book),
      _ => panic!("BookUpdate::from_str were not able to parse RecordUpdate::Initial")
    }
  }
}<commit_msg>add some debug output<commit_after>use ::data::messages::{BookUpdate, RecordUpdate, BookRecord};
use ::data::trade::TradeBook;
use std::str::FromStr;
use super::Actor;
use std::sync::{Arc, Mutex};
use ::error::PoloError;

#[derive(Clone)]
pub struct Accountant {
  tb: Arc<Mutex<TradeBook>>,
}

impl Accountant {
  pub fn new(tb: &Arc<Mutex<TradeBook>>) -> Accountant {
    Accountant {
      tb: tb.clone()
    }
  }
}

impl Actor for Accountant {
  fn process_message(&mut self, msg: String) -> Result<(),PoloError> {
    let err = |title| PoloError::wrong_data(format!("{} {:?}", title, msg));

    let update = BookUpdate::from_str(&msg)?;
    for rec in update.records {
      let mut tb = self.tb.lock().unwrap();
      match rec {
        RecordUpdate::Initial(book) => {
          tb.add_book(book, update.book_id);
        },
        RecordUpdate::SellTotal(BookRecord {rate, amount}) => {
          let book = tb.book_by_id(&update.book_id)
            .ok_or_else(|| err("book not initialized"))?;
          book.update_sell(rate, amount);
        },
        RecordUpdate::BuyTotal(BookRecord {rate, amount}) => {
          let book = tb.book_by_id(&update.book_id)
            .ok_or_else(|| err("book not initialized"))?;
          book.update_buy(rate, amount);
        },
        RecordUpdate::Sell(deal) => {
          let book = tb.book_by_id(&update.book_id)
            .ok_or_else(|| err("book not initialized"))?;
          println!("new sell {:?}", deal);
          book.new_deal(deal.id, deal.rate, -deal.amount)?;
        },
        RecordUpdate::Buy(deal) => {
          let book = tb.book_by_id(&update.book_id)
            .ok_or_else(|| err("book not initialized"))?;
          println!("new buy {:?}", deal);
          book.new_deal(deal.id, deal.rate, deal.amount)?;
        },
      }
    };
    Ok(())
  }
}

#[cfg(test)]
mod tests {
  use super::Accountant;
  use bus::Bus;
  use ::actors::Actor;
  use ::data::trade::TradeBook;
  use ::data::messages::{BookUpdate, RecordUpdate};
  use std::str::FromStr;
  use std::sync::{Arc, Mutex};
  use std::thread;

  #[test]
  fn initial_order() {
    let tb = Arc::new(Mutex::new(TradeBook::new()));
    let mut actor = Accountant::new(&tb);
    let order = String::from(r#"[189, 5130995, [["i", {"currencyPair": "BTC_BCH", "orderBook": [{"0.13161901": 0.23709568, "0.13164313": "0.17328089"}, {"0.13169621": 0.2331}]}]]]"#);

    let mut bus = Bus::new(1);
    let ch1 = bus.add_rx();
    let th = thread::spawn(move || actor.listen(ch1));
    bus.broadcast(Some(order.clone()));
    bus.broadcast(None);
    println!("{:?}", th.join());

    let mut tb_mut = tb.lock().unwrap();
    let actor_book = tb_mut.book_by_id(&189).unwrap().book_ref();
    match BookUpdate::from_str(&order).unwrap().records[0] {
      RecordUpdate::Initial(ref book) => assert_eq!(*book, *actor_book),
      _ => panic!("BookUpdate::from_str were not able to parse RecordUpdate::Initial")
    }
  }
}<|endoftext|>


<commit_before>// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use std::result::Result;

use RequestError;

/// Wrapper over an HTTP Header type.
#[derive(Debug, Eq, Hash, PartialEq)]
pub enum Header {
    /// Header `Content-Length`.
    ContentLength,
    /// Header `Content-Type`.
    ContentType,
    /// Header `Expect`.
    Expect,
    /// Header `Transfer-Encoding`.
    TransferEncoding,
}

impl Header {
    pub fn raw(&self) -> &'static [u8] {
        match self {
            Header::ContentLength => b"Content-Length",
            Header::ContentType => b"Content-Type",
            Header::Expect => b"Expect",
            Header::TransferEncoding => b"Transfer-Encoding",
        }
    }

    fn try_from(string: &[u8]) -> Result<Self, RequestError> {
        if let Ok(utf8_string) = String::from_utf8(string.to_vec()) {
            match utf8_string.trim() {
                "Content-Length" => Ok(Header::ContentLength),
                "Content-Type" => Ok(Header::ContentType),
                "Expect" => Ok(Header::Expect),
                "Transfer-Encoding" => Ok(Header::TransferEncoding),
                _ => Err(RequestError::InvalidHeader),
            }
        } else {
            Err(RequestError::InvalidRequest)
        }
    }
}

/// Wrapper over the list of headers associated with a Request that we need
/// in order to parse the request correctly and be able to respond to it.
///
/// The only `Content-Type`s supported are `text/plain` and `application/json`, which are both
/// in plain text actually and don't influence our parsing process.
///
/// All the other possible header fields are not necessary in order to serve this connection
/// and, thus, are not of interest to us. However, we still look for header fields that might
/// invalidate our request as we don't support the full set of HTTP/1.1 specification.
/// Such header entries are "Transfer-Encoding: identity; q=0", which means a compression
/// algorithm is applied to the body of the request, or "Expect: 103-checkpoint".
#[derive(Debug)]
pub struct Headers {
    /// The `Content-Length` header field tells us how many bytes we need to receive
    /// from the source after the headers.
    content_length: i32,
    /// The `Expect` header field is set when the headers contain the entry "Expect: 100-continue".
    /// This means that, per HTTP/1.1 specifications, we must send a response with the status code
    /// 100 after we have received the headers in order to receive the body of the request. This
    /// field should be known immediately after parsing the headers.
    expect: bool,
    /// `Chunked` is a possible value of the `Transfer-Encoding` header field and every HTTP/1.1
    /// server must support it. It is useful only when receiving the body of the request and should
    /// be known immediately after parsing the headers.
    chunked: bool,
}

impl Headers {
    /// By default Requests are created with no headers.
    pub fn default() -> Headers {
        Headers {
            content_length: 0,
            expect: false,
            chunked: false,
        }
    }

    /// Expects one header line and parses it, updating the header structure or returning an
    /// error if the header is invalid.
    ///
    /// # Errors
    /// `UnsupportedHeader` is returned when the parsed header line is not of interest
    /// to us or when it is unrecognizable.
    /// `InvalidHeader` is returned when the parsed header is formatted incorrectly or suggests
    /// that the client is using HTTP features that we do not support in this implementation,
    /// which invalidates the request.
    pub fn parse_header_line(&mut self, header_line: &[u8]) -> Result<(), RequestError> {
        // Headers must be ASCII, so also UTF-8 valid.
        match std::str::from_utf8(header_line) {
            Ok(headers_str) => {
                let entry = headers_str.split(": ").collect::<Vec<&str>>();
                if entry.len() != 2 {
                    return Err(RequestError::InvalidHeader);
                }
                if let Ok(head) = Header::try_from(entry[0].as_bytes()) {
                    match head {
                        Header::ContentLength => {
                            let try_numeric: Result<i32, std::num::ParseIntError> =
                                std::str::FromStr::from_str(entry[1].trim());
                            if try_numeric.is_ok() {
                                self.content_length = try_numeric.unwrap();
                                Ok(())
                            } else {
                                Err(RequestError::InvalidHeader)
                            }
                        }
                        Header::ContentType => {
                            match MediaType::try_from(entry[1].trim().as_bytes()) {
                                Ok(_) => Ok(()),
                                Err(_) => Err(RequestError::InvalidHeader),
                            }
                        }
                        Header::TransferEncoding => match entry[1].trim() {
                            "chunked" => {
                                self.chunked = true;
                                Ok(())
                            }
                            "identity; q=0" => Err(RequestError::InvalidHeader),
                            _ => Err(RequestError::UnsupportedHeader),
                        },
                        Header::Expect => match entry[1].trim() {
                            "100-continue" => {
                                self.expect = true;
                                Ok(())
                            }
                            _ => Err(RequestError::InvalidHeader),
                        },
                    }
                } else {
                    Err(RequestError::UnsupportedHeader)
                }
            }
            _ => Err(RequestError::InvalidHeader),
        }
    }

    /// Returns the content length of the body.
    pub fn content_length(&self) -> i32 {
        self.content_length
    }

    /// Returns `true` if the transfer encoding is chunked.
    #[allow(unused)]
    pub fn chunked(&self) -> bool {
        self.chunked
    }

    /// Returns `true` if the client is expecting the code 100.
    #[allow(unused)]
    pub fn expect(&self) -> bool {
        self.expect
    }

    #[cfg(test)]
    pub fn new(content_length: i32, expect: bool, chunked: bool) -> Self {
        Headers {
            content_length,
            expect,
            chunked,
        }
    }

    /// Parses a byte slice into a Headers structure for a HTTP request.
    ///
    /// The byte slice is expected to have the following format: </br>
    ///     * Request Header Lines "<header_line> CRLF"- Optional </br>
    /// There can be any number of request headers, including none, followed by
    /// an extra sequence of Carriage Return and Line Feed.
    /// All header fields are parsed. However, only the ones present in the
    /// [`Headers`](struct.Headers.html) struct are relevant to us and stored
    /// for future use.
    ///
    /// # Errors
    /// The function returns `InvalidHeader` when parsing the byte stream fails.
    ///
    /// # Examples
    ///
    /// ```
    /// extern crate micro_http;
    /// use micro_http::Headers;
    ///
    /// let request_headers = Headers::try_from(b"Content-Length: 55\r\n\r\n");
    /// ```
    pub fn try_from(bytes: &[u8]) -> Result<Headers, RequestError> {
        // Headers must be ASCII, so also UTF-8 valid.
        if let Ok(text) = std::str::from_utf8(bytes) {
            let mut headers = Headers::default();

            let header_lines = text.split("\r\n");
            for header_line in header_lines {
                if header_line.is_empty() {
                    break;
                }
                match headers.parse_header_line(header_line.as_bytes()) {
                    Ok(_) | Err(RequestError::UnsupportedHeader) => continue,
                    Err(e) => return Err(e),
                };
            }
            return Ok(headers);
        }
        Err(RequestError::InvalidRequest)
    }
}

/// Wrapper over supported Media Types.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum MediaType {
    /// Media Type: "text/plain".
    PlainText,
    /// Media Type: "application/json".
    ApplicationJson,
}

impl Default for MediaType {
    fn default() -> Self {
        MediaType::PlainText
    }
}

impl MediaType {
    fn try_from(bytes: &[u8]) -> Result<Self, RequestError> {
        if bytes.is_empty() {
            return Err(RequestError::InvalidRequest);
        }
        let utf8_slice =
            String::from_utf8(bytes.to_vec()).map_err(|_| RequestError::InvalidRequest)?;
        match utf8_slice.as_str() {
            "text/plain" => Ok(MediaType::PlainText),
            "application/json" => Ok(MediaType::ApplicationJson),
            _ => Err(RequestError::InvalidRequest),
        }
    }

    /// Returns a static string representation of the object.
    pub fn as_str(self) -> &'static str {
        match self {
            MediaType::PlainText => "text/plain",
            MediaType::ApplicationJson => "application/json",
        }
    }
}

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

    #[test]
    fn test_default() {
        let headers = Headers::default();
        assert_eq!(headers.content_length(), 0);
        assert_eq!(headers.chunked(), false);
        assert_eq!(headers.expect(), false);
    }

    #[test]
    fn test_try_from_media() {
        assert_eq!(
            MediaType::try_from(b"application/json").unwrap(),
            MediaType::ApplicationJson
        );

        assert_eq!(
            MediaType::try_from(b"text/plain").unwrap(),
            MediaType::PlainText
        );

        assert_eq!(
            MediaType::try_from(b"").unwrap_err(),
            RequestError::InvalidRequest
        );

        assert_eq!(
            MediaType::try_from(b"application/json-patch").unwrap_err(),
            RequestError::InvalidRequest
        );
    }

    #[test]
    fn test_media_as_str() {
        let media_type = MediaType::ApplicationJson;
        assert_eq!(media_type.as_str(), "application/json");

        let media_type = MediaType::PlainText;
        assert_eq!(media_type.as_str(), "text/plain");
    }

    #[test]
    fn test_try_from_headers() {
        // Valid headers.
        assert_eq!(
            Headers::try_from(
                b"Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT\r\nContent-Length: 55\r\n\r\n"
            )
            .unwrap()
            .content_length,
            55
        );

        let bytes: [u8; 10] = [130, 140, 150, 130, 140, 150, 130, 140, 150, 160];
        // Invalid headers.
        assert!(Headers::try_from(&bytes[..]).is_err());
    }

    #[test]
    fn test_parse_header_line() {
        let mut header = Headers::default();

        // Invalid header syntax.
        assert_eq!(
            header.parse_header_line(b"Expect"),
            Err(RequestError::InvalidHeader)
        );

        // Invalid content length.
        assert_eq!(
            header.parse_header_line(b"Content-Length: five"),
            Err(RequestError::InvalidHeader)
        );

        // Invalid transfer encoding.
        assert_eq!(
            header.parse_header_line(b"Transfer-Encoding: gzip"),
            Err(RequestError::UnsupportedHeader)
        );

        // Invalid expect.
        assert_eq!(
            header
                .parse_header_line(b"Expect: 102-processing")
                .unwrap_err(),
            RequestError::InvalidHeader
        );

        // Invalid media type.
        assert_eq!(
            header
                .parse_header_line(b"Content-Type: application/json-patch")
                .unwrap_err(),
            RequestError::InvalidHeader
        );

        // Invalid input format.
        let input: [u8; 10] = [130, 140, 150, 130, 140, 150, 130, 140, 150, 160];
        assert_eq!(
            header.parse_header_line(&input[..]).unwrap_err(),
            RequestError::InvalidHeader
        );

        // Test valid transfer encoding.
        assert!(header
            .parse_header_line(b"Transfer-Encoding: chunked")
            .is_ok());
        assert!(header.chunked());

        // Test valid expect.
        assert!(header.parse_header_line(b"Expect: 100-continue").is_ok());
        assert!(header.expect());

        // Test valid media type.
        assert!(header
            .parse_header_line(b"Content-Type: application/json")
            .is_ok());
    }

    #[test]
    fn test_header_try_from() {
        // Bad header.
        assert_eq!(
            Header::try_from(b"Encoding").unwrap_err(),
            RequestError::InvalidHeader
        );

        // Invalid encoding.
        let input: [u8; 10] = [130, 140, 150, 130, 140, 150, 130, 140, 150, 160];
        assert_eq!(
            Header::try_from(&input[..]).unwrap_err(),
            RequestError::InvalidRequest
        );

        // Test valid headers.
        let header = Header::try_from(b"Expect").unwrap();
        assert_eq!(header.raw(), b"Expect");

        let header = Header::try_from(b"Transfer-Encoding").unwrap();
        assert_eq!(header.raw(), b"Transfer-Encoding");
    }
}
<commit_msg>micro_http: Fix clippy warning<commit_after>// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use std::result::Result;

use RequestError;

/// Wrapper over an HTTP Header type.
#[derive(Debug, Eq, Hash, PartialEq)]
pub enum Header {
    /// Header `Content-Length`.
    ContentLength,
    /// Header `Content-Type`.
    ContentType,
    /// Header `Expect`.
    Expect,
    /// Header `Transfer-Encoding`.
    TransferEncoding,
}

impl Header {
    pub fn raw(&self) -> &'static [u8] {
        match self {
            Header::ContentLength => b"Content-Length",
            Header::ContentType => b"Content-Type",
            Header::Expect => b"Expect",
            Header::TransferEncoding => b"Transfer-Encoding",
        }
    }

    fn try_from(string: &[u8]) -> Result<Self, RequestError> {
        if let Ok(utf8_string) = String::from_utf8(string.to_vec()) {
            match utf8_string.trim() {
                "Content-Length" => Ok(Header::ContentLength),
                "Content-Type" => Ok(Header::ContentType),
                "Expect" => Ok(Header::Expect),
                "Transfer-Encoding" => Ok(Header::TransferEncoding),
                _ => Err(RequestError::InvalidHeader),
            }
        } else {
            Err(RequestError::InvalidRequest)
        }
    }
}

/// Wrapper over the list of headers associated with a Request that we need
/// in order to parse the request correctly and be able to respond to it.
///
/// The only `Content-Type`s supported are `text/plain` and `application/json`, which are both
/// in plain text actually and don't influence our parsing process.
///
/// All the other possible header fields are not necessary in order to serve this connection
/// and, thus, are not of interest to us. However, we still look for header fields that might
/// invalidate our request as we don't support the full set of HTTP/1.1 specification.
/// Such header entries are "Transfer-Encoding: identity; q=0", which means a compression
/// algorithm is applied to the body of the request, or "Expect: 103-checkpoint".
#[derive(Debug)]
pub struct Headers {
    /// The `Content-Length` header field tells us how many bytes we need to receive
    /// from the source after the headers.
    content_length: i32,
    /// The `Expect` header field is set when the headers contain the entry "Expect: 100-continue".
    /// This means that, per HTTP/1.1 specifications, we must send a response with the status code
    /// 100 after we have received the headers in order to receive the body of the request. This
    /// field should be known immediately after parsing the headers.
    expect: bool,
    /// `Chunked` is a possible value of the `Transfer-Encoding` header field and every HTTP/1.1
    /// server must support it. It is useful only when receiving the body of the request and should
    /// be known immediately after parsing the headers.
    chunked: bool,
}

impl Headers {
    /// By default Requests are created with no headers.
    pub fn default() -> Headers {
        Headers {
            content_length: 0,
            expect: false,
            chunked: false,
        }
    }

    /// Expects one header line and parses it, updating the header structure or returning an
    /// error if the header is invalid.
    ///
    /// # Errors
    /// `UnsupportedHeader` is returned when the parsed header line is not of interest
    /// to us or when it is unrecognizable.
    /// `InvalidHeader` is returned when the parsed header is formatted incorrectly or suggests
    /// that the client is using HTTP features that we do not support in this implementation,
    /// which invalidates the request.
    pub fn parse_header_line(&mut self, header_line: &[u8]) -> Result<(), RequestError> {
        // Headers must be ASCII, so also UTF-8 valid.
        match std::str::from_utf8(header_line) {
            Ok(headers_str) => {
                let entry = headers_str.split(": ").collect::<Vec<&str>>();
                if entry.len() != 2 {
                    return Err(RequestError::InvalidHeader);
                }
                if let Ok(head) = Header::try_from(entry[0].as_bytes()) {
                    match head {
                        Header::ContentLength => {
                            let try_numeric: Result<i32, std::num::ParseIntError> =
                                std::str::FromStr::from_str(entry[1].trim());
                            if let Ok(content_length) = try_numeric {
                                self.content_length = content_length;
                                Ok(())
                            } else {
                                Err(RequestError::InvalidHeader)
                            }
                        }
                        Header::ContentType => {
                            match MediaType::try_from(entry[1].trim().as_bytes()) {
                                Ok(_) => Ok(()),
                                Err(_) => Err(RequestError::InvalidHeader),
                            }
                        }
                        Header::TransferEncoding => match entry[1].trim() {
                            "chunked" => {
                                self.chunked = true;
                                Ok(())
                            }
                            "identity; q=0" => Err(RequestError::InvalidHeader),
                            _ => Err(RequestError::UnsupportedHeader),
                        },
                        Header::Expect => match entry[1].trim() {
                            "100-continue" => {
                                self.expect = true;
                                Ok(())
                            }
                            _ => Err(RequestError::InvalidHeader),
                        },
                    }
                } else {
                    Err(RequestError::UnsupportedHeader)
                }
            }
            _ => Err(RequestError::InvalidHeader),
        }
    }

    /// Returns the content length of the body.
    pub fn content_length(&self) -> i32 {
        self.content_length
    }

    /// Returns `true` if the transfer encoding is chunked.
    #[allow(unused)]
    pub fn chunked(&self) -> bool {
        self.chunked
    }

    /// Returns `true` if the client is expecting the code 100.
    #[allow(unused)]
    pub fn expect(&self) -> bool {
        self.expect
    }

    #[cfg(test)]
    pub fn new(content_length: i32, expect: bool, chunked: bool) -> Self {
        Headers {
            content_length,
            expect,
            chunked,
        }
    }

    /// Parses a byte slice into a Headers structure for a HTTP request.
    ///
    /// The byte slice is expected to have the following format: </br>
    ///     * Request Header Lines "<header_line> CRLF"- Optional </br>
    /// There can be any number of request headers, including none, followed by
    /// an extra sequence of Carriage Return and Line Feed.
    /// All header fields are parsed. However, only the ones present in the
    /// [`Headers`](struct.Headers.html) struct are relevant to us and stored
    /// for future use.
    ///
    /// # Errors
    /// The function returns `InvalidHeader` when parsing the byte stream fails.
    ///
    /// # Examples
    ///
    /// ```
    /// extern crate micro_http;
    /// use micro_http::Headers;
    ///
    /// let request_headers = Headers::try_from(b"Content-Length: 55\r\n\r\n");
    /// ```
    pub fn try_from(bytes: &[u8]) -> Result<Headers, RequestError> {
        // Headers must be ASCII, so also UTF-8 valid.
        if let Ok(text) = std::str::from_utf8(bytes) {
            let mut headers = Headers::default();

            let header_lines = text.split("\r\n");
            for header_line in header_lines {
                if header_line.is_empty() {
                    break;
                }
                match headers.parse_header_line(header_line.as_bytes()) {
                    Ok(_) | Err(RequestError::UnsupportedHeader) => continue,
                    Err(e) => return Err(e),
                };
            }
            return Ok(headers);
        }
        Err(RequestError::InvalidRequest)
    }
}

/// Wrapper over supported Media Types.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum MediaType {
    /// Media Type: "text/plain".
    PlainText,
    /// Media Type: "application/json".
    ApplicationJson,
}

impl Default for MediaType {
    fn default() -> Self {
        MediaType::PlainText
    }
}

impl MediaType {
    fn try_from(bytes: &[u8]) -> Result<Self, RequestError> {
        if bytes.is_empty() {
            return Err(RequestError::InvalidRequest);
        }
        let utf8_slice =
            String::from_utf8(bytes.to_vec()).map_err(|_| RequestError::InvalidRequest)?;
        match utf8_slice.as_str() {
            "text/plain" => Ok(MediaType::PlainText),
            "application/json" => Ok(MediaType::ApplicationJson),
            _ => Err(RequestError::InvalidRequest),
        }
    }

    /// Returns a static string representation of the object.
    pub fn as_str(self) -> &'static str {
        match self {
            MediaType::PlainText => "text/plain",
            MediaType::ApplicationJson => "application/json",
        }
    }
}

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

    #[test]
    fn test_default() {
        let headers = Headers::default();
        assert_eq!(headers.content_length(), 0);
        assert_eq!(headers.chunked(), false);
        assert_eq!(headers.expect(), false);
    }

    #[test]
    fn test_try_from_media() {
        assert_eq!(
            MediaType::try_from(b"application/json").unwrap(),
            MediaType::ApplicationJson
        );

        assert_eq!(
            MediaType::try_from(b"text/plain").unwrap(),
            MediaType::PlainText
        );

        assert_eq!(
            MediaType::try_from(b"").unwrap_err(),
            RequestError::InvalidRequest
        );

        assert_eq!(
            MediaType::try_from(b"application/json-patch").unwrap_err(),
            RequestError::InvalidRequest
        );
    }

    #[test]
    fn test_media_as_str() {
        let media_type = MediaType::ApplicationJson;
        assert_eq!(media_type.as_str(), "application/json");

        let media_type = MediaType::PlainText;
        assert_eq!(media_type.as_str(), "text/plain");
    }

    #[test]
    fn test_try_from_headers() {
        // Valid headers.
        assert_eq!(
            Headers::try_from(
                b"Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT\r\nContent-Length: 55\r\n\r\n"
            )
            .unwrap()
            .content_length,
            55
        );

        let bytes: [u8; 10] = [130, 140, 150, 130, 140, 150, 130, 140, 150, 160];
        // Invalid headers.
        assert!(Headers::try_from(&bytes[..]).is_err());
    }

    #[test]
    fn test_parse_header_line() {
        let mut header = Headers::default();

        // Invalid header syntax.
        assert_eq!(
            header.parse_header_line(b"Expect"),
            Err(RequestError::InvalidHeader)
        );

        // Invalid content length.
        assert_eq!(
            header.parse_header_line(b"Content-Length: five"),
            Err(RequestError::InvalidHeader)
        );

        // Invalid transfer encoding.
        assert_eq!(
            header.parse_header_line(b"Transfer-Encoding: gzip"),
            Err(RequestError::UnsupportedHeader)
        );

        // Invalid expect.
        assert_eq!(
            header
                .parse_header_line(b"Expect: 102-processing")
                .unwrap_err(),
            RequestError::InvalidHeader
        );

        // Invalid media type.
        assert_eq!(
            header
                .parse_header_line(b"Content-Type: application/json-patch")
                .unwrap_err(),
            RequestError::InvalidHeader
        );

        // Invalid input format.
        let input: [u8; 10] = [130, 140, 150, 130, 140, 150, 130, 140, 150, 160];
        assert_eq!(
            header.parse_header_line(&input[..]).unwrap_err(),
            RequestError::InvalidHeader
        );

        // Test valid transfer encoding.
        assert!(header
            .parse_header_line(b"Transfer-Encoding: chunked")
            .is_ok());
        assert!(header.chunked());

        // Test valid expect.
        assert!(header.parse_header_line(b"Expect: 100-continue").is_ok());
        assert!(header.expect());

        // Test valid media type.
        assert!(header
            .parse_header_line(b"Content-Type: application/json")
            .is_ok());
    }

    #[test]
    fn test_header_try_from() {
        // Bad header.
        assert_eq!(
            Header::try_from(b"Encoding").unwrap_err(),
            RequestError::InvalidHeader
        );

        // Invalid encoding.
        let input: [u8; 10] = [130, 140, 150, 130, 140, 150, 130, 140, 150, 160];
        assert_eq!(
            Header::try_from(&input[..]).unwrap_err(),
            RequestError::InvalidRequest
        );

        // Test valid headers.
        let header = Header::try_from(b"Expect").unwrap();
        assert_eq!(header.raw(), b"Expect");

        let header = Header::try_from(b"Transfer-Encoding").unwrap();
        assert_eq!(header.raw(), b"Transfer-Encoding");
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::mem;
use std::iter::Empty;
use std::ops::Range;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::Weak;

use command_buffer::Submission;
use device::Device;
use format::FormatDesc;
use format::FormatTy;
use image::sys::Dimensions;
use image::sys::Layout;
use image::sys::UnsafeImage;
use image::sys::UnsafeImageView;
use image::sys::Usage;
use image::traits::AccessRange;
use image::traits::GpuAccessResult;
use image::traits::Image;
use image::traits::ImageContent;
use image::traits::ImageView;
use image::traits::Transition;
use memory::DeviceMemory;
use sync::Sharing;

use OomError;

pub struct AttachmentImage<F> {
    image: UnsafeImage,
    view: UnsafeImageView,
    memory: DeviceMemory,
    format: F,

    // Layout to use when the image is used as a framebuffer attachment.
    // Should be either "depth-stencil optimal" or "color optimal".
    attachment_layout: Layout,

    guarded: Mutex<Guarded>,
}

struct Guarded {
    correct_layout: bool,
    latest_submission: Option<Weak<Submission>>,    // TODO: can use `Weak::new()` once it's stabilized
}

impl<F> AttachmentImage<F> {
    pub fn new(device: &Arc<Device>, dimensions: [u32; 2], format: F)
               -> Result<Arc<AttachmentImage<F>>, OomError>
        where F: FormatDesc
    {
        let is_depth = match format.format().ty() {
            FormatTy::Depth => true,
            FormatTy::DepthStencil => true,
            FormatTy::Compressed => panic!(),
            _ => false
        };

        let usage = Usage {
            transfer_source: true,
            sampled: true,
            color_attachment: !is_depth,
            depth_stencil_attachment: is_depth,
            input_attachment: true,
            .. Usage::none()
        };

        let (image, mem_reqs) = unsafe {
            try!(UnsafeImage::new(device, &usage, format.format(),
                                  Dimensions::Dim2d { width: dimensions[0], height: dimensions[1] },
                                  1, 1, Sharing::Exclusive::<Empty<u32>>, false, false))
        };

        let mem_ty = {
            let device_local = device.physical_device().memory_types()
                                     .filter(|t| (mem_reqs.memory_type_bits & (1 << t.id())) != 0)
                                     .filter(|t| t.is_device_local());
            let any = device.physical_device().memory_types()
                            .filter(|t| (mem_reqs.memory_type_bits & (1 << t.id())) != 0);
            device_local.chain(any).next().unwrap()
        };

        // note: alignment doesn't need to be checked because allocating memory is guaranteed to
        //       fulfill any alignment requirement

        let mem = try!(DeviceMemory::alloc(device, &mem_ty, mem_reqs.size));
        unsafe { try!(image.bind_memory(&mem, 0)); }

        let view = unsafe {
            try!(UnsafeImageView::new(&image))
        };

        Ok(Arc::new(AttachmentImage {
            image: image,
            view: view,
            memory: mem,
            format: format,
            attachment_layout: if is_depth { Layout::DepthStencilAttachmentOptimal }
                               else { Layout::ColorAttachmentOptimal },
            guarded: Mutex::new(Guarded {
                correct_layout: false,
                latest_submission: None,
            }),
        }))
    }

    #[inline]
    pub fn dimensions(&self) -> Dimensions {
        self.image.dimensions()
    }
}

unsafe impl<F> Image for AttachmentImage<F> {
    #[inline]
    fn inner_image(&self) -> &UnsafeImage {
        &self.image
    }

    #[inline]
    fn blocks(&self, _: Range<u32>, _: Range<u32>) -> Vec<(u32, u32)> {
        vec![(0, 0)]
    }

    #[inline]
    fn block_mipmap_levels_range(&self, block: (u32, u32)) -> Range<u32> {
        0 .. 1
    }

    #[inline]
    fn block_array_layers_range(&self, block: (u32, u32)) -> Range<u32> {
        0 .. 1
    }

    #[inline]
    fn initial_layout(&self, _: (u32, u32), _: Layout) -> (Layout, bool, bool) {
        (self.attachment_layout, false, false)
    }

    #[inline]
    fn final_layout(&self, _: (u32, u32), _: Layout) -> (Layout, bool, bool) {
        (self.attachment_layout, false, false)
    }

    fn needs_fence(&self, access: &mut Iterator<Item = AccessRange>) -> Option<bool> {
        Some(false)
    }

    unsafe fn gpu_access(&self, _: &mut Iterator<Item = AccessRange>,
                         submission: &Arc<Submission>) -> GpuAccessResult
    {
        let mut guarded = self.guarded.lock().unwrap();

        let dependency = mem::replace(&mut guarded.latest_submission, Some(Arc::downgrade(submission)));
        let dependency = dependency.and_then(|d| d.upgrade());

        let transition = if !guarded.correct_layout {
            vec![Transition {
                block: (0, 0),
                from: Layout::Undefined,
                to: self.attachment_layout,
            }]
        } else {
            vec![]
        };

        guarded.correct_layout = true;

        GpuAccessResult {
            dependencies: if let Some(dependency) = dependency {
                vec![dependency]
            } else {
                vec![]
            },
            additional_wait_semaphore: None,
            additional_signal_semaphore: None,
            before_transitions: transition,
            after_transitions: vec![],
        }
    }
}

unsafe impl<P, F> ImageContent<P> for AttachmentImage<F> {
    #[inline]
    fn matches_format(&self) -> bool {
        true        // FIXME:
    }
}

unsafe impl<F: 'static> ImageView for AttachmentImage<F> {
    #[inline]
    fn parent(&self) -> &Image {
        self
    }

    #[inline]
    fn parent_arc(me: &Arc<Self>) -> Arc<Image> where Self: Sized {
        me.clone() as Arc<_>
    }

    #[inline]
    fn blocks(&self) -> Vec<(u32, u32)> {
        vec![(0, 0)]
    }

    #[inline]
    fn inner_view(&self) -> &UnsafeImageView {
        &self.view
    }

    #[inline]
    fn descriptor_set_storage_image_layout(&self) -> Layout {
        Layout::ColorAttachmentOptimal
    }

    #[inline]
    fn descriptor_set_combined_image_sampler_layout(&self) -> Layout {
        Layout::ColorAttachmentOptimal
    }

    #[inline]
    fn descriptor_set_sampled_image_layout(&self) -> Layout {
        Layout::ColorAttachmentOptimal
    }

    #[inline]
    fn descriptor_set_input_attachment_layout(&self) -> Layout {
        Layout::ColorAttachmentOptimal
    }

    #[inline]
    fn identity_swizzle(&self) -> bool {
        true
    }
}
<commit_msg>Implement ImageClearValue for AttachmentImage<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::mem;
use std::iter::Empty;
use std::ops::Range;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::Weak;

use command_buffer::Submission;
use device::Device;
use format::ClearValue;
use format::FormatDesc;
use format::FormatTy;
use image::sys::Dimensions;
use image::sys::Layout;
use image::sys::UnsafeImage;
use image::sys::UnsafeImageView;
use image::sys::Usage;
use image::traits::AccessRange;
use image::traits::GpuAccessResult;
use image::traits::Image;
use image::traits::ImageClearValue;
use image::traits::ImageContent;
use image::traits::ImageView;
use image::traits::Transition;
use memory::DeviceMemory;
use sync::Sharing;

use OomError;

pub struct AttachmentImage<F> {
    image: UnsafeImage,
    view: UnsafeImageView,
    memory: DeviceMemory,
    format: F,

    // Layout to use when the image is used as a framebuffer attachment.
    // Should be either "depth-stencil optimal" or "color optimal".
    attachment_layout: Layout,

    guarded: Mutex<Guarded>,
}

struct Guarded {
    correct_layout: bool,
    latest_submission: Option<Weak<Submission>>,    // TODO: can use `Weak::new()` once it's stabilized
}

impl<F> AttachmentImage<F> {
    pub fn new(device: &Arc<Device>, dimensions: [u32; 2], format: F)
               -> Result<Arc<AttachmentImage<F>>, OomError>
        where F: FormatDesc
    {
        let is_depth = match format.format().ty() {
            FormatTy::Depth => true,
            FormatTy::DepthStencil => true,
            FormatTy::Compressed => panic!(),
            _ => false
        };

        let usage = Usage {
            transfer_source: true,
            sampled: true,
            color_attachment: !is_depth,
            depth_stencil_attachment: is_depth,
            input_attachment: true,
            .. Usage::none()
        };

        let (image, mem_reqs) = unsafe {
            try!(UnsafeImage::new(device, &usage, format.format(),
                                  Dimensions::Dim2d { width: dimensions[0], height: dimensions[1] },
                                  1, 1, Sharing::Exclusive::<Empty<u32>>, false, false))
        };

        let mem_ty = {
            let device_local = device.physical_device().memory_types()
                                     .filter(|t| (mem_reqs.memory_type_bits & (1 << t.id())) != 0)
                                     .filter(|t| t.is_device_local());
            let any = device.physical_device().memory_types()
                            .filter(|t| (mem_reqs.memory_type_bits & (1 << t.id())) != 0);
            device_local.chain(any).next().unwrap()
        };

        // note: alignment doesn't need to be checked because allocating memory is guaranteed to
        //       fulfill any alignment requirement

        let mem = try!(DeviceMemory::alloc(device, &mem_ty, mem_reqs.size));
        unsafe { try!(image.bind_memory(&mem, 0)); }

        let view = unsafe {
            try!(UnsafeImageView::new(&image))
        };

        Ok(Arc::new(AttachmentImage {
            image: image,
            view: view,
            memory: mem,
            format: format,
            attachment_layout: if is_depth { Layout::DepthStencilAttachmentOptimal }
                               else { Layout::ColorAttachmentOptimal },
            guarded: Mutex::new(Guarded {
                correct_layout: false,
                latest_submission: None,
            }),
        }))
    }

    #[inline]
    pub fn dimensions(&self) -> Dimensions {
        self.image.dimensions()
    }
}

unsafe impl<F> Image for AttachmentImage<F> {
    #[inline]
    fn inner_image(&self) -> &UnsafeImage {
        &self.image
    }

    #[inline]
    fn blocks(&self, _: Range<u32>, _: Range<u32>) -> Vec<(u32, u32)> {
        vec![(0, 0)]
    }

    #[inline]
    fn block_mipmap_levels_range(&self, block: (u32, u32)) -> Range<u32> {
        0 .. 1
    }

    #[inline]
    fn block_array_layers_range(&self, block: (u32, u32)) -> Range<u32> {
        0 .. 1
    }

    #[inline]
    fn initial_layout(&self, _: (u32, u32), _: Layout) -> (Layout, bool, bool) {
        (self.attachment_layout, false, false)
    }

    #[inline]
    fn final_layout(&self, _: (u32, u32), _: Layout) -> (Layout, bool, bool) {
        (self.attachment_layout, false, false)
    }

    fn needs_fence(&self, access: &mut Iterator<Item = AccessRange>) -> Option<bool> {
        Some(false)
    }

    unsafe fn gpu_access(&self, _: &mut Iterator<Item = AccessRange>,
                         submission: &Arc<Submission>) -> GpuAccessResult
    {
        let mut guarded = self.guarded.lock().unwrap();

        let dependency = mem::replace(&mut guarded.latest_submission, Some(Arc::downgrade(submission)));
        let dependency = dependency.and_then(|d| d.upgrade());

        let transition = if !guarded.correct_layout {
            vec![Transition {
                block: (0, 0),
                from: Layout::Undefined,
                to: self.attachment_layout,
            }]
        } else {
            vec![]
        };

        guarded.correct_layout = true;

        GpuAccessResult {
            dependencies: if let Some(dependency) = dependency {
                vec![dependency]
            } else {
                vec![]
            },
            additional_wait_semaphore: None,
            additional_signal_semaphore: None,
            before_transitions: transition,
            after_transitions: vec![],
        }
    }
}

unsafe impl<F> ImageClearValue<F::ClearValue> for AttachmentImage<F> where F: FormatDesc {
    #[inline]
    fn decode(&self, value: F::ClearValue) -> Option<ClearValue> {
        Some(self.format.decode_clear_value(value))
    }
}

unsafe impl<P, F> ImageContent<P> for AttachmentImage<F> {
    #[inline]
    fn matches_format(&self) -> bool {
        true        // FIXME:
    }
}

unsafe impl<F: 'static> ImageView for AttachmentImage<F> {
    #[inline]
    fn parent(&self) -> &Image {
        self
    }

    #[inline]
    fn parent_arc(me: &Arc<Self>) -> Arc<Image> where Self: Sized {
        me.clone() as Arc<_>
    }

    #[inline]
    fn blocks(&self) -> Vec<(u32, u32)> {
        vec![(0, 0)]
    }

    #[inline]
    fn inner_view(&self) -> &UnsafeImageView {
        &self.view
    }

    #[inline]
    fn descriptor_set_storage_image_layout(&self) -> Layout {
        Layout::ColorAttachmentOptimal
    }

    #[inline]
    fn descriptor_set_combined_image_sampler_layout(&self) -> Layout {
        Layout::ColorAttachmentOptimal
    }

    #[inline]
    fn descriptor_set_sampled_image_layout(&self) -> Layout {
        Layout::ColorAttachmentOptimal
    }

    #[inline]
    fn descriptor_set_input_attachment_layout(&self) -> Layout {
        Layout::ColorAttachmentOptimal
    }

    #[inline]
    fn identity_swizzle(&self) -> bool {
        true
    }
}
<|endoftext|>


<commit_before>//! Modules for events in the *m.key.verification* namespace.
//!
//! This module also contains types shared by events in its child namespaces.

use serde::{Deserialize, Serialize};
use strum::{Display, EnumString};

pub mod accept;
pub mod cancel;
pub mod key;
pub mod mac;
pub mod request;
pub mod start;

/// A hash algorithm.
#[derive(Clone, Copy, Debug, PartialEq, Display, EnumString, Serialize, Deserialize)]
#[non_exhaustive]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum HashAlgorithm {
    /// The SHA256 hash algorithm.
    Sha256,
}

/// A key agreement protocol.
#[derive(Clone, Copy, Debug, PartialEq, Display, EnumString, Serialize, Deserialize)]
#[non_exhaustive]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum KeyAgreementProtocol {
    /// The [Curve25519](https://cr.yp.to/ecdh.html) key agreement protocol.
    Curve25519,
}

/// A message authentication code algorithm.
#[derive(Clone, Copy, Debug, PartialEq, Display, EnumString, Serialize, Deserialize)]
#[non_exhaustive]
#[serde(rename_all = "kebab-case")]
#[strum(serialize_all = "kebab-case")]
pub enum MessageAuthenticationCode {
    /// The HKDF-HMAC-SHA256 MAC.
    HkdfHmacSha256,
}

/// A Short Authentication String method.
#[derive(Clone, Copy, Debug, PartialEq, Display, EnumString, Serialize, Deserialize)]
#[non_exhaustive]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum ShortAuthenticationString {
    /// The decimal method.
    Decimal,

    /// The emoji method.
    Emoji,
}

/// A Short Authentication String (SAS) verification method.
#[derive(Clone, Copy, Debug, PartialEq, Display, EnumString, Deserialize, Serialize)]
#[non_exhaustive]
pub enum VerificationMethod {
    /// The *m.sas.v1* verification method.
    #[serde(rename = "m.sas.v1")]
    #[strum(serialize = "m.sas.v1")]
    MSasV1,
}
<commit_msg>ruma-events: Add the new key agreement method.<commit_after>//! Modules for events in the *m.key.verification* namespace.
//!
//! This module also contains types shared by events in its child namespaces.

use serde::{Deserialize, Serialize};
use strum::{Display, EnumString};

pub mod accept;
pub mod cancel;
pub mod key;
pub mod mac;
pub mod request;
pub mod start;

/// A hash algorithm.
#[derive(Clone, Copy, Debug, PartialEq, Display, EnumString, Serialize, Deserialize)]
#[non_exhaustive]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum HashAlgorithm {
    /// The SHA256 hash algorithm.
    Sha256,
}

/// A key agreement protocol.
#[derive(Clone, Copy, Debug, PartialEq, Display, EnumString, Serialize, Deserialize)]
#[non_exhaustive]
#[serde(rename_all = "kebab-case")]
#[strum(serialize_all = "kebab-case")]
pub enum KeyAgreementProtocol {
    /// The [Curve25519](https://cr.yp.to/ecdh.html) key agreement protocol.
    Curve25519,
    /// The Curve25519 key agreement protocol with check for public keys.
    Curve25519HkdfSha256,
}

/// A message authentication code algorithm.
#[derive(Clone, Copy, Debug, PartialEq, Display, EnumString, Serialize, Deserialize)]
#[non_exhaustive]
#[serde(rename_all = "kebab-case")]
#[strum(serialize_all = "kebab-case")]
pub enum MessageAuthenticationCode {
    /// The HKDF-HMAC-SHA256 MAC.
    HkdfHmacSha256,
}

/// A Short Authentication String method.
#[derive(Clone, Copy, Debug, PartialEq, Display, EnumString, Serialize, Deserialize)]
#[non_exhaustive]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum ShortAuthenticationString {
    /// The decimal method.
    Decimal,

    /// The emoji method.
    Emoji,
}

/// A Short Authentication String (SAS) verification method.
#[derive(Clone, Copy, Debug, PartialEq, Display, EnumString, Deserialize, Serialize)]
#[non_exhaustive]
pub enum VerificationMethod {
    /// The *m.sas.v1* verification method.
    #[serde(rename = "m.sas.v1")]
    #[strum(serialize = "m.sas.v1")]
    MSasV1,
}
<|endoftext|>


<commit_before>// This build script checks that all files in the `src` directory have lines of at most 100
// characters and don't contain trailing whitespace.
//
// It also ensures that comment indicating which task a file solves is at the top.
//
// In case we find a line that doesn't comply with this rules, the build will fail and indicate
// the cause of the problem.
extern crate regex;
extern crate toml;

use std::fs::{self, File, metadata};
use std::io::Read;
use std::path::Path;

use regex::Regex;

fn check_toml() {
    let mut cargo_toml_file = File::open("Cargo.toml").unwrap();
    let mut cargo_toml_string = String::new();
    cargo_toml_file.read_to_string(&mut cargo_toml_string).unwrap();

    let cargo_toml = toml::Parser::new(&cargo_toml_string).parse().unwrap();
    let binaries = cargo_toml.get("bin").unwrap().as_slice().unwrap().to_owned();
    let mut sorted_binaries = binaries.clone();
    sorted_binaries.sort_by(|a, b| {
        let a_name = a.as_table().unwrap().get("name").unwrap().as_str().unwrap();
        let b_name = b.as_table().unwrap().get("name").unwrap().as_str().unwrap();
        a_name.cmp(b_name)
    });

    for (bin, correct_bin) in binaries.iter().zip(sorted_binaries) {
        let bin_name = bin.as_table().unwrap().get("name").unwrap().as_str().unwrap();
        let correct_bin_name = correct_bin.as_table().unwrap().get("name").unwrap().as_str().unwrap();
        if bin_name != correct_bin_name {
            panic!("{} is not in the correct order in Cargo.toml!", bin_name);
        }
    }
}

fn main() {
    check_toml();

    let files = fs::read_dir("src").unwrap()
                                   .map(|e| e.unwrap());

    for f in files {
        let path = f.path();
        if metadata(&path).unwrap().is_file() {
            check(&path);
        }
    }
}

fn check(path: &Path) {
    let mut content = String::new();
    File::open(&path).unwrap().read_to_string(&mut content).unwrap();

    for (i, mut line) in content.lines().enumerate() {
        if i == 0 && path.file_name().unwrap() != "lib.rs" {
            // Ensure the first line has a URL of the proper form
            let task_comment = Regex::new(r"// http://rosettacode\.org/wiki/.+").unwrap();
            if !task_comment.is_match(line) {
                line_error(i + 1, path, "file does not start with \
                           \"// http://rosettacode.org/wiki/<TASK NAME>\"");
            }
        }

        // Ignore '\r'
        if let Some('\r') = line.chars().rev().next() {
            line = &line[..line.len() - 1];
        }

        // Check length
        if line.len() > 100 {
            line_error(i + 1, path, "line is longer than 100 characters");
        }

        // Check trailing whitespace
        if let Some(last_char) = line.chars().rev().next() {
            if last_char.is_whitespace() {
                line_error(i + 1, path, "line has trailing whitespace");
            }
        }
    }
}

fn line_error(line: usize, path: &Path, msg: &str) {
    panic!("Formatting error, {} (line {} of file \"{}\")",
           msg, line, path.to_str().unwrap())
}
<commit_msg>format build.rs<commit_after>// This build script checks that all files in the `src` directory have lines of at most 100
// characters and don't contain trailing whitespace.
//
// It also ensures that comment indicating which task a file solves is at the top.
//
// In case we find a line that doesn't comply with this rules, the build will fail and indicate
// the cause of the problem.
extern crate regex;
extern crate toml;

use std::fs::{self, File, metadata};
use std::io::Read;
use std::path::Path;

use regex::Regex;

fn check_toml() {
    let mut cargo_toml_file = File::open("Cargo.toml").unwrap();
    let mut cargo_toml_string = String::new();
    cargo_toml_file.read_to_string(&mut cargo_toml_string).unwrap();

    let cargo_toml = toml::Parser::new(&cargo_toml_string).parse().unwrap();
    let binaries = cargo_toml.get("bin").unwrap().as_slice().unwrap().to_owned();
    let mut sorted_binaries = binaries.clone();
    sorted_binaries.sort_by(|a, b| {
        let a_name = a.as_table().unwrap().get("name").unwrap().as_str().unwrap();
        let b_name = b.as_table().unwrap().get("name").unwrap().as_str().unwrap();
        a_name.cmp(b_name)
    });

    for (bin, correct_bin) in binaries.iter().zip(sorted_binaries) {
        let bin_name = bin.as_table().unwrap().get("name").unwrap().as_str().unwrap();
        let correct_bin_name = correct_bin.as_table()
                                          .unwrap()
                                          .get("name")
                                          .unwrap()
                                          .as_str()
                                          .unwrap();
        if bin_name != correct_bin_name {
            panic!("{} is not in the correct order in Cargo.toml!", bin_name);
        }
    }
}

fn main() {
    check_toml();

    let files = fs::read_dir("src")
                    .unwrap()
                    .map(|e| e.unwrap());

    for f in files {
        let path = f.path();
        if metadata(&path).unwrap().is_file() {
            check(&path);
        }
    }
}

fn check(path: &Path) {
    let mut content = String::new();
    File::open(&path).unwrap().read_to_string(&mut content).unwrap();

    for (i, mut line) in content.lines().enumerate() {
        if i == 0 && path.file_name().unwrap() != "lib.rs" {
            // Ensure the first line has a URL of the proper form
            let task_comment = Regex::new(r"// http://rosettacode\.org/wiki/.+").unwrap();
            if !task_comment.is_match(line) {
                line_error(i + 1,
                           path,
                           "file does not start with \"// http://rosettacode.org/wiki/<TASK \
                            NAME>\"");
            }
        }

        // Ignore '\r'
        if let Some('\r') = line.chars().rev().next() {
            line = &line[..line.len() - 1];
        }

        // Check length
        if line.len() > 100 {
            line_error(i + 1, path, "line is longer than 100 characters");
        }

        // Check trailing whitespace
        if let Some(last_char) = line.chars().rev().next() {
            if last_char.is_whitespace() {
                line_error(i + 1, path, "line has trailing whitespace");
            }
        }
    }
}

fn line_error(line: usize, path: &Path, msg: &str) {
    panic!("Formatting error, {} (line {} of file \"{}\")",
           msg,
           line,
           path.to_str().unwrap())
}
<|endoftext|>


<commit_before>// build.rs

extern crate cmake;
#[cfg(unix)]
extern crate pkg_config;
#[cfg(unix)]
#[cfg(feature = "decode_test")]
extern crate bindgen;
#[cfg(target_arch = "x86_64")]
extern crate nasm_rs;

use std::env;
use std::fs;
use std::path::Path;

fn main() {
    #[cfg(all(target_arch = "x86_64", not(windows)))] {
        use std::fs::File;
        use std::io::Write;
        let out_dir = env::var("OUT_DIR").unwrap();
        {
            let dest_path = Path::new(&out_dir).join("config.asm");
            let mut config_file = File::create(dest_path).unwrap();
            config_file.write(b"	%define private_prefix rav1e\n").unwrap();
            config_file.write(b"	%define ARCH_X86_32 0\n").unwrap();
            config_file.write(b" %define ARCH_X86_64 1\n").unwrap();
            config_file.write(b"	%define PIC 1\n").unwrap();
            config_file.write(b" %define STACK_ALIGNMENT 32\n").unwrap();
            if cfg!(target_os="macos") {
              config_file.write(b" %define PREFIX 1\n").unwrap();
            }
        }
        let mut config_include_arg = String::from("-I");
        config_include_arg.push_str(&out_dir);
        config_include_arg.push('/');
        nasm_rs::compile_library_args("rav1easm", &["src/x86/ipred.asm"], &[&config_include_arg, "-Isrc/"]);
        println!("cargo:rustc-link-lib=static=rav1easm");
    }

    if cfg!(windows) && cfg!(feature = "decode_test") {
        panic!("Unsupported feature on this platform!");
    }

    let cargo_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let build_path = Path::new(&cargo_dir).join("aom_build/aom");
    let debug = if let Some(v) = env::var("PROFILE").ok() {
        match v.as_str() {
            "bench" | "release" => false,
            _ => true,
        }
    } else {
        false
    };

    let dst = cmake::Config::new(build_path)
        .define("CONFIG_DEBUG", (debug as u8).to_string())
        .define("CONFIG_ANALYZER", "0")
        .define("ENABLE_DOCS", "0")
        .define("ENABLE_NASM", "1")
        .define("ENABLE_TESTS", "0")
        .no_build_target(cfg!(windows))
        .build();

    // Dirty hack to force a rebuild whenever the defaults are changed upstream
    let _ = fs::remove_file(dst.join("build/CMakeCache.txt"));

    #[cfg(windows)] {
        println!("cargo:rustc-link-search=native={}", dst.join("build").to_str().unwrap());
        println!("cargo:rustc-link-search=native={}", dst.join("build/Debug").to_str().unwrap());
        println!("cargo:rustc-link-search=native={}", dst.join("build/Release").to_str().unwrap());
        println!("cargo:rustc-link-lib=static=aom");
    }

    #[cfg(unix)] {
        env::set_var("PKG_CONFIG_PATH", dst.join("lib/pkgconfig"));
        let _libs = pkg_config::Config::new().statik(true).probe("aom").unwrap();

        #[cfg(feature = "decode_test")] {
            use std::io::Write;

            let out_dir = env::var("OUT_DIR").unwrap();

            let headers = _libs.include_paths.clone();

            let mut builder = bindgen::builder()
                .blacklist_type("max_align_t")
                .rustfmt_bindings(false)
                .header("data/aom.h");

            for header in headers {
                builder = builder.clang_arg("-I").clang_arg(header.to_str().unwrap());
            }

            // Manually fix the comment so rustdoc won't try to pick them
            let s = builder
                .generate()
                .unwrap()
                .to_string()
                .replace("/**", "/*")
                .replace("/*!", "/*");

            let dest_path = Path::new(&out_dir).join("aom.rs");

            let mut file = fs::File::create(dest_path).unwrap();

            let _ = file.write(s.as_bytes());
        }
    }

    fn rerun_dir<P: AsRef<Path>>(dir: P) {
        for entry in fs::read_dir(dir).unwrap() {
            let entry = entry.unwrap();
            let path = entry.path();
            println!("cargo:rerun-if-changed={}", path.to_string_lossy());

            if path.is_dir() {
                rerun_dir(path);
            }
        }
    }

    rerun_dir("aom_build");
}
<commit_msg>Add the nasm file to the rerun-if-changed list<commit_after>// build.rs

extern crate cmake;
#[cfg(unix)]
extern crate pkg_config;
#[cfg(unix)]
#[cfg(feature = "decode_test")]
extern crate bindgen;
#[cfg(target_arch = "x86_64")]
extern crate nasm_rs;

use std::env;
use std::fs;
use std::path::Path;

fn main() {
    #[cfg(all(target_arch = "x86_64", not(windows)))] {
        use std::fs::File;
        use std::io::Write;
        let out_dir = env::var("OUT_DIR").unwrap();
        {
            let dest_path = Path::new(&out_dir).join("config.asm");
            let mut config_file = File::create(dest_path).unwrap();
            config_file.write(b"	%define private_prefix rav1e\n").unwrap();
            config_file.write(b"	%define ARCH_X86_32 0\n").unwrap();
            config_file.write(b" %define ARCH_X86_64 1\n").unwrap();
            config_file.write(b"	%define PIC 1\n").unwrap();
            config_file.write(b" %define STACK_ALIGNMENT 32\n").unwrap();
            if cfg!(target_os="macos") {
              config_file.write(b" %define PREFIX 1\n").unwrap();
            }
        }
        let mut config_include_arg = String::from("-I");
        config_include_arg.push_str(&out_dir);
        config_include_arg.push('/');
        nasm_rs::compile_library_args("rav1easm", &["src/x86/ipred.asm"], &[&config_include_arg, "-Isrc/"]);
        println!("cargo:rustc-link-lib=static=rav1easm");
        rerun_dir("src/x86");
        rerun_dir("src/ext/x86");
    }

    if cfg!(windows) && cfg!(feature = "decode_test") {
        panic!("Unsupported feature on this platform!");
    }

    let cargo_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let build_path = Path::new(&cargo_dir).join("aom_build/aom");
    let debug = if let Some(v) = env::var("PROFILE").ok() {
        match v.as_str() {
            "bench" | "release" => false,
            _ => true,
        }
    } else {
        false
    };

    let dst = cmake::Config::new(build_path)
        .define("CONFIG_DEBUG", (debug as u8).to_string())
        .define("CONFIG_ANALYZER", "0")
        .define("ENABLE_DOCS", "0")
        .define("ENABLE_NASM", "1")
        .define("ENABLE_TESTS", "0")
        .no_build_target(cfg!(windows))
        .build();

    // Dirty hack to force a rebuild whenever the defaults are changed upstream
    let _ = fs::remove_file(dst.join("build/CMakeCache.txt"));

    #[cfg(windows)] {
        println!("cargo:rustc-link-search=native={}", dst.join("build").to_str().unwrap());
        println!("cargo:rustc-link-search=native={}", dst.join("build/Debug").to_str().unwrap());
        println!("cargo:rustc-link-search=native={}", dst.join("build/Release").to_str().unwrap());
        println!("cargo:rustc-link-lib=static=aom");
    }

    #[cfg(unix)] {
        env::set_var("PKG_CONFIG_PATH", dst.join("lib/pkgconfig"));
        let _libs = pkg_config::Config::new().statik(true).probe("aom").unwrap();

        #[cfg(feature = "decode_test")] {
            use std::io::Write;

            let out_dir = env::var("OUT_DIR").unwrap();

            let headers = _libs.include_paths.clone();

            let mut builder = bindgen::builder()
                .blacklist_type("max_align_t")
                .rustfmt_bindings(false)
                .header("data/aom.h");

            for header in headers {
                builder = builder.clang_arg("-I").clang_arg(header.to_str().unwrap());
            }

            // Manually fix the comment so rustdoc won't try to pick them
            let s = builder
                .generate()
                .unwrap()
                .to_string()
                .replace("/**", "/*")
                .replace("/*!", "/*");

            let dest_path = Path::new(&out_dir).join("aom.rs");

            let mut file = fs::File::create(dest_path).unwrap();

            let _ = file.write(s.as_bytes());
        }
    }

    fn rerun_dir<P: AsRef<Path>>(dir: P) {
        for entry in fs::read_dir(dir).unwrap() {
            let entry = entry.unwrap();
            let path = entry.path();
            println!("cargo:rerun-if-changed={}", path.to_string_lossy());

            if path.is_dir() {
                rerun_dir(path);
            }
        }
    }

    rerun_dir("aom_build");
}
<|endoftext|>