<commit_before>//! Read and write Elasto Mania replay files.
use std::io::{ Read, Write };
use std::fs::File;
use byteorder::{ ReadBytesExt, WriteBytesExt, LittleEndian };
use super::{ Position };

// Magic arbitrary number to signify end of replay file.
const EOR: u32 = 0x00492F75;

/// One frame of replay.
#[derive(Debug, Default, PartialEq)]
pub struct Frame {
    /// Bike position?
    pub bike: Position<f32>,
    /// Left wheel position.
    pub left_wheel: Position<i16>,
    /// Right wheel position.
    pub right_wheel: Position<i16>,
    /// Head position.
    pub head: Position<i16>,
    /// Bike rotation. Range 0..10000.
    pub rotation: i16,
    /// Left wheel rotation. Range 0..255.
    pub left_wheel_rotation: u8,
    /// Right wheel rotation. Range 0..255.
    pub right_wheel_rotation: u8,
    /// Throttle.
    pub throttle: bool,
    /// Right direction. True = right, False = left.
    // TODO: consider making right field = direction and enum with right and left?
    pub right: bool,
    /// Spring sound effect volume.
    pub volume: i16
}

impl Frame {
    pub fn new() -> Self {
        Frame {
            bike: Position { x: 0f32, y: 0f32 },
            left_wheel: Position { x: 0, y: 0 },
            right_wheel: Position { x: 0, y: 0 },
            head: Position { x: 0, y: 0 },
            rotation: 0,
            left_wheel_rotation: 0,
            right_wheel_rotation: 0,
            throttle: false,
            right: false,
            volume: 0
        }
    }
}

#[derive(Debug, Default, PartialEq)]
pub struct Event {
    /// Time of event.
    pub time: f64,
    /// Event type.
    // TODO: Make enum.
    pub event_type: [u32; 2]
}

impl Event {
    pub fn new() -> Self {
        Event {
            time: 0f64,
            event_type: [0, 0]
        }
    }
}

/// Replay struct
#[derive(Debug, Default, PartialEq)]
pub struct Replay {
    /// Raw binary data.
    raw: Vec<u8>,
    /// Number of Frames in replay.
    pub frame_count: i32,
    /// Whether replay is multi-player or not.
    pub multi: bool,
    /// Whether replay is flag-tag or not.
    pub flag_tag: bool,
    /// Random number to link with level file.
    pub link: u32,
    /// Full level filename.
    pub level: String,
    /// Vector with Frame structs.
    pub frames: Vec<Frame>,
    /// Events.
    pub events: Vec<Event>
}

impl Replay {
    /// Build a new Replay.
    ///
    /// # Examples
    ///
    /// ```
    /// let rec = elma::rec::Replay::new();
    /// ```
    pub fn new() -> Self {
        Replay {
            raw: vec![],
            frame_count: 0,
            multi: false,
            flag_tag: false,
            link: 0,
            level: String::new(),
            frames: vec![],
            events: vec![]
        }
    }

    /// Loads a replay file and returns a Replay struct.
    ///
    /// # Examples
    ///
    /// ```
    /// let rec = elma::rec::Replay::load_replay("tests/test.rec");
    /// ```
    pub fn load_replay(filename: &str) -> Self {
        let mut replay = Replay::new();
        let mut file = File::open(filename).unwrap();
        let mut buffer = vec![];
        file.read_to_end(&mut buffer).unwrap();
        replay.raw = buffer;
        replay.parse_replay();
        replay
    }

    pub fn parse_replay(&mut self) {
        // TODO: do that.
    }
}
<commit_msg>Implement Default trait for Replay<commit_after>//! Read and write Elasto Mania replay files.
use std::io::{ Read, Write };
use std::fs::File;
use byteorder::{ ReadBytesExt, WriteBytesExt, LittleEndian };
use super::{ Position };

// Magic arbitrary number to signify end of replay file.
const EOR: u32 = 0x00492F75;

/// One frame of replay.
#[derive(Debug, Default, PartialEq)]
pub struct Frame {
    /// Bike position?
    pub bike: Position<f32>,
    /// Left wheel position.
    pub left_wheel: Position<i16>,
    /// Right wheel position.
    pub right_wheel: Position<i16>,
    /// Head position.
    pub head: Position<i16>,
    /// Bike rotation. Range 0..10000.
    pub rotation: i16,
    /// Left wheel rotation. Range 0..255.
    pub left_wheel_rotation: u8,
    /// Right wheel rotation. Range 0..255.
    pub right_wheel_rotation: u8,
    /// Throttle.
    pub throttle: bool,
    /// Right direction. True = right, False = left.
    // TODO: consider making right field = direction and enum with right and left?
    pub right: bool,
    /// Spring sound effect volume.
    pub volume: i16
}

impl Frame {
    pub fn new() -> Self {
        Frame {
            bike: Position { x: 0f32, y: 0f32 },
            left_wheel: Position { x: 0, y: 0 },
            right_wheel: Position { x: 0, y: 0 },
            head: Position { x: 0, y: 0 },
            rotation: 0,
            left_wheel_rotation: 0,
            right_wheel_rotation: 0,
            throttle: false,
            right: false,
            volume: 0
        }
    }
}

#[derive(Debug, Default, PartialEq)]
pub struct Event {
    /// Time of event.
    pub time: f64,
    /// Event type.
    // TODO: Make enum.
    pub event_type: [u32; 2]
}

impl Event {
    pub fn new() -> Self {
        Event {
            time: 0f64,
            event_type: [0, 0]
        }
    }
}

/// Replay struct
#[derive(Debug, PartialEq)]
pub struct Replay {
    /// Raw binary data.
    raw: Vec<u8>,
    /// Number of Frames in replay.
    pub frame_count: i32,
    /// Whether replay is multi-player or not.
    pub multi: bool,
    /// Whether replay is flag-tag or not.
    pub flag_tag: bool,
    /// Random number to link with level file.
    pub link: u32,
    /// Full level filename.
    pub level: String,
    /// Vector with Frame structs.
    pub frames: Vec<Frame>,
    /// Events.
    pub events: Vec<Event>
}

impl Default for Replay {
    fn default() -> Replay { Replay::new() }
}

impl Replay {
    /// Build a new Replay.
    ///
    /// # Examples
    ///
    /// ```
    /// let rec = elma::rec::Replay::new();
    /// ```
    pub fn new() -> Self {
        Replay {
            raw: vec![],
            frame_count: 0,
            multi: false,
            flag_tag: false,
            link: 0,
            level: String::new(),
            frames: vec![],
            events: vec![]
        }
    }

    /// Loads a replay file and returns a Replay struct.
    ///
    /// # Examples
    ///
    /// ```
    /// let rec = elma::rec::Replay::load_replay("tests/test.rec");
    /// ```
    pub fn load_replay(filename: &str) -> Self {
        let mut replay = Replay::new();
        let mut file = File::open(filename).unwrap();
        let mut buffer = vec![];
        file.read_to_end(&mut buffer).unwrap();
        replay.raw = buffer;
        replay.parse_replay();
        replay
    }

    pub fn parse_replay(&mut self) {
        // TODO: do that.
    }
}
<|endoftext|>


<commit_before>use serde::Deserialize;
use serde::Serialize;
use serde_json;
use std::collections::BTreeMap;
use super::Result;

/// represents an general jira error response
#[derive(Deserialize, Debug)]
pub struct Errors {
    #[serde(rename = "errorMessages")]
    pub error_messages: Vec<String>,
    pub errors: BTreeMap<String, String>,
}

/// represents a single jira issue
#[derive(Deserialize, Debug)]
pub struct Issue {
    #[serde(rename = "self")]
    pub self_link: String,
    pub key: String,
    pub id: String,
    pub fields: BTreeMap<String, ::serde_json::Value>,
    pub changelog: Option<Changelog>,
}

impl Issue {
    /// resolves a typed field from an issues lists of arbitrary fields
    pub fn field<F>(&self, name: &str) -> Option<Result<F>>
    where
        for<'de> F: Deserialize<'de>,
    {
        self.fields.get(name).map(|value| {
            let decoded = try!(serde_json::value::from_value::<F>(value.clone()));
            Ok(decoded)
        })
    }

    fn user_field(&self, name: &str) -> Option<Result<User>> {
        self.field::<User>(name)
    }

    fn string_field(&self, name: &str) -> Option<Result<String>> {
        self.field::<String>(name)
    }

    /// user assigned to issue
    pub fn assignee(&self) -> Option<User> {
        self.user_field("assignee").and_then(|value| value.ok())
    }

    /// user that created the issue
    pub fn creator(&self) -> Option<User> {
        self.user_field("creator").and_then(|value| value.ok())
    }

    /// user that reported the issue
    pub fn reporter(&self) -> Option<User> {
        self.user_field("reporter").and_then(|value| value.ok())
    }

    /// the current status of the issue
    pub fn status(&self) -> Option<Status> {
        self.field::<Status>("status").and_then(|value| value.ok())
    }

    /// brief summary of the issue
    pub fn summary(&self) -> Option<String> {
        self.string_field("summary").and_then(|value| value.ok())
    }

    /// description of the issue
    pub fn description(&self) -> Option<String> {
        self.string_field("description").and_then(
            |value| value.ok(),
        )
    }

    /// updated timestamp
    pub fn updated(&self) -> Option<String> {
        self.string_field("updated").and_then(|value| value.ok())
    }

    /// created timestamp
    pub fn created(&self) -> Option<String> {
        self.string_field("created").and_then(|value| value.ok())
    }

    pub fn resolution_date(&self) -> Option<String> {
        self.string_field("resolution_date").and_then(
            |value| value.ok(),
        )
    }

    /// an issue type
    pub fn issue_type(&self) -> Option<IssueType> {
        self.field::<IssueType>("issuetype").and_then(
            |value| value.ok(),
        )
    }

    /// labels associated with the issue
    pub fn labels(&self) -> Vec<String> {
        self.field::<Vec<String>>("labels")
            .and_then(|value| value.ok())
            .unwrap_or(vec![])
    }

    /// list of versions associated with the issue
    pub fn fix_versions(&self) -> Vec<Version> {
        self.field::<Vec<Version>>("fixVersions")
            .and_then(|value| value.ok())
            .unwrap_or(vec![])
    }

    /// priority of the issue
    pub fn priority(&self) -> Option<Priority> {
        self.field::<Priority>("priority").and_then(
            |value| value.ok(),
        )
    }

    /// links to other issues
    pub fn links(&self) -> Option<Result<Vec<IssueLink>>> {
        self.field::<Vec<IssueLink>>("issuelinks") //.and_then(|value| value.ok()).unwrap_or(vec![])
    }

    pub fn project(&self) -> Option<Project> {
        self.field::<Project>("project").and_then(
            |value| value.ok(),
        )
    }

    pub fn resolution(&self) -> Option<Resolution> {
        self.field::<Resolution>("resolution").and_then(
            |value| value.ok(),
        )
    }

    pub fn attachment(&self) -> Vec<Attachment> {
        self.field::<Vec<Attachment>>("attachment")
            .and_then(|value| value.ok())
            .unwrap_or(vec![])
    }
}

#[derive(Deserialize, Debug)]
pub struct Attachment {
  pub id: String,
  #[serde(rename = "self")]
  pub self_link: String,
  pub filename: String,
  pub author: User,
  pub created: String,
  pub size: u64,
  #[serde(rename = "mimeType")]
  pub mime_type: String,
  pub content: String,
  pub thumbnail: Option<String>,
}

#[derive(Deserialize, Debug)]
pub struct Changelog {
    pub histories: Vec<History>,
}

#[derive(Deserialize, Debug)]
pub struct History {
    pub author: User,
    pub created: String,
    pub items: Vec<HistoryItem>,
}

#[derive(Deserialize, Debug)]
pub struct HistoryItem {
    pub field: String,
    pub from: Option<String>,
    #[serde(rename = "fromString")]
    pub from_string: Option<String>,
    pub to: Option<String>,
    #[serde(rename = "toString")]
    pub to_string: Option<String>,
}

#[derive(Deserialize, Debug)]
pub struct Project {
    pub id: String,
    pub key: String,
    pub name: String,
}

/// represents link relationship between issues
#[derive(Deserialize, Debug)]
pub struct IssueLink {
    pub id: String,
    #[serde(rename = "self")]
    pub self_link: String,
    #[serde(rename = "outwardIssue")]
    pub outward_issue: Option<Issue>,
    #[serde(rename = "inwardIssue")]
    pub inward_issue: Option<Issue>,
    #[serde(rename = "type")]
    pub link_type: LinkType,
}

/// represents type of issue relation
#[derive(Deserialize, Debug)]
pub struct LinkType {
    pub id: String,
    pub inward: String,
    pub name: String,
    pub outward: String,
    #[serde(rename = "self")]
    pub self_link: String,
}

#[derive(Deserialize, Debug)]
pub struct Version {
    pub archived: bool,
    pub id: String,
    pub name: String,
    pub released: bool,
    #[serde(rename = "self")]
    pub self_link: String,
}

#[derive(Deserialize, Debug)]
pub struct User {
    pub active: bool,
    #[serde(rename = "avatarUrls")]
    pub avatar_urls: BTreeMap<String, String>,
    #[serde(rename = "displayName")]
    pub display_name: String,
    #[serde(rename = "emailAddress")]
    pub email_address: String,
    pub key: Option<String>,
    pub name: String,
    #[serde(rename = "self")]
    pub self_link: String,
    #[serde(rename = "timeZone")]
    pub timezone: Option<String>,
}

#[derive(Deserialize, Debug)]
pub struct Status {
    pub description: String,
    #[serde(rename = "iconUrl")]
    pub icon_url: String,
    pub id: String,
    pub name: String,
    #[serde(rename = "self")]
    pub self_link: String,
}

#[derive(Deserialize, Debug)]
pub struct Priority {
    pub icon_url: String,
    pub id: String,
    pub name: String,
    #[serde(rename = "self")]
    pub self_link: String,
}

#[derive(Deserialize, Debug)]
pub struct IssueType {
    pub description: String,
    #[serde(rename = "iconUrl")]
    pub icon_url: String,
    pub id: String,
    pub name: String,
    #[serde(rename = "self")]
    pub self_link: String,
    pub subtask: bool,
}

#[derive(Deserialize, Debug)]
pub struct SearchResults {
    pub total: u64,
    #[serde(rename = "maxResults")]
    pub max_results: u64,
    #[serde(rename = "startAt")]
    pub start_at: u64,
    pub expand: String,
    pub issues: Vec<Issue>,
}

#[derive(Deserialize, Debug)]
pub struct TransitionOption {
    pub id: String,
    pub name: String,
    pub to: TransitionTo,
}

#[derive(Deserialize, Debug)]
pub struct TransitionTo {
    pub name: String,
    pub id: String,
}

/// contains list of options an issue can transitions through
#[derive(Deserialize, Debug)]
pub struct TransitionOptions {
    pub transitions: Vec<TransitionOption>,
}

#[derive(Serialize, Debug)]
pub struct TransitionTriggerOptions {
    pub transition: Transition,
    pub fields: BTreeMap<String, ::serde_json::Value>,
}

impl TransitionTriggerOptions {
    /// creates a new instance
    pub fn new<I>(id: I) -> TransitionTriggerOptions
    where
        I: Into<String>,
    {
        TransitionTriggerOptions {
            transition: Transition { id: id.into() },
            fields: BTreeMap::new(),
        }
    }

    pub fn builder<I>(id: I) -> TransitionTriggerOptionsBuilder
    where
        I: Into<String>,
    {
        TransitionTriggerOptionsBuilder::new(id)
    }
}

pub struct TransitionTriggerOptionsBuilder {
    pub transition: Transition,
    pub fields: BTreeMap<String, ::serde_json::Value>,
}

impl TransitionTriggerOptionsBuilder {
    /// creates a new instance
    pub fn new<I>(id: I) -> TransitionTriggerOptionsBuilder
    where
        I: Into<String>,
    {
        TransitionTriggerOptionsBuilder {
            transition: Transition { id: id.into() },
            fields: BTreeMap::new(),
        }
    }

    /// appends a field to update as part of transition
    pub fn field<N, V>(&mut self, name: N, value: V) -> &mut TransitionTriggerOptionsBuilder
    where
        N: Into<String>,
        V: Serialize,
    {
        self.fields.insert(
            name.into(),
            serde_json::to_value(value).expect("Value to serialize"),
        );
        self
    }

    /// updates resolution in transition
    pub fn resolution<R>(&mut self, name: R) -> &mut TransitionTriggerOptionsBuilder
    where
        R: Into<String>,
    {
        self.field("resolution", Resolution { name: name.into() });
        self
    }

    pub fn build(&self) -> TransitionTriggerOptions {
        TransitionTriggerOptions {
            transition: self.transition.clone(),
            fields: self.fields.clone(),
        }
    }
}

#[derive(Serialize, Debug, Deserialize)]
pub struct Resolution {
    name: String,
}

#[derive(Serialize, Clone, Debug)]
pub struct Transition {
    pub id: String,
}
<commit_msg>[feat] add comment<commit_after>use serde::Deserialize;
use serde::Serialize;
use serde_json;
use std::collections::BTreeMap;
use super::Result;

/// represents an general jira error response
#[derive(Deserialize, Debug)]
pub struct Errors {
    #[serde(rename = "errorMessages")]
    pub error_messages: Vec<String>,
    pub errors: BTreeMap<String, String>,
}

/// represents a single jira issue
#[derive(Deserialize, Debug)]
pub struct Issue {
    #[serde(rename = "self")]
    pub self_link: String,
    pub key: String,
    pub id: String,
    pub fields: BTreeMap<String, ::serde_json::Value>,
    pub changelog: Option<Changelog>,
}

impl Issue {
    /// resolves a typed field from an issues lists of arbitrary fields
    pub fn field<F>(&self, name: &str) -> Option<Result<F>>
    where
        for<'de> F: Deserialize<'de>,
    {
        self.fields.get(name).map(|value| {
            let decoded = try!(serde_json::value::from_value::<F>(value.clone()));
            Ok(decoded)
        })
    }

    fn user_field(&self, name: &str) -> Option<Result<User>> {
        self.field::<User>(name)
    }

    fn string_field(&self, name: &str) -> Option<Result<String>> {
        self.field::<String>(name)
    }

    /// user assigned to issue
    pub fn assignee(&self) -> Option<User> {
        self.user_field("assignee").and_then(|value| value.ok())
    }

    /// user that created the issue
    pub fn creator(&self) -> Option<User> {
        self.user_field("creator").and_then(|value| value.ok())
    }

    /// user that reported the issue
    pub fn reporter(&self) -> Option<User> {
        self.user_field("reporter").and_then(|value| value.ok())
    }

    /// the current status of the issue
    pub fn status(&self) -> Option<Status> {
        self.field::<Status>("status").and_then(|value| value.ok())
    }

    /// brief summary of the issue
    pub fn summary(&self) -> Option<String> {
        self.string_field("summary").and_then(|value| value.ok())
    }

    /// description of the issue
    pub fn description(&self) -> Option<String> {
        self.string_field("description").and_then(
            |value| value.ok(),
        )
    }

    /// updated timestamp
    pub fn updated(&self) -> Option<String> {
        self.string_field("updated").and_then(|value| value.ok())
    }

    /// created timestamp
    pub fn created(&self) -> Option<String> {
        self.string_field("created").and_then(|value| value.ok())
    }

    pub fn resolution_date(&self) -> Option<String> {
        self.string_field("resolution_date").and_then(
            |value| value.ok(),
        )
    }

    /// an issue type
    pub fn issue_type(&self) -> Option<IssueType> {
        self.field::<IssueType>("issuetype").and_then(
            |value| value.ok(),
        )
    }

    /// labels associated with the issue
    pub fn labels(&self) -> Vec<String> {
        self.field::<Vec<String>>("labels")
            .and_then(|value| value.ok())
            .unwrap_or(vec![])
    }

    /// list of versions associated with the issue
    pub fn fix_versions(&self) -> Vec<Version> {
        self.field::<Vec<Version>>("fixVersions")
            .and_then(|value| value.ok())
            .unwrap_or(vec![])
    }

    /// priority of the issue
    pub fn priority(&self) -> Option<Priority> {
        self.field::<Priority>("priority").and_then(
            |value| value.ok(),
        )
    }

    /// links to other issues
    pub fn links(&self) -> Option<Result<Vec<IssueLink>>> {
        self.field::<Vec<IssueLink>>("issuelinks") //.and_then(|value| value.ok()).unwrap_or(vec![])
    }

    pub fn project(&self) -> Option<Project> {
        self.field::<Project>("project").and_then(
            |value| value.ok(),
        )
    }

    pub fn resolution(&self) -> Option<Resolution> {
        self.field::<Resolution>("resolution").and_then(
            |value| value.ok(),
        )
    }

    pub fn attachment(&self) -> Vec<Attachment> {
        self.field::<Vec<Attachment>>("attachment")
            .and_then(|value| value.ok())
            .unwrap_or(vec![])
    }

    pub fn comment(&self) -> Vec<Comment> {
        self.field::<Comments>("comment")
            .and_then(|value| value.ok())
            .map(|value| value.comments)
            .unwrap_or(vec![])
    }
}

#[derive(Deserialize, Debug)]
pub struct Attachment {
  pub id: String,
  #[serde(rename = "self")]
  pub self_link: String,
  pub filename: String,
  pub author: User,
  pub created: String,
  pub size: u64,
  #[serde(rename = "mimeType")]
  pub mime_type: String,
  pub content: String,
  pub thumbnail: Option<String>,
}

#[derive(Deserialize, Debug)]
pub struct Comments {
    pub comments: Vec<Comment>,
}

#[derive(Deserialize, Debug)]
pub struct Comment {
    pub id: Option<String>,
    #[serde(rename = "self")]
    pub self_link: String,
    pub author: Option<User>,
    #[serde(rename = "updateAuthor")]
    pub update_author: Option<User>,
    pub created: String,
    pub updated: String,
    pub body: String,
    pub visibility: Option<Visibility>,
}

#[derive(Deserialize, Debug)]
pub struct Visibility {
    #[serde(rename = "type")]
    pub visibility_type: String,
    pub value: String,
}

#[derive(Deserialize, Debug)]
pub struct Changelog {
    pub histories: Vec<History>,
}

#[derive(Deserialize, Debug)]
pub struct History {
    pub author: User,
    pub created: String,
    pub items: Vec<HistoryItem>,
}

#[derive(Deserialize, Debug)]
pub struct HistoryItem {
    pub field: String,
    pub from: Option<String>,
    #[serde(rename = "fromString")]
    pub from_string: Option<String>,
    pub to: Option<String>,
    #[serde(rename = "toString")]
    pub to_string: Option<String>,
}

#[derive(Deserialize, Debug)]
pub struct Project {
    pub id: String,
    pub key: String,
    pub name: String,
}

/// represents link relationship between issues
#[derive(Deserialize, Debug)]
pub struct IssueLink {
    pub id: String,
    #[serde(rename = "self")]
    pub self_link: String,
    #[serde(rename = "outwardIssue")]
    pub outward_issue: Option<Issue>,
    #[serde(rename = "inwardIssue")]
    pub inward_issue: Option<Issue>,
    #[serde(rename = "type")]
    pub link_type: LinkType,
}

/// represents type of issue relation
#[derive(Deserialize, Debug)]
pub struct LinkType {
    pub id: String,
    pub inward: String,
    pub name: String,
    pub outward: String,
    #[serde(rename = "self")]
    pub self_link: String,
}

#[derive(Deserialize, Debug)]
pub struct Version {
    pub archived: bool,
    pub id: String,
    pub name: String,
    pub released: bool,
    #[serde(rename = "self")]
    pub self_link: String,
}

#[derive(Deserialize, Debug)]
pub struct User {
    pub active: bool,
    #[serde(rename = "avatarUrls")]
    pub avatar_urls: BTreeMap<String, String>,
    #[serde(rename = "displayName")]
    pub display_name: String,
    #[serde(rename = "emailAddress")]
    pub email_address: String,
    pub key: Option<String>,
    pub name: String,
    #[serde(rename = "self")]
    pub self_link: String,
    #[serde(rename = "timeZone")]
    pub timezone: Option<String>,
}

#[derive(Deserialize, Debug)]
pub struct Status {
    pub description: String,
    #[serde(rename = "iconUrl")]
    pub icon_url: String,
    pub id: String,
    pub name: String,
    #[serde(rename = "self")]
    pub self_link: String,
}

#[derive(Deserialize, Debug)]
pub struct Priority {
    pub icon_url: String,
    pub id: String,
    pub name: String,
    #[serde(rename = "self")]
    pub self_link: String,
}

#[derive(Deserialize, Debug)]
pub struct IssueType {
    pub description: String,
    #[serde(rename = "iconUrl")]
    pub icon_url: String,
    pub id: String,
    pub name: String,
    #[serde(rename = "self")]
    pub self_link: String,
    pub subtask: bool,
}

#[derive(Deserialize, Debug)]
pub struct SearchResults {
    pub total: u64,
    #[serde(rename = "maxResults")]
    pub max_results: u64,
    #[serde(rename = "startAt")]
    pub start_at: u64,
    pub expand: String,
    pub issues: Vec<Issue>,
}

#[derive(Deserialize, Debug)]
pub struct TransitionOption {
    pub id: String,
    pub name: String,
    pub to: TransitionTo,
}

#[derive(Deserialize, Debug)]
pub struct TransitionTo {
    pub name: String,
    pub id: String,
}

/// contains list of options an issue can transitions through
#[derive(Deserialize, Debug)]
pub struct TransitionOptions {
    pub transitions: Vec<TransitionOption>,
}

#[derive(Serialize, Debug)]
pub struct TransitionTriggerOptions {
    pub transition: Transition,
    pub fields: BTreeMap<String, ::serde_json::Value>,
}

impl TransitionTriggerOptions {
    /// creates a new instance
    pub fn new<I>(id: I) -> TransitionTriggerOptions
    where
        I: Into<String>,
    {
        TransitionTriggerOptions {
            transition: Transition { id: id.into() },
            fields: BTreeMap::new(),
        }
    }

    pub fn builder<I>(id: I) -> TransitionTriggerOptionsBuilder
    where
        I: Into<String>,
    {
        TransitionTriggerOptionsBuilder::new(id)
    }
}

pub struct TransitionTriggerOptionsBuilder {
    pub transition: Transition,
    pub fields: BTreeMap<String, ::serde_json::Value>,
}

impl TransitionTriggerOptionsBuilder {
    /// creates a new instance
    pub fn new<I>(id: I) -> TransitionTriggerOptionsBuilder
    where
        I: Into<String>,
    {
        TransitionTriggerOptionsBuilder {
            transition: Transition { id: id.into() },
            fields: BTreeMap::new(),
        }
    }

    /// appends a field to update as part of transition
    pub fn field<N, V>(&mut self, name: N, value: V) -> &mut TransitionTriggerOptionsBuilder
    where
        N: Into<String>,
        V: Serialize,
    {
        self.fields.insert(
            name.into(),
            serde_json::to_value(value).expect("Value to serialize"),
        );
        self
    }

    /// updates resolution in transition
    pub fn resolution<R>(&mut self, name: R) -> &mut TransitionTriggerOptionsBuilder
    where
        R: Into<String>,
    {
        self.field("resolution", Resolution { name: name.into() });
        self
    }

    pub fn build(&self) -> TransitionTriggerOptions {
        TransitionTriggerOptions {
            transition: self.transition.clone(),
            fields: self.fields.clone(),
        }
    }
}

#[derive(Serialize, Debug, Deserialize)]
pub struct Resolution {
    name: String,
}

#[derive(Serialize, Clone, Debug)]
pub struct Transition {
    pub id: String,
}
<|endoftext|>


<commit_before>use std::sync::mpsc;
use std::thread;
use bencode::{self, BEncode};
use {amy, tiny_http, serde_json, control, CONTROL, torrent};

pub struct Handle {
    pub tx: mpsc::Sender<Response>,
}

impl Handle {
    pub fn init(&self) { }

    pub fn get(&self) -> mpsc::Sender<Response> {
        self.tx.clone()
    }
}

unsafe impl Sync for Handle {}

pub struct RPC {
    rx: mpsc::Receiver<Response>,
    tx: amy::Sender<control::Request>,
}

macro_rules! id_match {
    ($req:expr, $resp:expr, $s:expr, $body:expr) => (
        let mut s = $s.to_owned();
        let idx = s.find("{}").unwrap();
        let mut remaining = s.split_off(idx);
        let end = remaining.split_off(2);
        if $req.url().starts_with(&s) && $req.url().ends_with(&end) {
            let len = $req.url().len();
            let val = &$req.url()[idx..(len - end.len())];
            if let Ok(i) = val.parse::<usize>() {
                $resp = Ok($body(i));
            } else {
                $resp = Err(format!("{} is not a valid integer!", val));
            }
        }
    );
}

impl RPC {
    pub fn new(rx: mpsc::Receiver<Response>) -> RPC {
        RPC {
            rx,
            tx: CONTROL.ctrl_tx.lock().unwrap().try_clone().unwrap(),
        }
    }

    pub fn run(&mut self) {
        let server = tiny_http::Server::http("0.0.0.0:8412").unwrap();
        for mut request in server.incoming_requests() {
            let mut resp = Err("Invalid URL".to_owned());
            id_match!(request, resp, "/torrent/{}/info", |i| Request::TorrentInfo(i));
            id_match!(request, resp, "/torrent/{}/pause", |i| Request::PauseTorrent(i));
            id_match!(request, resp, "/torrent/{}/resume", |i| Request::ResumeTorrent(i));
            id_match!(request, resp, "/torrent/{}/remove", |i| Request::RemoveTorrent(i));
            id_match!(request, resp, "/throttle/upload/{}", |i| Request::ThrottleUpload(i));
            id_match!(request, resp, "/throttle/download/{}", |i| Request::ThrottleDownload(i));
            if request.url() == "/torrent/list" {
                resp = Ok(Request::ListTorrents);
            };
            if request.url() == "/torrent" && request.method() == &tiny_http::Method::Post {
                let mut data = Vec::new();
                request.as_reader().read_to_end(&mut data).unwrap();
                resp = match bencode::decode_buf(&mut data) {
                    Ok(b) => Ok(Request::AddTorrent(b)),
                    Err(_) => Err("Bad torrent!".to_owned()),
                };
            }

            let resp = match resp {
                Ok(rpc) => {
                    self.tx.send(control::Request::RPC(rpc)).unwrap();
                    let resp = self.rx.recv().unwrap();
                    serde_json::to_string(&resp).unwrap()
                }
                Err(e) => serde_json::to_string(&Response::Err(e)).unwrap(),
            };
            let mut resp = tiny_http::Response::from_string(resp);
            let cors = tiny_http::Header::from_bytes(&b"Access-Control-Allow-Origin"[..], &b"*"[..]).unwrap();
            resp.add_header(cors);
            request.respond(resp).unwrap();
        }
    }
}

pub enum Request {
    ListTorrents,
    TorrentInfo(usize),
    AddTorrent(BEncode),
    PauseTorrent(usize),
    ResumeTorrent(usize),
    RemoveTorrent(usize),
    ThrottleUpload(usize),
    ThrottleDownload(usize),
}

#[derive(Serialize, Debug)]
pub enum Response {
    Torrents(Vec<usize>),
    TorrentInfo(TorrentInfo),
    AddResult(Result<usize, &'static str>),
    Ack,
    Err(String),
}

#[derive(Serialize, Debug)]
pub struct TorrentInfo {
    pub name: String,
    pub status: Status,
    pub size: u64,
    pub downloaded: u64,
    pub uploaded: u64,
    pub tracker: String,
    pub tracker_status: torrent::TrackerStatus,
}

#[derive(Serialize, Debug)]
pub enum Status {
    Downloading,
    Seeding,
    Paused,
}

pub fn start() -> Handle {
    let (tx, rx) = mpsc::channel();
    thread::spawn(move || {
        let mut d = RPC::new(rx);
        d.run();
    });
    Handle { tx }
}
<commit_msg>Optimize http route matching slightly with lazy statics<commit_after>use std::sync::mpsc;
use std::thread;
use bencode::{self, BEncode};
use {amy, tiny_http, serde_json, control, CONTROL, torrent};

pub struct Handle {
    pub tx: mpsc::Sender<Response>,
}

impl Handle {
    pub fn init(&self) { }

    pub fn get(&self) -> mpsc::Sender<Response> {
        self.tx.clone()
    }
}

unsafe impl Sync for Handle {}

pub struct RPC {
    rx: mpsc::Receiver<Response>,
    tx: amy::Sender<control::Request>,
}

macro_rules! id_match {
    ($req:expr, $resp:expr, $s:expr, $body:expr) => (
        {
            lazy_static! {
                static ref M: (String, String, usize) = {
                    let mut s = $s.to_owned();
                    let idx = s.find("{}").unwrap();
                    let mut remaining = s.split_off(idx);
                    let end = remaining.split_off(2);
                    (s, end, idx)
                };
            };
            let ref start = M.0;
            let ref end = M.1;
            let idx = M.2;
            if $req.url().starts_with(start) && $req.url().ends_with(end) {
                let len = $req.url().len();
                let val = &$req.url()[idx..(len - end.len())];
                if let Ok(i) = val.parse::<usize>() {
                    $resp = Ok($body(i));
                } else {
                    $resp = Err(format!("{} is not a valid integer!", val));
                }
            }
        }
    );
}

impl RPC {
    pub fn new(rx: mpsc::Receiver<Response>) -> RPC {
        RPC {
            rx,
            tx: CONTROL.ctrl_tx.lock().unwrap().try_clone().unwrap(),
        }
    }

    pub fn run(&mut self) {
        let server = tiny_http::Server::http("0.0.0.0:8412").unwrap();
        for mut request in server.incoming_requests() {
            let mut resp = Err("Invalid URL".to_owned());
            id_match!(request, resp, "/torrent/{}/info", |i| Request::TorrentInfo(i));
            id_match!(request, resp, "/torrent/{}/pause", |i| Request::PauseTorrent(i));
            id_match!(request, resp, "/torrent/{}/resume", |i| Request::ResumeTorrent(i));
            id_match!(request, resp, "/torrent/{}/remove", |i| Request::RemoveTorrent(i));
            id_match!(request, resp, "/throttle/upload/{}", |i| Request::ThrottleUpload(i));
            id_match!(request, resp, "/throttle/download/{}", |i| Request::ThrottleDownload(i));
            if request.url() == "/torrent/list" {
                resp = Ok(Request::ListTorrents);
            };
            if request.url() == "/torrent" && request.method() == &tiny_http::Method::Post {
                let mut data = Vec::new();
                request.as_reader().read_to_end(&mut data).unwrap();
                resp = match bencode::decode_buf(&mut data) {
                    Ok(b) => Ok(Request::AddTorrent(b)),
                    Err(_) => Err("Bad torrent!".to_owned()),
                };
            }

            let resp = match resp {
                Ok(rpc) => {
                    self.tx.send(control::Request::RPC(rpc)).unwrap();
                    let resp = self.rx.recv().unwrap();
                    serde_json::to_string(&resp).unwrap()
                }
                Err(e) => serde_json::to_string(&Response::Err(e)).unwrap(),
            };
            let mut resp = tiny_http::Response::from_string(resp);
            let cors = tiny_http::Header::from_bytes(&b"Access-Control-Allow-Origin"[..], &b"*"[..]).unwrap();
            resp.add_header(cors);
            request.respond(resp).unwrap();
        }
    }
}

pub enum Request {
    ListTorrents,
    TorrentInfo(usize),
    AddTorrent(BEncode),
    PauseTorrent(usize),
    ResumeTorrent(usize),
    RemoveTorrent(usize),
    ThrottleUpload(usize),
    ThrottleDownload(usize),
}

#[derive(Serialize, Debug)]
pub enum Response {
    Torrents(Vec<usize>),
    TorrentInfo(TorrentInfo),
    AddResult(Result<usize, &'static str>),
    Ack,
    Err(String),
}

#[derive(Serialize, Debug)]
pub struct TorrentInfo {
    pub name: String,
    pub status: Status,
    pub size: u64,
    pub downloaded: u64,
    pub uploaded: u64,
    pub tracker: String,
    pub tracker_status: torrent::TrackerStatus,
}

#[derive(Serialize, Debug)]
pub enum Status {
    Downloading,
    Seeding,
    Paused,
}

pub fn start() -> Handle {
    let (tx, rx) = mpsc::channel();
    thread::spawn(move || {
        let mut d = RPC::new(rx);
        d.run();
    });
    Handle { tx }
}
<|endoftext|>


<commit_before>/// Utilities to write SDP offers and answers using Janus's SDP parsing machinery.

use super::ffi;
use super::glib;
use super::libc;
use super::serde::de::{self, Deserialize, Deserializer, Unexpected, Visitor};
use super::serde::ser::{Serialize, Serializer};
pub use ffi::sdp::janus_sdp_generate_answer as generate_answer;
pub use ffi::sdp::janus_sdp_generate_offer as generate_offer;
use std::collections::HashMap;
use std::error::Error;
use std::fmt;
use std::ffi::{CStr, CString};
use std::ops::Deref;
use std::str;
use utils::GLibString;

pub type RawSdp = ffi::sdp::janus_sdp;
pub type RawMLine = ffi::sdp::janus_sdp_mline;
pub type RawAttribute = ffi::sdp::janus_sdp_attribute;
pub use ffi::sdp::janus_sdp_mtype as MediaType;
pub use ffi::sdp::janus_sdp_mdirection as MediaDirection;

// courtesy of c_string crate, which also has some other stuff we aren't interested in
// taking in as a dependency here.
macro_rules! c_str {
    ($lit:expr) => {
        unsafe {
            CStr::from_ptr(concat!($lit, "\0").as_ptr() as *const $crate::c_char)
        }
    }
}

/// SDP attributes which may refer to a specific RTP payload type.
static MEDIA_PAYLOAD_ATTRIBUTES: [&'static str; 3] = ["rtpmap", "fmtp", "rtcp-fb"];

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
/// Available Janus audio codecs. See utils.c.
pub enum AudioCodec {
    Opus,
    Pcmu,
    Pcma,
    G722,
    Isac16,
    Isac32,
}

impl AudioCodec {
    pub fn to_str(&self) -> &'static str {
        self.to_cstr().to_str().unwrap()
    }
    pub fn to_cstr(&self) -> &'static CStr {
        match *self {
            AudioCodec::Opus => c_str!("opus"),
            AudioCodec::Pcmu => c_str!("pcmu"),
            AudioCodec::Pcma => c_str!("pcma"),
            AudioCodec::G722 => c_str!("g722"),
            AudioCodec::Isac16 => c_str!("isac16"),
            AudioCodec::Isac32 => c_str!("isac32"),
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
/// Available Janus video codecs. See utils.c.
pub enum VideoCodec {
    Vp8,
    Vp9,
    H264,
}

impl VideoCodec {
    pub fn to_str(&self) -> &'static str {
        self.to_cstr().to_str().unwrap()
    }
    pub fn to_cstr(&self) -> &'static CStr {
        match *self {
            VideoCodec::Vp8 => c_str!("vp8"),
            VideoCodec::Vp9 => c_str!("vp9"),
            VideoCodec::H264 => c_str!("h264"),
        }
    }
}

#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
/// Parameters controlling SDP offer answering behavior. Used as keys in the parameter list
/// for `janus_sdp_generate_answer`. See sdp-utils.h in the Janus source for more details.
pub enum OfferAnswerParameters {
    /// Used to signal the end of the offer-answer parameter list.
    Done = 0,
    /// Whether to accept or reject audio.
    Audio = 1,
    /// Whether to accept or reject video.
    Video = 2,
    /// Whether to accept or reject data.
    Data = 3,
    /// The MediaDirection for the audio stream.
    AudioDirection = 4,
    /// The MediaDirection for the video stream.
    VideoDirection = 5,
    /// The AudioCodec for the audio stream.
    AudioCodec = 6,
    /// The VideoCodec for the video stream.
    VideoCodec = 7,
    /// The payload type for the audio stream.
    AudioPayloadType = 8,
    /// The payload type for the video stream.
    VideoPayloadType = 9,
    /// Whether to negotiate telephone events.
    AudioDtmf = 10,
    /// Whether to add RTCP-FB attributes.
    VideoRtcpfbDefaults = 11,
    /// Whether to add attributes for H.264 video.
    VideoH264Fmtp = 12,
}

#[derive(Debug)]
/// An SDP session description.
pub struct Sdp {
    pub ptr: *mut RawSdp, // annoyingly pub because of answer_sdp macro
}

/// An error indicating that we failed to parse an SDP for some reason.
#[derive(Debug, Clone)]
pub struct SdpParseError {
    buffer: Vec<u8>
}

impl Error for SdpParseError {
    fn description(&self) -> &str {
        str::from_utf8(&self.buffer).unwrap_or("SDP parsing failed, but the error was not valid UTF-8 :(")
    }
}

impl fmt::Display for SdpParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.description())
    }
}

impl Sdp {
    pub unsafe fn new(ptr: *mut RawSdp) -> Option<Self> {
        ptr.as_mut().map(|p| Self { ptr: p })
    }

    /// Parses an SDP offer string from a client into a structured SDP object.
    pub fn parse(offer: &CStr) -> Result<Self, SdpParseError> {
        let mut error_buffer = Vec::with_capacity(512);
        let error_ptr = error_buffer.as_mut_ptr() as *mut _;
        unsafe {
            let result = ffi::sdp::janus_sdp_parse(offer.as_ptr(), error_ptr, error_buffer.capacity());
            Sdp::new(result).ok_or_else(|| {
                error_buffer.set_len(libc::strlen(error_ptr));
                SdpParseError { buffer: error_buffer }
            })
        }
    }

    /// Gets the payload type number for a codec in this SDP, or None if the codec isn't present.
    pub fn get_payload_type(&self, codec_name: &CStr) -> Option<i32> {
        unsafe {
            match ffi::sdp::janus_sdp_get_codec_pt(self.ptr, codec_name.as_ptr()) {
                err if err < 0 => None,
                n => Some(n),
            }
        }
    }

    /// Rewrites any references from one dynamically assigned payload type in this SDP to another dynamically assigned
    /// payload type.
    pub fn rewrite_payload_type(&mut self, from: i32, to: i32) {
        let from_pt_string = from.to_string();
        let to_pt_string = to.to_string();
        for (_media, m_lines) in self.get_mlines() {
            unsafe {
                for m_line in m_lines {
                    // 1. replace the payload type ID in this media line's payload type list
                    if !glib::g_list_find(m_line.ptypes, from as *const _).is_null() {
                        // payload type data in the list is cast to pointers
                        m_line.ptypes = glib::g_list_remove(m_line.ptypes, from as *const _);
                        m_line.ptypes = glib::g_list_prepend(m_line.ptypes, to as *mut _);
                    }
                    // 2. rewrite the values of attribute lines with the old payload type to have the new payload type
                    let mut attr_node = m_line.attributes;
                    while let Some(node) = attr_node.as_ref() {
                        let next = node.next; // we might delete this link, so grab next now!
                        let data = node.data as *mut RawAttribute;
                        let attr = data.as_ref().expect("Null data in SDP attribute node :(");
                        let name = CStr::from_ptr(attr.name).to_str().expect("Invalid attribute name in SDP :(");
                        if MEDIA_PAYLOAD_ATTRIBUTES.contains(&name) {
                            // each of the attributes with payload types in the values look like "$pt $stuff"
                            // where $stuff is specifying payload-type-specfic options; just rewrite $pt
                            let value = CStr::from_ptr(attr.value).to_str().expect("Invalid attribute value in SDP :(");
                            if value.starts_with(&from_pt_string) {
                                let new_val = if name == "fmtp" {
                                    CString::new("107 profile-level-id=42e01f;packetization-mode=1").unwrap()
                                } else {
                                    CString::new(value.replacen(&from_pt_string, &to_pt_string, 1)).unwrap()
                                };
                                // value string is copied into the attribute
                                let new_attr = ffi::sdp::janus_sdp_attribute_create(attr.name, new_val.as_ptr());
                                m_line.attributes = glib::g_list_prepend(m_line.attributes, new_attr as *mut _);
                                m_line.attributes = glib::g_list_delete_link(m_line.attributes, attr_node);
                                ffi::sdp::janus_sdp_attribute_destroy(data);
                            }
                        }
                        attr_node = next;
                    }
                }
            }
        }
    }

    /// Returns a map of all the SDP media lines per SDP media type.
    pub fn get_mlines(&self) -> HashMap<MediaType, Vec<&mut RawMLine>> {
        let mut result = HashMap::new();
        unsafe {
            let mut ml_node = (*self.ptr).m_lines;
            while let Some(node) = ml_node.as_ref() {
                let ml = (node.data as *mut RawMLine).as_mut().expect("Null data in SDP media node :(");
                result.entry(ml.type_).or_insert_with(Vec::new).push(ml);
                ml_node = node.next;
            }
            result
	}
    }

    /// Writes this SDP into a string.
    pub fn to_string(&self) -> GLibString {
        unsafe {
            let sdp = ffi::sdp::janus_sdp_write(self.ptr);
            GLibString::from_chars(sdp).expect("Mysterious error writing SDP to string :(")
        }
    }
}

impl Deref for Sdp {
    type Target = RawSdp;

    fn deref(&self) -> &RawSdp {
        unsafe { &*self.ptr }
    }
}

impl Drop for Sdp {
    fn drop(&mut self) {
        unsafe {
            #[cfg(not(feature="refcount"))]
            ffi::sdp::janus_sdp_free(self.ptr);
            #[cfg(feature="refcount")]
            ffi::sdp::janus_sdp_destroy(self.ptr);
        }
    }
}

impl Serialize for Sdp {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
        self.to_string().serialize(serializer)
    }
}

impl<'de> Deserialize<'de> for Sdp {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
        struct SdpVisitor;
        impl<'de> Visitor<'de> for SdpVisitor {
            type Value = Sdp;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("an SDP string")
            }

            fn visit_str<E>(self, value: &str) -> Result<Sdp, E> where E: de::Error {
                if let Ok(cs_value) = CString::new(value) {
                    if let Ok(sdp) = Sdp::parse(&cs_value) {
                        return Ok(sdp)
                    }
                }
                Err(E::invalid_value(Unexpected::Str(value), &self))
            }
        }
        deserializer.deserialize_str(SdpVisitor)
    }
}

unsafe impl Send for Sdp {}

#[macro_export]
/// Given an SDP offer from a client, generates an SDP answer.
/// (This has to be a macro because `generate_answer` is variadic.)
macro_rules! answer_sdp {
    ($sdp:expr $(, $param:expr, $value:expr)* $(,)*) => {{
        unsafe {
            let result = $crate::sdp::generate_answer(
                $sdp.ptr,
                $($param, $value,)*
                $crate::sdp::OfferAnswerParameters::Done
            );
            $crate::sdp::Sdp::new(result).expect("Mysterious error generating SDP answer :(")
        }
    }}
}

#[macro_export]
/// Generates an SDP offer given some parameters.
/// (This has to be a macro because `generate_offer` is variadic.)
macro_rules! offer_sdp {
    ($name:expr, $address:expr $(, $param:expr, $value:expr)* $(,)*) => {{
        unsafe {
            let result = $crate::sdp::generate_offer(
                $name,
                $address,
                $($param, $value,)*
                $crate::sdp::OfferAnswerParameters::Done
            );
            $crate::sdp::Sdp::new(result).expect("Mysterious error generating SDP offer :(")
        }
    }}
}
<commit_msg>Fix macro definition (thanks @ivanovaleksey)<commit_after>/// Utilities to write SDP offers and answers using Janus's SDP parsing machinery.

use super::ffi;
use super::glib;
use super::libc;
use super::serde::de::{self, Deserialize, Deserializer, Unexpected, Visitor};
use super::serde::ser::{Serialize, Serializer};
pub use ffi::sdp::janus_sdp_generate_answer as generate_answer;
pub use ffi::sdp::janus_sdp_generate_offer as generate_offer;
use std::collections::HashMap;
use std::error::Error;
use std::fmt;
use std::ffi::{CStr, CString};
use std::ops::Deref;
use std::str;
use utils::GLibString;

pub type RawSdp = ffi::sdp::janus_sdp;
pub type RawMLine = ffi::sdp::janus_sdp_mline;
pub type RawAttribute = ffi::sdp::janus_sdp_attribute;
pub use ffi::sdp::janus_sdp_mtype as MediaType;
pub use ffi::sdp::janus_sdp_mdirection as MediaDirection;

// courtesy of c_string crate, which also has some other stuff we aren't interested in
// taking in as a dependency here.
macro_rules! c_str {
    ($lit:expr) => {
        unsafe {
            ::std::ffi::CStr::from_ptr(concat!($lit, "\0").as_ptr() as *const ::std::os::raw::c_char)
        }
    }
}

/// SDP attributes which may refer to a specific RTP payload type.
static MEDIA_PAYLOAD_ATTRIBUTES: [&'static str; 3] = ["rtpmap", "fmtp", "rtcp-fb"];

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
/// Available Janus audio codecs. See utils.c.
pub enum AudioCodec {
    Opus,
    Pcmu,
    Pcma,
    G722,
    Isac16,
    Isac32,
}

impl AudioCodec {
    pub fn to_str(&self) -> &'static str {
        self.to_cstr().to_str().unwrap()
    }
    pub fn to_cstr(&self) -> &'static CStr {
        match *self {
            AudioCodec::Opus => c_str!("opus"),
            AudioCodec::Pcmu => c_str!("pcmu"),
            AudioCodec::Pcma => c_str!("pcma"),
            AudioCodec::G722 => c_str!("g722"),
            AudioCodec::Isac16 => c_str!("isac16"),
            AudioCodec::Isac32 => c_str!("isac32"),
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
/// Available Janus video codecs. See utils.c.
pub enum VideoCodec {
    Vp8,
    Vp9,
    H264,
}

impl VideoCodec {
    pub fn to_str(&self) -> &'static str {
        self.to_cstr().to_str().unwrap()
    }
    pub fn to_cstr(&self) -> &'static CStr {
        match *self {
            VideoCodec::Vp8 => c_str!("vp8"),
            VideoCodec::Vp9 => c_str!("vp9"),
            VideoCodec::H264 => c_str!("h264"),
        }
    }
}

#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
/// Parameters controlling SDP offer answering behavior. Used as keys in the parameter list
/// for `janus_sdp_generate_answer`. See sdp-utils.h in the Janus source for more details.
pub enum OfferAnswerParameters {
    /// Used to signal the end of the offer-answer parameter list.
    Done = 0,
    /// Whether to accept or reject audio.
    Audio = 1,
    /// Whether to accept or reject video.
    Video = 2,
    /// Whether to accept or reject data.
    Data = 3,
    /// The MediaDirection for the audio stream.
    AudioDirection = 4,
    /// The MediaDirection for the video stream.
    VideoDirection = 5,
    /// The AudioCodec for the audio stream.
    AudioCodec = 6,
    /// The VideoCodec for the video stream.
    VideoCodec = 7,
    /// The payload type for the audio stream.
    AudioPayloadType = 8,
    /// The payload type for the video stream.
    VideoPayloadType = 9,
    /// Whether to negotiate telephone events.
    AudioDtmf = 10,
    /// Whether to add RTCP-FB attributes.
    VideoRtcpfbDefaults = 11,
    /// Whether to add attributes for H.264 video.
    VideoH264Fmtp = 12,
}

#[derive(Debug)]
/// An SDP session description.
pub struct Sdp {
    pub ptr: *mut RawSdp, // annoyingly pub because of answer_sdp macro
}

/// An error indicating that we failed to parse an SDP for some reason.
#[derive(Debug, Clone)]
pub struct SdpParseError {
    buffer: Vec<u8>
}

impl Error for SdpParseError {
    fn description(&self) -> &str {
        str::from_utf8(&self.buffer).unwrap_or("SDP parsing failed, but the error was not valid UTF-8 :(")
    }
}

impl fmt::Display for SdpParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.description())
    }
}

impl Sdp {
    pub unsafe fn new(ptr: *mut RawSdp) -> Option<Self> {
        ptr.as_mut().map(|p| Self { ptr: p })
    }

    /// Parses an SDP offer string from a client into a structured SDP object.
    pub fn parse(offer: &CStr) -> Result<Self, SdpParseError> {
        let mut error_buffer = Vec::with_capacity(512);
        let error_ptr = error_buffer.as_mut_ptr() as *mut _;
        unsafe {
            let result = ffi::sdp::janus_sdp_parse(offer.as_ptr(), error_ptr, error_buffer.capacity());
            Sdp::new(result).ok_or_else(|| {
                error_buffer.set_len(libc::strlen(error_ptr));
                SdpParseError { buffer: error_buffer }
            })
        }
    }

    /// Gets the payload type number for a codec in this SDP, or None if the codec isn't present.
    pub fn get_payload_type(&self, codec_name: &CStr) -> Option<i32> {
        unsafe {
            match ffi::sdp::janus_sdp_get_codec_pt(self.ptr, codec_name.as_ptr()) {
                err if err < 0 => None,
                n => Some(n),
            }
        }
    }

    /// Rewrites any references from one dynamically assigned payload type in this SDP to another dynamically assigned
    /// payload type.
    pub fn rewrite_payload_type(&mut self, from: i32, to: i32) {
        let from_pt_string = from.to_string();
        let to_pt_string = to.to_string();
        for (_media, m_lines) in self.get_mlines() {
            unsafe {
                for m_line in m_lines {
                    // 1. replace the payload type ID in this media line's payload type list
                    if !glib::g_list_find(m_line.ptypes, from as *const _).is_null() {
                        // payload type data in the list is cast to pointers
                        m_line.ptypes = glib::g_list_remove(m_line.ptypes, from as *const _);
                        m_line.ptypes = glib::g_list_prepend(m_line.ptypes, to as *mut _);
                    }
                    // 2. rewrite the values of attribute lines with the old payload type to have the new payload type
                    let mut attr_node = m_line.attributes;
                    while let Some(node) = attr_node.as_ref() {
                        let next = node.next; // we might delete this link, so grab next now!
                        let data = node.data as *mut RawAttribute;
                        let attr = data.as_ref().expect("Null data in SDP attribute node :(");
                        let name = CStr::from_ptr(attr.name).to_str().expect("Invalid attribute name in SDP :(");
                        if MEDIA_PAYLOAD_ATTRIBUTES.contains(&name) {
                            // each of the attributes with payload types in the values look like "$pt $stuff"
                            // where $stuff is specifying payload-type-specfic options; just rewrite $pt
                            let value = CStr::from_ptr(attr.value).to_str().expect("Invalid attribute value in SDP :(");
                            if value.starts_with(&from_pt_string) {
                                let new_val = if name == "fmtp" {
                                    CString::new("107 profile-level-id=42e01f;packetization-mode=1").unwrap()
                                } else {
                                    CString::new(value.replacen(&from_pt_string, &to_pt_string, 1)).unwrap()
                                };
                                // value string is copied into the attribute
                                let new_attr = ffi::sdp::janus_sdp_attribute_create(attr.name, new_val.as_ptr());
                                m_line.attributes = glib::g_list_prepend(m_line.attributes, new_attr as *mut _);
                                m_line.attributes = glib::g_list_delete_link(m_line.attributes, attr_node);
                                ffi::sdp::janus_sdp_attribute_destroy(data);
                            }
                        }
                        attr_node = next;
                    }
                }
            }
        }
    }

    /// Returns a map of all the SDP media lines per SDP media type.
    pub fn get_mlines(&self) -> HashMap<MediaType, Vec<&mut RawMLine>> {
        let mut result = HashMap::new();
        unsafe {
            let mut ml_node = (*self.ptr).m_lines;
            while let Some(node) = ml_node.as_ref() {
                let ml = (node.data as *mut RawMLine).as_mut().expect("Null data in SDP media node :(");
                result.entry(ml.type_).or_insert_with(Vec::new).push(ml);
                ml_node = node.next;
            }
            result
	}
    }

    /// Writes this SDP into a string.
    pub fn to_string(&self) -> GLibString {
        unsafe {
            let sdp = ffi::sdp::janus_sdp_write(self.ptr);
            GLibString::from_chars(sdp).expect("Mysterious error writing SDP to string :(")
        }
    }
}

impl Deref for Sdp {
    type Target = RawSdp;

    fn deref(&self) -> &RawSdp {
        unsafe { &*self.ptr }
    }
}

impl Drop for Sdp {
    fn drop(&mut self) {
        unsafe {
            #[cfg(not(feature="refcount"))]
            ffi::sdp::janus_sdp_free(self.ptr);
            #[cfg(feature="refcount")]
            ffi::sdp::janus_sdp_destroy(self.ptr);
        }
    }
}

impl Serialize for Sdp {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
        self.to_string().serialize(serializer)
    }
}

impl<'de> Deserialize<'de> for Sdp {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
        struct SdpVisitor;
        impl<'de> Visitor<'de> for SdpVisitor {
            type Value = Sdp;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("an SDP string")
            }

            fn visit_str<E>(self, value: &str) -> Result<Sdp, E> where E: de::Error {
                if let Ok(cs_value) = CString::new(value) {
                    if let Ok(sdp) = Sdp::parse(&cs_value) {
                        return Ok(sdp)
                    }
                }
                Err(E::invalid_value(Unexpected::Str(value), &self))
            }
        }
        deserializer.deserialize_str(SdpVisitor)
    }
}

unsafe impl Send for Sdp {}

#[macro_export]
/// Given an SDP offer from a client, generates an SDP answer.
/// (This has to be a macro because `generate_answer` is variadic.)
macro_rules! answer_sdp {
    ($sdp:expr $(, $param:expr, $value:expr)* $(,)*) => {{
        unsafe {
            let result = $crate::sdp::generate_answer(
                $sdp.ptr,
                $($param, $value,)*
                $crate::sdp::OfferAnswerParameters::Done
            );
            $crate::sdp::Sdp::new(result).expect("Mysterious error generating SDP answer :(")
        }
    }}
}

#[macro_export]
/// Generates an SDP offer given some parameters.
/// (This has to be a macro because `generate_offer` is variadic.)
macro_rules! offer_sdp {
    ($name:expr, $address:expr $(, $param:expr, $value:expr)* $(,)*) => {{
        unsafe {
            let result = $crate::sdp::generate_offer(
                $name,
                $address,
                $($param, $value,)*
                $crate::sdp::OfferAnswerParameters::Done
            );
            $crate::sdp::Sdp::new(result).expect("Mysterious error generating SDP offer :(")
        }
    }}
}
<|endoftext|>


<commit_before>
use parser::ParsingPassLogStream;
use parser::parse_with_rule;
use parser::prepare_rules;

use std::io::Read;
use std::collections::HashMap;

use char_iter;

use xml_sax::*;

pub struct SaxParser<'a> {
    content_handler: Option<&'a mut ContentHandler>,
    counter: i64,
    element_names: Vec<String>,
    attribute_values: Vec<String>,
    attributes: Attributes,
}

#[derive(Clone)]
struct Attribute {
    uri: String,
    qualified_name: String,
    local_name: String,
    value: String,
}

impl SAXAttribute for Attribute {
    fn get_value(&self) -> &str {
        &self.value
    }
}

impl Attribute {
    fn new() -> Attribute {
        Attribute {
            uri: String::new(),
            qualified_name: String::new(),
            local_name: String::new(),
            value: String::new(),
        }
    }
}

struct Attributes {
    attribute_vec: Vec<Box<Attribute>>,
    attribute_qname_map: HashMap<String, usize>,
    attribute_uri_local_map: HashMap<String, usize>,
}
impl SAXAttributes for Attributes {
    fn get_length(&self) -> usize {
        self.attribute_vec.len()
    }

    fn get_by_index(&self, index: usize) -> Option<Box<SAXAttribute>> {
        match self.attribute_vec.get(index) {
            // Some(val) => Some(Box::new((*val).clone())),
            Some(val) => {
                let x = *val.clone();
                Some(Box::new(x) as Box<SAXAttribute>)
            }
            None => None,
        }

    }

    fn iter(&self) -> Box<Iterator<Item = Box<SAXAttribute>>> {
        let x = AttributesIter {
            attribute_vec: self.attribute_vec.clone(),
            cur: 0,
        };
        return Box::new(x);
    }
}

pub struct AttributesIter {
    attribute_vec: Vec<Box<Attribute>>,
    cur: usize,
}

impl Iterator for AttributesIter {
    type Item = Box<SAXAttribute>;
    fn next(&mut self) -> Option<Box<SAXAttribute>> {
        let r = match self.attribute_vec.get(self.cur) {
            Some(val) => Some((*val).clone() as Box<SAXAttribute>),
            None => None,
        };

        self.cur += 1;
        return r;
    }
}

impl Attributes {
    fn new() -> Attributes {
        Attributes {
            attribute_vec: Vec::new(),
            attribute_qname_map: HashMap::new(),
            attribute_uri_local_map: HashMap::new(),
        }
    }
    fn clear(&mut self) {

        self.attribute_vec.clear();
        self.attribute_qname_map.clear();
        self.attribute_uri_local_map.clear();
    }
}

impl<'a> ParsingPassLogStream for SaxParser<'a> {
    fn try(&mut self, rulename: &str, starting_pos: usize) -> () {

        if rulename == "STag" || rulename == "EmptyElemTag" {
            self.element_names.clear();
            self.attribute_values.clear();
            self.attributes.clear();
        }
    }

    fn pass(&mut self,
            rulename: &str,
            chars: &Vec<char>,
            starting_pos: usize,
            ending_pos: usize)
            -> () {

        if starting_pos > ending_pos || ending_pos == 0 {
            return;
        }
        if (rulename == "Name") {
            let s: String = chars[starting_pos..ending_pos]
                .into_iter()
                .cloned()
                .collect();
            self.element_names.push(s);

        }

        if rulename == "AttValue" {
            let s: String = chars[starting_pos..ending_pos]
                .into_iter()
                .skip(1)
                .take(ending_pos - starting_pos - 2)
                .cloned()
                .collect();
            self.attribute_values.push(s);
        }

        if (rulename == "Attribute") {
            let name: String = self.element_names.pop().unwrap();
            let value: String = self.attribute_values.pop().unwrap();
            let mut attr: Attribute = Attribute::new();
            attr.qualified_name = name;
            attr.value = value;
            self.attributes.attribute_vec.push(Box::new(attr));
        }

        if (rulename == "element") {
            self.counter = self.counter + 1;
        }

        if (rulename == "STag") {
            let name: String = self.element_names.pop().unwrap();
            self.content_handler.as_mut().unwrap().start_element(&name, &self.attributes);
        }

        if (rulename == "EmptyElemTag") {

            let name: String = self.element_names.pop().unwrap();
            self.content_handler.as_mut().unwrap().start_element(&name, &self.attributes);
        }

        if (rulename == "ETag") {
            let s: String = chars[starting_pos..ending_pos].into_iter().cloned().collect();
            self.content_handler.as_mut().unwrap().end_element(&s);
        }
        if rulename == "CharData?" {
            let s: String = chars[starting_pos..ending_pos].into_iter().cloned().collect();
            self.content_handler.as_mut().unwrap().characters(&s);
        }
    }
}

impl<'a> SaxParser<'a> {
    pub fn new() -> SaxParser<'a> {
        return SaxParser {
            content_handler: None,
            counter: 0,
            element_names: Vec::new(),
            attribute_values: Vec::new(),
            attributes: Attributes::new(),
        };
    }
    pub fn set_content_handler<T: ContentHandler>(&mut self, content_handler: &'a mut T) {
        self.content_handler = Some(content_handler);
    }

    pub fn parse<R: Read>(mut self, read: R) {

        let parser_rules = prepare_rules();
        let rule = &parser_rules.rule_vec[*parser_rules.rule_registry.get("document").unwrap()];

        let mut resume_vec = Vec::new();
        let mut state_vec = Vec::new();
        let offset: usize = 0;

        let mut chars: Vec<char> = Vec::new();
        let citer = char_iter::chars(read);

        let mut chunk_count: usize = 0;
        for ch in citer {

            if ch.is_ok() {

                chars.push(ch.unwrap());
                chunk_count += 1;
                if chunk_count >= 10000 {
                    chunk_count = 0;

                    let result = parse_with_rule(&parser_rules.rule_vec,
                                                 &rule,
                                                 &chars,
                                                 0,
                                                 offset,
                                                 &mut resume_vec,
                                                 &mut state_vec,
                                                 self);
                    self = result.0;

                    let mut erasable_pos: usize = 0;
                    let mut split_pos: usize = 0;
                    for (index, state) in state_vec.iter().enumerate() {
                        if state.2 {
                            // no backtrack req true
                            erasable_pos = state.1; //pos
                            split_pos = index + 1;
                        } else {
                            split_pos = index;
                            break;
                        }
                    }

                    for state in &mut state_vec {
                        state.1 = 0;
                    }
                    state_vec.split_off(split_pos);

                    state_vec.reverse();
                    resume_vec = state_vec;

                    state_vec = Vec::new();
                    // starting position can be different erasable_pos?
                    &self.content_handler.as_mut().unwrap().offset(erasable_pos);
                    chars = chars.split_off(erasable_pos);

                }
            } else {

                break;
            }
        }

        // last bit
        let result = parse_with_rule(&parser_rules.rule_vec,
                                     &rule,
                                     &chars,
                                     0,
                                     offset,
                                     &mut resume_vec,
                                     &mut state_vec,
                                     self);
        self = result.0;

        &self.content_handler.as_mut().unwrap().offset(chars.len());

    }
}
<commit_msg>fixed ETag name<commit_after>
use parser::ParsingPassLogStream;
use parser::parse_with_rule;
use parser::prepare_rules;

use std::io::Read;
use std::collections::HashMap;

use char_iter;

use xml_sax::*;

pub struct SaxParser<'a> {
    content_handler: Option<&'a mut ContentHandler>,
    counter: i64,
    element_names: Vec<String>,
    attribute_values: Vec<String>,
    attributes: Attributes,
}

#[derive(Clone)]
struct Attribute {
    uri: String,
    qualified_name: String,
    local_name: String,
    value: String,
}

impl SAXAttribute for Attribute {
    fn get_value(&self) -> &str {
        &self.value
    }
}

impl Attribute {
    fn new() -> Attribute {
        Attribute {
            uri: String::new(),
            qualified_name: String::new(),
            local_name: String::new(),
            value: String::new(),
        }
    }
}

struct Attributes {
    attribute_vec: Vec<Box<Attribute>>,
    attribute_qname_map: HashMap<String, usize>,
    attribute_uri_local_map: HashMap<String, usize>,
}
impl SAXAttributes for Attributes {
    fn get_length(&self) -> usize {
        self.attribute_vec.len()
    }

    fn get_by_index(&self, index: usize) -> Option<Box<SAXAttribute>> {
        match self.attribute_vec.get(index) {
            // Some(val) => Some(Box::new((*val).clone())),
            Some(val) => {
                let x = *val.clone();
                Some(Box::new(x) as Box<SAXAttribute>)
            }
            None => None,
        }

    }

    fn iter(&self) -> Box<Iterator<Item = Box<SAXAttribute>>> {
        let x = AttributesIter {
            attribute_vec: self.attribute_vec.clone(),
            cur: 0,
        };
        return Box::new(x);
    }
}

pub struct AttributesIter {
    attribute_vec: Vec<Box<Attribute>>,
    cur: usize,
}

impl Iterator for AttributesIter {
    type Item = Box<SAXAttribute>;
    fn next(&mut self) -> Option<Box<SAXAttribute>> {
        let r = match self.attribute_vec.get(self.cur) {
            Some(val) => Some((*val).clone() as Box<SAXAttribute>),
            None => None,
        };

        self.cur += 1;
        return r;
    }
}

impl Attributes {
    fn new() -> Attributes {
        Attributes {
            attribute_vec: Vec::new(),
            attribute_qname_map: HashMap::new(),
            attribute_uri_local_map: HashMap::new(),
        }
    }
    fn clear(&mut self) {

        self.attribute_vec.clear();
        self.attribute_qname_map.clear();
        self.attribute_uri_local_map.clear();
    }
}

impl<'a> ParsingPassLogStream for SaxParser<'a> {
    fn try(&mut self, rulename: &str, starting_pos: usize) -> () {

        if rulename == "STag" || rulename == "EmptyElemTag" {
            self.element_names.clear();
            self.attribute_values.clear();
            self.attributes.clear();
        }
    }

    fn pass(&mut self,
            rulename: &str,
            chars: &Vec<char>,
            starting_pos: usize,
            ending_pos: usize)
            -> () {

        if starting_pos > ending_pos || ending_pos == 0 {
            return;
        }
        if rulename == "Name" {
            let s: String = chars[starting_pos..ending_pos]
                .into_iter()
                .cloned()
                .collect();
            self.element_names.push(s);

        }

        if rulename == "AttValue" {
            let s: String = chars[starting_pos..ending_pos]
                .into_iter()
                .skip(1)
                .take(ending_pos - starting_pos - 2)
                .cloned()
                .collect();
            self.attribute_values.push(s);
        }

        if rulename == "Attribute" {
            let name: String = self.element_names.pop().unwrap();
            let value: String = self.attribute_values.pop().unwrap();
            let mut attr: Attribute = Attribute::new();
            attr.qualified_name = name;
            attr.value = value;
            self.attributes.attribute_vec.push(Box::new(attr));
        }

        if rulename == "element" {
            self.counter = self.counter + 1;
        }

        if rulename == "STag" {
            let name: String = self.element_names.pop().unwrap();
            self.content_handler.as_mut().unwrap().start_element(&name, &self.attributes);
        }

        if rulename == "EmptyElemTag" {

            let name: String = self.element_names.pop().unwrap();
            self.content_handler.as_mut().unwrap().start_element(&name, &self.attributes);
        }

        if rulename == "ETag" {
            let name: String = self.element_names.pop().unwrap();
            self.content_handler.as_mut().unwrap().end_element(&name);
        }
        if rulename == "CharData?" {
            let s: String = chars[starting_pos..ending_pos].into_iter().cloned().collect();
            self.content_handler.as_mut().unwrap().characters(&s);
        }
    }
}

impl<'a> SaxParser<'a> {
    pub fn new() -> SaxParser<'a> {
        return SaxParser {
            content_handler: None,
            counter: 0,
            element_names: Vec::new(),
            attribute_values: Vec::new(),
            attributes: Attributes::new(),
        };
    }
    pub fn set_content_handler<T: ContentHandler>(&mut self, content_handler: &'a mut T) {
        self.content_handler = Some(content_handler);
    }

    pub fn parse<R: Read>(mut self, read: R) {

        let parser_rules = prepare_rules();
        let rule = &parser_rules.rule_vec[*parser_rules.rule_registry.get("document").unwrap()];

        let mut resume_vec = Vec::new();
        let mut state_vec = Vec::new();
        let offset: usize = 0;

        let mut chars: Vec<char> = Vec::new();
        let citer = char_iter::chars(read);

        let mut chunk_count: usize = 0;
        for ch in citer {

            if ch.is_ok() {

                chars.push(ch.unwrap());
                chunk_count += 1;
                if chunk_count >= 10000 {
                    chunk_count = 0;

                    let result = parse_with_rule(&parser_rules.rule_vec,
                                                 &rule,
                                                 &chars,
                                                 0,
                                                 offset,
                                                 &mut resume_vec,
                                                 &mut state_vec,
                                                 self);
                    self = result.0;

                    let mut erasable_pos: usize = 0;
                    let mut split_pos: usize = 0;
                    for (index, state) in state_vec.iter().enumerate() {
                        if state.2 {
                            // no backtrack req true
                            erasable_pos = state.1; //pos
                            split_pos = index + 1;
                        } else {
                            split_pos = index;
                            break;
                        }
                    }

                    for state in &mut state_vec {
                        state.1 = 0;
                    }
                    state_vec.split_off(split_pos);

                    state_vec.reverse();
                    resume_vec = state_vec;

                    state_vec = Vec::new();
                    // starting position can be different erasable_pos?
                    &self.content_handler.as_mut().unwrap().offset(erasable_pos);
                    chars = chars.split_off(erasable_pos);

                }
            } else {

                break;
            }
        }

        // last bit
        let result = parse_with_rule(&parser_rules.rule_vec,
                                     &rule,
                                     &chars,
                                     0,
                                     offset,
                                     &mut resume_vec,
                                     &mut state_vec,
                                     self);
        self = result.0;

        &self.content_handler.as_mut().unwrap().offset(chars.len());

    }
}
<|endoftext|>


<commit_before>//! Provides SDK Information
use std::fs;
use std::io::{Read, Write, Seek};
use std::path::{Path, PathBuf};

use zip;
use walkdir;
use regex::Regex;
use mach_object::Error as MachError;

use super::{Result, Error, ErrorKind};
use super::dsym::Object;
use super::memdbdump::MemDbBuilder;

enum ObjectIterSource {
    Zip {
        archive: zip::ZipArchive<fs::File>,
        idx: usize,
    },
    Dir {
        base: PathBuf,
        dir_iter: walkdir::Iter,
    }
}

fn get_sdk_name_from_folder(folder: &str) -> Option<&'static str> {
    match folder {
        "iOS DeviceSupport" => Some("iOS"),
        "tvOS DeviceSupport" => Some("tvOS"),
        _ => None,
    }
}

/// Information of the SDK
#[derive(Debug, Clone)]
pub struct SdkInfo {
    name: String,
    version_major: u32,
    version_minor: u32,
    version_patchlevel: u32,
    build: String,
    flavour: Option<String>,
}

/// Iterates over all objects in an SDK
pub struct ObjectsIter {
    source: ObjectIterSource,
}

/// Helper struct to process an SDK from the FS or a ZIP
pub struct Sdk {
    path: PathBuf,
    info: SdkInfo,
}

impl SdkInfo {

    /// Manually construct an SDK info
    pub fn new(name: &str, version_major: u32, version_minor: u32,
               version_patchlevel: u32, build: &str, flavour: Option<&str>)
        -> SdkInfo
    {
        SdkInfo {
            name: name.to_string(),
            version_major: version_major,
            version_minor: version_minor,
            version_patchlevel: version_patchlevel,
            build: build.to_string(),
            flavour: flavour.map(|x| x.to_string()),
        }
    }

    /// Load an SDK info from a given path
    ///
    /// If the parse cannot be parsed for an SDK info `None` is returned.
    pub fn from_path<P: AsRef<Path>>(path: P) -> Option<SdkInfo> {
        lazy_static! {
            static ref SDK_FILENAME_RE: Regex = Regex::new(r"(?x)
                ^
                    (\d+)\.(\d+)(?:\.(\d+))?
                    \s+
                    \(([a-zA-Z0-9]+)\)
                    (?:\.zip|\.memdb)?
                $
            ").unwrap();
        }

        let p = path.as_ref();
        let folder = try_opt!(p.parent().and_then(|x| x.file_name()).and_then(|x| x.to_str()));
        let filename = try_opt!(p.file_name().and_then(|x| x.to_str()));
        let caps = try_opt!(SDK_FILENAME_RE.captures(filename));
        Some(SdkInfo::new(
            try_opt!(get_sdk_name_from_folder(folder)),
            try_opt!(caps.get(1).unwrap().as_str().parse().ok()),
            try_opt!(caps.get(2).unwrap().as_str().parse().ok()),
            try_opt!(caps.get(3).map(|x| x.as_str()).unwrap_or("0").parse().ok()),
            try_opt!(caps.get(4).map(|x| x.as_str())),
            None,
        ))
    }

    /// The SDK name (iOS, tvOS etc.)
    pub fn name(&self) -> &str {
        &self.name
    }

    /// The major version of the SDK.
    pub fn version_major(&self) -> u32 {
        self.version_major
    }

    /// The minor version of the SDK
    pub fn version_minor(&self) -> u32 {
        self.version_minor
    }

    /// The patchlevel version of the SDK
    pub fn version_patchlevel(&self) -> u32 {
        self.version_patchlevel
    }

    /// The build of the SDK
    pub fn build(&self) -> &str {
        &self.build
    }

    /// The optional flavour of the SDK
    pub fn flavour(&self) -> Option<&str> {
        self.flavour.as_ref().map(|x| &**x)
    }
}

impl ObjectIterSource {
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<ObjectIterSource> {
        let md = fs::metadata(path.as_ref())?;
        if md.is_file() {
            let f = fs::File::open(path.as_ref())?;
            let zip = zip::ZipArchive::new(f)?;
            Ok(ObjectIterSource::Zip {
                archive: zip,
                idx: 0,
            })
        } else {
            Ok(ObjectIterSource::Dir {
                base: path.as_ref().join("Symbols"),
                dir_iter: walkdir::WalkDir::new(path.as_ref()).into_iter(),
            })
        }
    }
}

fn strip_archive_file_prefix(path: &str) -> &str {
    let mut iter = path.splitn(2, '/');

    // Symbols/foo/bar -> foo/bar
    if let Some("Symbols") = iter.next() {
        if let Some(rest) = iter.next() {
            return rest;
        }
    }

    // Foo/Symbols/foo/bar -> foo/bar
    let mut iter = path.splitn(3, '/');
    if let Some(_) = iter.next() {
        if let Some("Symbols") = iter.next() {
            if let Some(rest) = iter.next() {
                return rest;
            }
        }
    }

    path
}

impl<'a> Iterator for ObjectsIter {
    type Item = Result<(String, Object<'static>)>;

    fn next(&mut self) -> Option<Result<(String, Object<'static>)>> {
        macro_rules! try_return_obj {
            ($expr:expr, $name:expr) => {
                match $expr {
                    Ok(rv) => {
                        return Some(Ok(($name.to_string(), rv)));
                    }
                    Err(err) => {
                        if let &ErrorKind::MachO(ref mach_err) = err.kind() {
                            if let &MachError::LoadError(_) = mach_err {
                                continue;
                            }
                        }
                        return Some(Err(err.into()));
                    }
                }
            }
        }

        loop {
            match self.source {
                ObjectIterSource::Zip { ref mut archive, ref mut idx } => {
                    if *idx >= archive.len() {
                        break;
                    }
                    let mut f = iter_try!(archive.by_index(*idx));
                    *idx += 1;
                    let mut buf : Vec<u8> = vec![];
                    if iter_try!(f.read_to_end(&mut buf)) > 0 {
                        try_return_obj!(Object::from_vec(buf),
                            strip_archive_file_prefix(f.name()));
                    }
                }
                ObjectIterSource::Dir { ref base, ref mut dir_iter } => {
                    if let Some(dent_res) = dir_iter.next() {
                        let dent = iter_try!(dent_res);
                        let md = iter_try!(dent.metadata());
                        if md.is_file() && md.len() > 0 {
                            let rp = dent.path().strip_prefix(base).unwrap_or(dent.path());
                            try_return_obj!(
                                Object::from_path(dent.path()),
                                rp.display());
                        }
                    } else {
                        break;
                    }
                }
            }
        }
        None
    }
}

impl Sdk {
    /// Constructs a processor from a file system path
    pub fn new<P: AsRef<Path>>(path: P) -> Result<Sdk> {
        let p = path.as_ref().to_path_buf();
        let sdk_info = SdkInfo::from_path(&p).ok_or_else(|| {
            Error::from(ErrorKind::UnknownSdk)
        })?;
        Ok(Sdk {
            path: p,
            info: sdk_info,
        })
    }

    /// Returns the SDK info (derived from the path)
    pub fn info(&self) -> &SdkInfo {
        &self.info
    }

    /// Returns an object iterator
    pub fn objects<'a>(&'a self) -> Result<ObjectsIter> {
        Ok(ObjectsIter {
            source: ObjectIterSource::from_path(&self.path)?,
        })
    }

    /// Writes a memdb file for the SDK
    ///
    /// This can then be later read with the `MemDb` type.
    pub fn dump_memdb<W: Write + Seek>(&self, writer: W) -> Result<()> {
        let mut builder = MemDbBuilder::new(writer, self.info())?;
        for obj_res in self.objects()? {
            let (filename, obj) = obj_res?;
            builder.write_object(&obj, Some(&filename))?;
        }
        builder.flush()?;
        Ok(())
    }
}
<commit_msg>Add PartialEq for SdkInfo<commit_after>//! Provides SDK Information
use std::fs;
use std::io::{Read, Write, Seek};
use std::path::{Path, PathBuf};

use zip;
use walkdir;
use regex::Regex;
use mach_object::Error as MachError;

use super::{Result, Error, ErrorKind};
use super::dsym::Object;
use super::memdbdump::MemDbBuilder;

enum ObjectIterSource {
    Zip {
        archive: zip::ZipArchive<fs::File>,
        idx: usize,
    },
    Dir {
        base: PathBuf,
        dir_iter: walkdir::Iter,
    }
}

fn get_sdk_name_from_folder(folder: &str) -> Option<&'static str> {
    match folder {
        "iOS DeviceSupport" => Some("iOS"),
        "tvOS DeviceSupport" => Some("tvOS"),
        _ => None,
    }
}

/// Information of the SDK
#[derive(Debug, Clone, PartialEq)]
pub struct SdkInfo {
    name: String,
    version_major: u32,
    version_minor: u32,
    version_patchlevel: u32,
    build: String,
    flavour: Option<String>,
}

/// Iterates over all objects in an SDK
pub struct ObjectsIter {
    source: ObjectIterSource,
}

/// Helper struct to process an SDK from the FS or a ZIP
pub struct Sdk {
    path: PathBuf,
    info: SdkInfo,
}

impl SdkInfo {

    /// Manually construct an SDK info
    pub fn new(name: &str, version_major: u32, version_minor: u32,
               version_patchlevel: u32, build: &str, flavour: Option<&str>)
        -> SdkInfo
    {
        SdkInfo {
            name: name.to_string(),
            version_major: version_major,
            version_minor: version_minor,
            version_patchlevel: version_patchlevel,
            build: build.to_string(),
            flavour: flavour.map(|x| x.to_string()),
        }
    }

    /// Load an SDK info from a given path
    ///
    /// If the parse cannot be parsed for an SDK info `None` is returned.
    pub fn from_path<P: AsRef<Path>>(path: P) -> Option<SdkInfo> {
        lazy_static! {
            static ref SDK_FILENAME_RE: Regex = Regex::new(r"(?x)
                ^
                    (\d+)\.(\d+)(?:\.(\d+))?
                    \s+
                    \(([a-zA-Z0-9]+)\)
                    (?:\.zip|\.memdb)?
                $
            ").unwrap();
        }

        let p = path.as_ref();
        let folder = try_opt!(p.parent().and_then(|x| x.file_name()).and_then(|x| x.to_str()));
        let filename = try_opt!(p.file_name().and_then(|x| x.to_str()));
        let caps = try_opt!(SDK_FILENAME_RE.captures(filename));
        Some(SdkInfo::new(
            try_opt!(get_sdk_name_from_folder(folder)),
            try_opt!(caps.get(1).unwrap().as_str().parse().ok()),
            try_opt!(caps.get(2).unwrap().as_str().parse().ok()),
            try_opt!(caps.get(3).map(|x| x.as_str()).unwrap_or("0").parse().ok()),
            try_opt!(caps.get(4).map(|x| x.as_str())),
            None,
        ))
    }

    /// The SDK name (iOS, tvOS etc.)
    pub fn name(&self) -> &str {
        &self.name
    }

    /// The major version of the SDK.
    pub fn version_major(&self) -> u32 {
        self.version_major
    }

    /// The minor version of the SDK
    pub fn version_minor(&self) -> u32 {
        self.version_minor
    }

    /// The patchlevel version of the SDK
    pub fn version_patchlevel(&self) -> u32 {
        self.version_patchlevel
    }

    /// The build of the SDK
    pub fn build(&self) -> &str {
        &self.build
    }

    /// The optional flavour of the SDK
    pub fn flavour(&self) -> Option<&str> {
        self.flavour.as_ref().map(|x| &**x)
    }
}

impl ObjectIterSource {
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<ObjectIterSource> {
        let md = fs::metadata(path.as_ref())?;
        if md.is_file() {
            let f = fs::File::open(path.as_ref())?;
            let zip = zip::ZipArchive::new(f)?;
            Ok(ObjectIterSource::Zip {
                archive: zip,
                idx: 0,
            })
        } else {
            Ok(ObjectIterSource::Dir {
                base: path.as_ref().join("Symbols"),
                dir_iter: walkdir::WalkDir::new(path.as_ref()).into_iter(),
            })
        }
    }
}

fn strip_archive_file_prefix(path: &str) -> &str {
    let mut iter = path.splitn(2, '/');

    // Symbols/foo/bar -> foo/bar
    if let Some("Symbols") = iter.next() {
        if let Some(rest) = iter.next() {
            return rest;
        }
    }

    // Foo/Symbols/foo/bar -> foo/bar
    let mut iter = path.splitn(3, '/');
    if let Some(_) = iter.next() {
        if let Some("Symbols") = iter.next() {
            if let Some(rest) = iter.next() {
                return rest;
            }
        }
    }

    path
}

impl<'a> Iterator for ObjectsIter {
    type Item = Result<(String, Object<'static>)>;

    fn next(&mut self) -> Option<Result<(String, Object<'static>)>> {
        macro_rules! try_return_obj {
            ($expr:expr, $name:expr) => {
                match $expr {
                    Ok(rv) => {
                        return Some(Ok(($name.to_string(), rv)));
                    }
                    Err(err) => {
                        if let &ErrorKind::MachO(ref mach_err) = err.kind() {
                            if let &MachError::LoadError(_) = mach_err {
                                continue;
                            }
                        }
                        return Some(Err(err.into()));
                    }
                }
            }
        }

        loop {
            match self.source {
                ObjectIterSource::Zip { ref mut archive, ref mut idx } => {
                    if *idx >= archive.len() {
                        break;
                    }
                    let mut f = iter_try!(archive.by_index(*idx));
                    *idx += 1;
                    let mut buf : Vec<u8> = vec![];
                    if iter_try!(f.read_to_end(&mut buf)) > 0 {
                        try_return_obj!(Object::from_vec(buf),
                            strip_archive_file_prefix(f.name()));
                    }
                }
                ObjectIterSource::Dir { ref base, ref mut dir_iter } => {
                    if let Some(dent_res) = dir_iter.next() {
                        let dent = iter_try!(dent_res);
                        let md = iter_try!(dent.metadata());
                        if md.is_file() && md.len() > 0 {
                            let rp = dent.path().strip_prefix(base).unwrap_or(dent.path());
                            try_return_obj!(
                                Object::from_path(dent.path()),
                                rp.display());
                        }
                    } else {
                        break;
                    }
                }
            }
        }
        None
    }
}

impl Sdk {
    /// Constructs a processor from a file system path
    pub fn new<P: AsRef<Path>>(path: P) -> Result<Sdk> {
        let p = path.as_ref().to_path_buf();
        let sdk_info = SdkInfo::from_path(&p).ok_or_else(|| {
            Error::from(ErrorKind::UnknownSdk)
        })?;
        Ok(Sdk {
            path: p,
            info: sdk_info,
        })
    }

    /// Returns the SDK info (derived from the path)
    pub fn info(&self) -> &SdkInfo {
        &self.info
    }

    /// Returns an object iterator
    pub fn objects<'a>(&'a self) -> Result<ObjectsIter> {
        Ok(ObjectsIter {
            source: ObjectIterSource::from_path(&self.path)?,
        })
    }

    /// Writes a memdb file for the SDK
    ///
    /// This can then be later read with the `MemDb` type.
    pub fn dump_memdb<W: Write + Seek>(&self, writer: W) -> Result<()> {
        let mut builder = MemDbBuilder::new(writer, self.info())?;
        for obj_res in self.objects()? {
            let (filename, obj) = obj_res?;
            builder.write_object(&obj, Some(&filename))?;
        }
        builder.flush()?;
        Ok(())
    }
}
<|endoftext|>