<commit_before>use crate::deserialize::*;
use crate::errors::*;

use std::path::Path;

/// sort <link> and <joint> to avoid the [issue](https://github.com/RReverser/serde-xml-rs/issues/5)
fn sort_link_joint(string: &str) -> Result<String> {
    let e: xml::Element = string.parse().map_err(UrdfError::new)?;
    let mut links = Vec::new();
    let mut joints = Vec::new();
    let mut materials = Vec::new();
    for c in &e.children {
        if let xml::Xml::ElementNode(ref xml_elm) = *c {
            if xml_elm.name == "link" {
                links.push(xml::Xml::ElementNode(xml_elm.clone()));
            } else if xml_elm.name == "joint" {
                joints.push(xml::Xml::ElementNode(xml_elm.clone()));
            } else if xml_elm.name == "material" {
                materials.push(xml::Xml::ElementNode(xml_elm.clone()));
            }
        };
    }
    let mut new_elm = e;
    links.extend(joints);
    links.extend(materials);
    new_elm.children = links;
    Ok(format!("{}", new_elm))
}

/// Read urdf file and create Robot instance
///
/// # Examples
///
/// ```
/// let urdf_robo = urdf_rs::read_file("sample.urdf").unwrap();
/// let links = urdf_robo.links;
/// println!("{:?}", links[0].visual[0].origin.xyz);
/// ```
pub fn read_file<P: AsRef<Path>>(path: P) -> Result<Robot> {
    read_from_string(&std::fs::read_to_string(path)?)
}

/// Read from string instead of file.
///
///
/// # Examples
///
/// ```
/// let s = r##"
///     <robot name="robo">
///         <link name="shoulder1">
///             <inertial>
///                 <origin xyz="0 0 0.5" rpy="0 0 0"/>
///                 <mass value="1"/>
///                 <inertia ixx="100"  ixy="0"  ixz="0" iyy="100" iyz="0" izz="100" />
///             </inertial>
///             <visual>
///                 <origin xyz="0.1 0.2 0.3" rpy="-0.1 -0.2  -0.3" />
///                 <geometry>
///                     <box size="1.0 2.0 3.0" />
///                 </geometry>
///                 <material name="Cyan">
///                     <color rgba="0 1.0 1.0 1.0"/>
///                 </material>
///             </visual>
///             <collision>
///                 <origin xyz="0 0 0" rpy="0 0 0"/>
///                 <geometry>
///                     <cylinder radius="1" length="0.5"/>
///                 </geometry>
///             </collision>
///         </link>
///         <link name="elbow1" />
///         <link name="wrist1" />
///         <joint name="shoulder_pitch" type="revolute">
///             <origin xyz="0.0 0.0 0.1" />
///             <parent link="shoulder1" />
///             <child link="elbow1" />
///             <axis xyz="0 1 -1" />
///             <limit lower="-1" upper="1.0" effort="0" velocity="1.0"/>
///         </joint>
///         <joint name="shoulder_pitch" type="revolute">
///             <origin xyz="0.0 0.0 0.0" />
///             <parent link="elbow1" />
///             <child link="wrist1" />
///             <axis xyz="0 1 0" />
///             <limit lower="-2" upper="1.0" effort="0" velocity="1.0"/>
///         </joint>
///     </robot>
///    "##;
/// let urdf_robo = urdf_rs::read_from_string(s).unwrap();
/// println!("{:?}", urdf_robo.links[0].visual[0].origin.xyz);
/// ```

pub fn read_from_string(string: &str) -> Result<Robot> {
    let sorted_string = sort_link_joint(string)?;
    serde_xml_rs::from_str(&sorted_string).map_err(UrdfError::new)
}

#[test]
fn it_works() {
    let s = r##"
        <robot name="robo">
            <material name="blue">
              <color rgba="0.0 0.0 0.8 1.0"/>
            </material>

            <link name="shoulder1">
                <inertial>
                    <origin xyz="0 0 0.5" rpy="0 0 0"/>
                    <mass value="1"/>
                    <inertia ixx="100"  ixy="0"  ixz="0" iyy="100" iyz="0" izz="100" />
                </inertial>
                <visual>
                    <origin xyz="0.1 0.2 0.3" rpy="-0.1 -0.2  -0.3" />
                    <geometry>
                        <box size="1.0 2.0 3.0" />
                    </geometry>
                    <material name="Cyan">
                        <color rgba="0 1.0 1.0 1.0"/>
                    </material>
                </visual>
                <collision>
                    <origin xyz="0 0 0" rpy="0 0 0"/>
                    <geometry>
                        <cylinder radius="1" length="0.5"/>
                    </geometry>
                </collision>
            </link>
            <joint name="shoulder_pitch" type="revolute">
                <origin xyz="0.0 0.0 0.1" />
                <parent link="shoulder1" />
                <child link="elbow1" />
                <axis xyz="0 1 -1" />
                <limit lower="-1" upper="1.0" effort="0" velocity="1.0"/>
            </joint>
            <link name="elbow1" />
            <link name="wrist1" />
            <joint name="shoulder_pitch" type="revolute">
                <origin xyz="0.0 0.0 0.0" />
                <parent link="elbow1" />
                <child link="wrist1" />
                <axis xyz="0 1 0" />
                <limit lower="-2" upper="1.0" effort="0" velocity="1.0"/>
            </joint>
        </robot>
    "##;
    let robo = read_from_string(s).unwrap();

    assert_eq!(robo.name, "robo");
    assert_eq!(robo.links.len(), 3);
    assert_eq!(robo.joints.len(), 2);
    assert_eq!(robo.links[0].visual.len(), 1);
    let xyz = robo.links[0].visual[0].origin.xyz;
    assert_eq!(xyz[0], 0.1);
    assert_eq!(xyz[1], 0.2);
    assert_eq!(xyz[2], 0.3);
    let rpy = robo.links[0].visual[0].origin.rpy;
    assert_eq!(rpy[0], -0.1);
    assert_eq!(rpy[1], -0.2);
    assert_eq!(rpy[2], -0.3);

    match robo.links[0].visual[0].geometry {
        Geometry::Box { size } => {
            assert_eq!(size[0], 1.0f64);
            assert_eq!(size[1], 2.0f64);
            assert_eq!(size[2], 3.0f64);
        }
        _ => panic!("geometry error"),
    }
    assert_eq!(robo.materials.len(), 1);

    assert_eq!(robo.joints[0].name, "shoulder_pitch");
    let xyz = robo.joints[0].axis.xyz;
    assert_eq!(xyz[0], 0.0f64);
    assert_eq!(xyz[1], 1.0f64);
    assert_eq!(xyz[2], -1.0f64);
    let xyz = robo.joints[0].axis.xyz;
    //"0 1 -1"
    assert_eq!(xyz[0], 0.0);
    assert_eq!(xyz[1], 1.0);
    assert_eq!(xyz[2], -1.0);
}
<commit_msg>Add test case `mesh without scale`<commit_after>use crate::deserialize::*;
use crate::errors::*;

use std::path::Path;

/// sort <link> and <joint> to avoid the [issue](https://github.com/RReverser/serde-xml-rs/issues/5)
fn sort_link_joint(string: &str) -> Result<String> {
    let e: xml::Element = string.parse().map_err(UrdfError::new)?;
    let mut links = Vec::new();
    let mut joints = Vec::new();
    let mut materials = Vec::new();
    for c in &e.children {
        if let xml::Xml::ElementNode(ref xml_elm) = *c {
            if xml_elm.name == "link" {
                links.push(xml::Xml::ElementNode(xml_elm.clone()));
            } else if xml_elm.name == "joint" {
                joints.push(xml::Xml::ElementNode(xml_elm.clone()));
            } else if xml_elm.name == "material" {
                materials.push(xml::Xml::ElementNode(xml_elm.clone()));
            }
        };
    }
    let mut new_elm = e;
    links.extend(joints);
    links.extend(materials);
    new_elm.children = links;
    Ok(format!("{}", new_elm))
}

/// Read urdf file and create Robot instance
///
/// # Examples
///
/// ```
/// let urdf_robo = urdf_rs::read_file("sample.urdf").unwrap();
/// let links = urdf_robo.links;
/// println!("{:?}", links[0].visual[0].origin.xyz);
/// ```
pub fn read_file<P: AsRef<Path>>(path: P) -> Result<Robot> {
    read_from_string(&std::fs::read_to_string(path)?)
}

/// Read from string instead of file.
///
///
/// # Examples
///
/// ```
/// let s = r##"
///     <robot name="robo">
///         <link name="shoulder1">
///             <inertial>
///                 <origin xyz="0 0 0.5" rpy="0 0 0"/>
///                 <mass value="1"/>
///                 <inertia ixx="100"  ixy="0"  ixz="0" iyy="100" iyz="0" izz="100" />
///             </inertial>
///             <visual>
///                 <origin xyz="0.1 0.2 0.3" rpy="-0.1 -0.2  -0.3" />
///                 <geometry>
///                     <box size="1.0 2.0 3.0" />
///                 </geometry>
///                 <material name="Cyan">
///                     <color rgba="0 1.0 1.0 1.0"/>
///                 </material>
///             </visual>
///             <collision>
///                 <origin xyz="0 0 0" rpy="0 0 0"/>
///                 <geometry>
///                     <cylinder radius="1" length="0.5"/>
///                 </geometry>
///             </collision>
///         </link>
///         <link name="elbow1" />
///         <link name="wrist1" />
///         <joint name="shoulder_pitch" type="revolute">
///             <origin xyz="0.0 0.0 0.1" />
///             <parent link="shoulder1" />
///             <child link="elbow1" />
///             <axis xyz="0 1 -1" />
///             <limit lower="-1" upper="1.0" effort="0" velocity="1.0"/>
///         </joint>
///         <joint name="shoulder_pitch" type="revolute">
///             <origin xyz="0.0 0.0 0.0" />
///             <parent link="elbow1" />
///             <child link="wrist1" />
///             <axis xyz="0 1 0" />
///             <limit lower="-2" upper="1.0" effort="0" velocity="1.0"/>
///         </joint>
///     </robot>
///    "##;
/// let urdf_robo = urdf_rs::read_from_string(s).unwrap();
/// println!("{:?}", urdf_robo.links[0].visual[0].origin.xyz);
/// ```

pub fn read_from_string(string: &str) -> Result<Robot> {
    let sorted_string = sort_link_joint(string)?;
    serde_xml_rs::from_str(&sorted_string).map_err(UrdfError::new)
}

#[test]
fn it_works() {
    let s = r##"
        <robot name="robo">
            <material name="blue">
              <color rgba="0.0 0.0 0.8 1.0"/>
            </material>

            <link name="shoulder1">
                <inertial>
                    <origin xyz="0 0 0.5" rpy="0 0 0"/>
                    <mass value="1"/>
                    <inertia ixx="100"  ixy="0"  ixz="0" iyy="100" iyz="0" izz="100" />
                </inertial>
                <visual>
                    <origin xyz="0.1 0.2 0.3" rpy="-0.1 -0.2  -0.3" />
                    <geometry>
                        <box size="1.0 2.0 3.0" />
                    </geometry>
                    <material name="Cyan">
                        <color rgba="0 1.0 1.0 1.0"/>
                    </material>
                </visual>
                <visual>
                    <origin xyz="0.1 0.2 0.3" rpy="-0.1 -0.2  -0.3" />
                    <geometry>
                        <mesh filename="aa.dae" />
                    </geometry>
                </visual>
                <visual>
                    <origin xyz="0.1 0.2 0.3" rpy="-0.1 -0.2  -0.3" />
                    <geometry>
                        <mesh filename="bbb.dae" scale="2.0 3.0 4.0" />
                    </geometry>
                </visual>
                <collision>
                    <origin xyz="0 0 0" rpy="0 0 0"/>
                    <geometry>
                        <cylinder radius="1" length="0.5"/>
                    </geometry>
                </collision>
            </link>
            <joint name="shoulder_pitch" type="revolute">
                <origin xyz="0.0 0.0 0.1" />
                <parent link="shoulder1" />
                <child link="elbow1" />
                <axis xyz="0 1 -1" />
                <limit lower="-1" upper="1.0" effort="0" velocity="1.0"/>
            </joint>
            <link name="elbow1" />
            <link name="wrist1" />
            <joint name="shoulder_pitch" type="revolute">
                <origin xyz="0.0 0.0 0.0" />
                <parent link="elbow1" />
                <child link="wrist1" />
                <axis xyz="0 1 0" />
                <limit lower="-2" upper="1.0" effort="0" velocity="1.0"/>
            </joint>
        </robot>
    "##;
    let robo = read_from_string(s).unwrap();

    assert_eq!(robo.name, "robo");
    assert_eq!(robo.links.len(), 3);
    assert_eq!(robo.joints.len(), 2);
    assert_eq!(robo.links[0].visual.len(), 3);
    let xyz = robo.links[0].visual[0].origin.xyz;
    assert_eq!(xyz[0], 0.1);
    assert_eq!(xyz[1], 0.2);
    assert_eq!(xyz[2], 0.3);
    let rpy = robo.links[0].visual[0].origin.rpy;
    assert_eq!(rpy[0], -0.1);
    assert_eq!(rpy[1], -0.2);
    assert_eq!(rpy[2], -0.3);

    match robo.links[0].visual[0].geometry {
        Geometry::Box { size } => {
            assert_eq!(size[0], 1.0f64);
            assert_eq!(size[1], 2.0f64);
            assert_eq!(size[2], 3.0f64);
        }
        _ => panic!("geometry error"),
    }
    match robo.links[0].visual[1].geometry {
        Geometry::Mesh { ref filename, scale } => {
            assert_eq!(filename, "aa.dae");
            assert_eq!(scale, None);
        }
        _ => panic!("geometry error"),
    }
    match robo.links[0].visual[2].geometry {
        Geometry::Mesh { ref filename, scale } => {
            assert_eq!(filename, "bbb.dae");
            assert!(scale.is_some());
        }
        _ => panic!("geometry error"),
    }

    assert_eq!(robo.materials.len(), 1);

    assert_eq!(robo.joints[0].name, "shoulder_pitch");
    let xyz = robo.joints[0].axis.xyz;
    assert_eq!(xyz[0], 0.0f64);
    assert_eq!(xyz[1], 1.0f64);
    assert_eq!(xyz[2], -1.0f64);
    let xyz = robo.joints[0].axis.xyz;
    //"0 1 -1"
    assert_eq!(xyz[0], 0.0);
    assert_eq!(xyz[1], 1.0);
    assert_eq!(xyz[2], -1.0);
}
<|endoftext|>


<commit_before>use std::str;

const COMMENT_START: char = '#';
const STRING_BOUNDARY: char = '"';
const ASSIGNMENT_OPERATOR: char = '=';

#[derive(Debug, PartialEq, Clone)]
pub enum Statement {
    Comment(String),
    Assignment(Option<String>, Expression),
}

#[derive(Debug, PartialEq, Clone)]
pub enum Expression {
    Text(String),
}

named!(pub parse< Vec<Statement> >, many0!(statement));

named!(statement<Statement>, ws!(alt!(
    comment => {|content: &str| Statement::Comment(content.to_owned())} |
    assignment => {|(name, expr): (Option<&str>, Expression)| Statement::Assignment(name.map(|s| s.to_owned()), expr)}
)));

named!(comment<&str>,
    do_parse!(
        char!(COMMENT_START) >>
        content: take_until_and_consume!("\n") >>
        (str::from_utf8(content).unwrap())
    )
);

named!(assignment<(Option<&str>, Expression)>,
    do_parse!(
        name: assignment_name >>
        char!(ASSIGNMENT_OPERATOR) >>
        expr: expression >>
        (name, expr)
    )
);

named!(assignment_name< Option<&str> >,
    map!(
        opt!(assignment_name_impl),
        |n: Option<&'a [u8]>| n.and_then(|s| str::from_utf8(s).ok())
    )
);

// Without this separation, type inference fails
named!(assignment_name_impl<&[u8]>,
    take_while1_s!(is_valid_assignment_name_char)
);

named!(expression<Expression>,
    alt!(
        expr_text => {|text: &str| Expression::Text(text.to_owned())}
    )
);

named!(expr_text<&str>,
    map!(
        delimited!(
            char!(STRING_BOUNDARY),
            take_while!(is_string_boundary),
            char!(STRING_BOUNDARY)
        ),
        |s: &'a [u8]| str::from_utf8(s).unwrap()
    )
);

fn is_valid_assignment_name_char(c: u8) -> bool {
    let ch = c as char;
    ch.is_alphanumeric() || ch == '_'
}

fn is_string_boundary(c: u8) -> bool {
    c as char == STRING_BOUNDARY
}
<commit_msg>Dealing with newlines properly and actually folding statements into a vector<commit_after>use std::str;

const COMMENT_START: char = '#';
const STRING_BOUNDARY: char = '"';
const ASSIGNMENT_OPERATOR: char = '=';

#[derive(Debug, PartialEq, Clone)]
pub enum Statement {
    Comment(String),
    Assignment(Option<String>, Expression),
}

#[derive(Debug, PartialEq, Clone)]
pub enum Expression {
    Text(String),
}

named!(pub parse< Vec<Statement> >, fold_many0!(statement, Vec::new(), |mut acc: Vec<_>, item| {
    acc.push(item);
    acc
}));

named!(statement<Statement>, ws!(alt!(
    comment => {|content: &str| Statement::Comment(content.to_owned())} |
    assignment => {|(name, expr): (Option<&str>, Expression)| Statement::Assignment(name.map(|s| s.to_owned()), expr)}
)));

named!(comment<&str>,
    do_parse!(
        char!(COMMENT_START) >>
        content: take_until_and_consume!("\n") >>
        (str::from_utf8(content).unwrap())
    )
);

named!(assignment<(Option<&str>, Expression)>,
    do_parse!(
        name: assignment_name >>
        char!(ASSIGNMENT_OPERATOR) >>
        expr: expression >>
        char!('\n') >>
        (name, expr)
    )
);

named!(assignment_name< Option<&str> >,
    map!(
        opt!(assignment_name_impl),
        |n: Option<&'a [u8]>| n.and_then(|s| str::from_utf8(s).ok())
    )
);

// Without this separation, type inference fails
named!(assignment_name_impl<&[u8]>,
    take_while1_s!(is_valid_assignment_name_char)
);

named!(expression<Expression>,
    alt!(
        expr_text => {|text: &str| Expression::Text(text.to_owned())}
    )
);

named!(expr_text<&str>,
    map!(
        delimited!(
            char!(STRING_BOUNDARY),
            take_while!(is_string_boundary),
            char!(STRING_BOUNDARY)
        ),
        |s: &'a [u8]| str::from_utf8(s).unwrap()
    )
);

fn is_valid_assignment_name_char(c: u8) -> bool {
    let ch = c as char;
    ch.is_alphanumeric() || ch == '_'
}

fn is_string_boundary(c: u8) -> bool {
    c as char == STRING_BOUNDARY
}
<|endoftext|>


<commit_before>use std::str::from_utf8;
use nom::IResult;

#[derive(Debug,PartialEq,Eq)]
pub struct PosixHeader<'a> {
    pub name:     & 'a str,
    pub mode:     & 'a str,
    pub uid:      u64,
    pub gid:      u64,
    pub size:     u64,
    pub mtime:    u64,
    pub chksum:   & 'a str,
    pub typeflag: char, /* TODO: enum */
    pub linkname: & 'a str,
    pub ustar:    Option<UStarHeader<'a>>
}

#[derive(Debug,PartialEq,Eq)]
pub struct UStarHeader<'a> {
    pub magic:    & 'a str,
    pub version:  & 'a str,
    pub uname:    & 'a str,
    pub gname:    & 'a str,
    pub devmajor: u64,
    pub devminor: u64,
    pub prefix:   & 'a str,
}

#[derive(Debug,PartialEq,Eq)]
pub struct TarEntry<'a> {
    pub header:   PosixHeader<'a>,
    pub contents: & 'a str
}

/* TODO: validation */
fn str_to_u64(s: &str, base: u64) -> u64 {
    let mut u = 0;
    let mut f = 1;

    for c in s.chars().rev().skip_while(|&c| c == '\0') {
        u += f * ((c as u64) - ('0' as u64));
        f *= base;
    }

    u
}

pub fn octal_to_u64(o: &str) -> u64 {
    str_to_u64(o, 8)
}

fn parse_ustar00(i: &[u8]) -> IResult<&[u8], Option<UStarHeader>> {
    chain!(i,
        tag!("00")               ~
        uname:    take_str!(32)  ~
        gname:    take_str!(32)  ~
        devmajor: take_str!(8)   ~
        devminor: take_str!(8)   ~
        prefix:   take_str!(155) ~
        take!(12), /* padding to 512 */
        ||{
            Some(UStarHeader {
                magic:    "ustar\0",
                version:  "00",
                uname:    uname,
                gname:    gname,
                devmajor: octal_to_u64(devmajor),
                devminor: octal_to_u64(devminor),
                prefix:   prefix
            })
        }
    )
}

fn parse_ustar(i: &[u8]) -> IResult<&[u8], Option<UStarHeader>> {
    chain!(i,
        tag!("ustar\0") ~
        ustar: parse_ustar00,
        ||{
            ustar
        }
    )
}

fn parse_posix(i: &[u8]) -> IResult<&[u8], Option<UStarHeader>> {
    chain!(i,
        take!(255), /* padding to 512 */
        ||{
            None
        }
    )
}

fn parse_header(i: &[u8]) -> IResult<&[u8], PosixHeader> {
    chain!(i,
        name:     take_str!(100) ~
        mode:     take_str!(8)   ~
        uid:      take_str!(8)   ~
        gid:      take_str!(8)   ~
        size:     take_str!(12)  ~
        mtime:    take_str!(12)  ~
        chksum:   take_str!(8)   ~
        typeflag: take!(1)       ~
        linkname: take_str!(100) ~
        ustar:    alt!(parse_ustar | parse_posix),
        ||{
            PosixHeader {
                name:     name,
                mode:     mode,
                uid:      octal_to_u64(uid),
                gid:      octal_to_u64(gid),
                size:     octal_to_u64(size),
                mtime:    octal_to_u64(mtime),
                chksum:   chksum,
                typeflag: typeflag[0] as char,
                linkname: linkname,
                ustar:    ustar
            }
        }
    )
}

fn parse_contents(i: &[u8], size: u64) -> IResult<&[u8], &str> {
    let trailing = size % 512;
    let padding = match trailing {
        0 => 0,
        t => 512 - t
    };
    chain!(i,
        contents: take_str!(size as usize) ~
        take!(padding as usize),
        ||{
            contents
        }
    )
}

fn parse_entry(i: &[u8]) -> IResult<&[u8], TarEntry> {
    chain!(i,
        header:   parse_header ~
        contents: apply!(parse_contents, header.size),
        ||{
            TarEntry {
                header: header,
                contents: contents
            }
        }
    )
}

pub fn parse_tar(i: &[u8]) -> IResult<&[u8], Vec<TarEntry>> {
    many0!(i, parse_entry)
}

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

    #[test]
    fn octal_to_u64_test() {
        match octal_to_u64("756") {
            494 => {},
            o => panic!("octal_to_u64 failed, expected 494 but got {}", o)
        }
        match octal_to_u64("") {
            0 => {},
            o => panic!("octal_to_u64 failed, expected 0 but got {}", o)
        }
    }
}
<commit_msg>use a proper enum for type flag<commit_after>use std::str::from_utf8;
use nom::IResult;

#[derive(Debug,PartialEq,Eq)]
pub struct PosixHeader<'a> {
    pub name:     & 'a str,
    pub mode:     & 'a str,
    pub uid:      u64,
    pub gid:      u64,
    pub size:     u64,
    pub mtime:    u64,
    pub chksum:   & 'a str,
    pub typeflag: TypeFlag,
    pub linkname: & 'a str,
    pub ustar:    Option<UStarHeader<'a>>
}

#[derive(Debug,PartialEq,Eq)]
pub struct UStarHeader<'a> {
    pub magic:    & 'a str,
    pub version:  & 'a str,
    pub uname:    & 'a str,
    pub gname:    & 'a str,
    pub devmajor: u64,
    pub devminor: u64,
    pub prefix:   & 'a str,
}

#[derive(Debug,PartialEq,Eq)]
pub struct TarEntry<'a> {
    pub header:   PosixHeader<'a>,
    pub contents: & 'a str
}

/* TODO: support vendor specific + sparse */
#[derive(Debug,PartialEq,Eq)]
pub enum TypeFlag {
    NormalFile,
    HardLink,
    SymbolicLink,
    CharacterSpecial,
    BlockSpecial,
    Directory,
    FIFO,
    ContiguousFile,
    GlobalExtendedHeaderWithMetadata,
    ExtendedHeaderWithMetadataForNext,
    VendorSpecific,
    Invalid
}

/* TODO: validation */
fn str_to_u64(s: &str, base: u64) -> u64 {
    let mut u = 0;
    let mut f = 1;

    for c in s.chars().rev().skip_while(|&c| c == '\0') {
        u += f * ((c as u64) - ('0' as u64));
        f *= base;
    }

    u
}

pub fn octal_to_u64(o: &str) -> u64 {
    str_to_u64(o, 8)
}

fn char_to_type_flag(c: char) -> TypeFlag {
    match c {
        '0' => TypeFlag::NormalFile,
        '1' => TypeFlag::HardLink,
        '2' => TypeFlag::SymbolicLink,
        '3' => TypeFlag::CharacterSpecial,
        '4' => TypeFlag::BlockSpecial,
        '5' => TypeFlag::Directory,
        '6' => TypeFlag::FIFO,
        '7' => TypeFlag::ContiguousFile,
        'g' => TypeFlag::GlobalExtendedHeaderWithMetadata,
        'x' => TypeFlag::ExtendedHeaderWithMetadataForNext,
        'A' ... 'Z' => TypeFlag::VendorSpecific,
        _ => TypeFlag::Invalid
    }
}

fn parse_ustar00(i: &[u8]) -> IResult<&[u8], Option<UStarHeader>> {
    chain!(i,
        tag!("00")               ~
        uname:    take_str!(32)  ~
        gname:    take_str!(32)  ~
        devmajor: take_str!(8)   ~
        devminor: take_str!(8)   ~
        prefix:   take_str!(155) ~
        take!(12), /* padding to 512 */
        ||{
            Some(UStarHeader {
                magic:    "ustar\0",
                version:  "00",
                uname:    uname,
                gname:    gname,
                devmajor: octal_to_u64(devmajor),
                devminor: octal_to_u64(devminor),
                prefix:   prefix
            })
        }
    )
}

fn parse_ustar(i: &[u8]) -> IResult<&[u8], Option<UStarHeader>> {
    chain!(i,
        tag!("ustar\0") ~
        ustar: parse_ustar00,
        ||{
            ustar
        }
    )
}

fn parse_posix(i: &[u8]) -> IResult<&[u8], Option<UStarHeader>> {
    chain!(i,
        take!(255), /* padding to 512 */
        ||{
            None
        }
    )
}

fn parse_header(i: &[u8]) -> IResult<&[u8], PosixHeader> {
    chain!(i,
        name:     take_str!(100) ~
        mode:     take_str!(8)   ~
        uid:      take_str!(8)   ~
        gid:      take_str!(8)   ~
        size:     take_str!(12)  ~
        mtime:    take_str!(12)  ~
        chksum:   take_str!(8)   ~
        typeflag: take!(1)       ~
        linkname: take_str!(100) ~
        ustar:    alt!(parse_ustar | parse_posix),
        ||{
            PosixHeader {
                name:     name,
                mode:     mode,
                uid:      octal_to_u64(uid),
                gid:      octal_to_u64(gid),
                size:     octal_to_u64(size),
                mtime:    octal_to_u64(mtime),
                chksum:   chksum,
                typeflag: char_to_type_flag(typeflag[0] as char),
                linkname: linkname,
                ustar:    ustar
            }
        }
    )
}

fn parse_contents(i: &[u8], size: u64) -> IResult<&[u8], &str> {
    let trailing = size % 512;
    let padding = match trailing {
        0 => 0,
        t => 512 - t
    };
    chain!(i,
        contents: take_str!(size as usize) ~
        take!(padding as usize),
        ||{
            contents
        }
    )
}

fn parse_entry(i: &[u8]) -> IResult<&[u8], TarEntry> {
    chain!(i,
        header:   parse_header ~
        contents: apply!(parse_contents, header.size),
        ||{
            TarEntry {
                header: header,
                contents: contents
            }
        }
    )
}

pub fn parse_tar(i: &[u8]) -> IResult<&[u8], Vec<TarEntry>> {
    many0!(i, parse_entry)
}

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

    #[test]
    fn octal_to_u64_test() {
        match octal_to_u64("756") {
            494 => {},
            o => panic!("octal_to_u64 failed, expected 494 but got {}", o)
        }
        match octal_to_u64("") {
            0 => {},
            o => panic!("octal_to_u64 failed, expected 0 but got {}", o)
        }
    }
}
<|endoftext|>


<commit_before>use std::str::Chars;
use std::collections::HashMap;
use std::borrow::{Cow, Borrow};
use std::hash::Hash;

use fragments::{Fragment, InputType, ReturnType};
use codegen::Content;

pub enum ExtensibleMap<'a, K: 'a, V: 'a> {
    Owned(HashMap<K, V>),
    Extended(&'a HashMap<K, V>, HashMap<K, V>)
}

impl<'a, K: Hash + Eq, V> ExtensibleMap<'a, K, V> {
    pub fn extend(base: &'a HashMap<K, V>) -> ExtensibleMap<'a, K, V> {
        ExtensibleMap::Extended(base, HashMap::new())
    }

    pub fn insert(&mut self, key: K, value: V) -> Option<V> {
        match self {
            &mut ExtensibleMap::Owned(ref mut map) => map.insert(key, value),
            &mut ExtensibleMap::Extended(_, ref mut map) => map.insert(key, value),
        }
    }

    pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V> where
        K: Borrow<Q>,
        Q: Hash + Eq
    {
        match self {
            &ExtensibleMap::Owned(ref map) => map.get(k),
            &ExtensibleMap::Extended(ref base, ref map) => map.get(k).or_else(|| base.get(k))
        }
    }
}

pub fn parse_content(content: &str, fragments: &ExtensibleMap<&'static str, Box<Fragment>>) -> Result<Vec<ReturnType>, Cow<'static, str>> {
    let mut buf = String::new();
    let mut tokens = Vec::new();
    let mut content = content.chars();

    while let Some(character) = content.next() {
        match character {
            '{' => match content.next() {
                Some('{') => {
                    if buf.len() > 0 {
                        tokens.push(ReturnType::Content(Content::String(buf)));
                        buf = String::new();
                    }

                    tokens.push(try!(parse_fragment(&mut content, fragments)));
                },
                Some(c) => {
                    buf.push('{');
                    buf.push(c);
                },
                None => break
            },
            '\n' => buf.push_str("\\n"),
            '\t' => buf.push_str("\\t"),
            c => buf.push(c)
        }
    }

    if buf.len() > 0 {
        tokens.push(ReturnType::Content(Content::String(buf)));
    }

    Ok(tokens)
}

fn parse_fragment(content: &mut Chars, fragments: &ExtensibleMap<&'static str, Box<Fragment>>) -> Result<ReturnType, Cow<'static, str>> {
    let mut buf = String::new();
    let mut found_fragment = None;
    while let Some(character) = content.next() {
        match character {
            '(' => {
                let args = try!(parse_sub_fragments(content, fragments));
                if let Some(fragment) = fragments.get(&buf[..]) {
                    found_fragment = Some(fragment.process(args));
                    buf.clear();
                } else {
                    return Err(Cow::Owned(format!("'{}' is not a registered fragment", buf)));
                }
            },
            '}' => match content.next() {
                Some('}') | None => break,
                Some(c) => {
                    buf.push('}');
                    buf.push(c);
                }
            },
            c if c.is_whitespace() => {},
            c => buf.push(c)
        }
    }

    if let Some(result) = found_fragment {
        result
    } else if &buf[..] == "end" {
        Ok(ReturnType::End)
    } else if buf.len() > 0 {
        Ok(ReturnType::Content(Content::Placeholder(buf)))
    } else {
        Err(Cow::Borrowed("empty fragment"))
    }
}

fn parse_sub_fragments(content: &mut Chars, fragments: &ExtensibleMap<&'static str, Box<Fragment>>) -> Result<Vec<InputType>, Cow<'static, str>> {
    let mut buf = String::new();
    let mut result = vec![];
    while let Some(character) = content.next() {
        match character {
            '(' => {
                let args = try!(parse_sub_fragments(content, fragments));
                if let Some(fragment) = fragments.get(&buf[..]) {
                    result.push(match try!(fragment.process(args)) {
                        ReturnType::Content(val) => InputType::Content(val),
                        ReturnType::Logic(cond) => InputType::Logic(cond),
                        _ => return Err(Cow::Borrowed("inner fragments must only return content and logic"))
                    });
                } else {
                    return Err(Cow::Owned(format!("'{}' is not a registered fragment", buf)));
                }
                buf.clear();
            },
            ',' => if buf.len() > 0 {
                result.push(InputType::Content(Content::Placeholder(buf.clone())));
                buf.clear();
            },
            ')' => break,
            c if c.is_whitespace() => {},
            c => buf.push(c)
        }
    }

    if buf.len() > 0 {
        result.push(InputType::Content(Content::Placeholder(buf)))
    }

    Ok(result)
}<commit_msg>Add naive escape sequences to the parser<commit_after>use std::str::Chars;
use std::collections::HashMap;
use std::borrow::{Cow, Borrow};
use std::hash::Hash;

use fragments::{Fragment, InputType, ReturnType};
use codegen::Content;

pub enum ExtensibleMap<'a, K: 'a, V: 'a> {
    Owned(HashMap<K, V>),
    Extended(&'a HashMap<K, V>, HashMap<K, V>)
}

impl<'a, K: Hash + Eq, V> ExtensibleMap<'a, K, V> {
    pub fn extend(base: &'a HashMap<K, V>) -> ExtensibleMap<'a, K, V> {
        ExtensibleMap::Extended(base, HashMap::new())
    }

    pub fn insert(&mut self, key: K, value: V) -> Option<V> {
        match self {
            &mut ExtensibleMap::Owned(ref mut map) => map.insert(key, value),
            &mut ExtensibleMap::Extended(_, ref mut map) => map.insert(key, value),
        }
    }

    pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V> where
        K: Borrow<Q>,
        Q: Hash + Eq
    {
        match self {
            &ExtensibleMap::Owned(ref map) => map.get(k),
            &ExtensibleMap::Extended(ref base, ref map) => map.get(k).or_else(|| base.get(k))
        }
    }
}

pub fn parse_content(content: &str, fragments: &ExtensibleMap<&'static str, Box<Fragment>>) -> Result<Vec<ReturnType>, Cow<'static, str>> {
    let mut buf = String::new();
    let mut tokens = Vec::new();
    let mut content = content.chars();

    while let Some(character) = content.next() {
        match character {
            '{' => match content.next() {
                Some('{') => {
                    if buf.len() > 0 {
                        tokens.push(ReturnType::Content(Content::String(buf)));
                        buf = String::new();
                    }

                    tokens.push(try!(parse_fragment(&mut content, fragments)));
                },
                Some(c) => {
                    buf.push('{');
                    buf.push(c);
                },
                None => break
            },
            '\n' => buf.push_str("\\n"),
            '\t' => buf.push_str("\\t"),
            '\\' => if let Some(c) = content.next() {
                buf.push(c);
            },
            c => buf.push(c)
        }
    }

    if buf.len() > 0 {
        tokens.push(ReturnType::Content(Content::String(buf)));
    }

    Ok(tokens)
}

fn parse_fragment(content: &mut Chars, fragments: &ExtensibleMap<&'static str, Box<Fragment>>) -> Result<ReturnType, Cow<'static, str>> {
    let mut buf = String::new();
    let mut found_fragment = None;
    while let Some(character) = content.next() {
        match character {
            '(' => {
                let args = try!(parse_sub_fragments(content, fragments));
                if let Some(fragment) = fragments.get(&buf[..]) {
                    found_fragment = Some(fragment.process(args));
                    buf.clear();
                } else {
                    return Err(Cow::Owned(format!("'{}' is not a registered fragment", buf)));
                }
            },
            '}' => match content.next() {
                Some('}') | None => break,
                Some(c) => {
                    buf.push('}');
                    buf.push(c);
                }
            },
            '\\' => if let Some(c) = content.next() {
                buf.push(c);
            },
            c if c.is_whitespace() => {},
            c => buf.push(c)
        }
    }

    if let Some(result) = found_fragment {
        result
    } else if &buf[..] == "end" {
        Ok(ReturnType::End)
    } else if buf.len() > 0 {
        Ok(ReturnType::Content(Content::Placeholder(buf)))
    } else {
        Err(Cow::Borrowed("empty fragment"))
    }
}

fn parse_sub_fragments(content: &mut Chars, fragments: &ExtensibleMap<&'static str, Box<Fragment>>) -> Result<Vec<InputType>, Cow<'static, str>> {
    let mut buf = String::new();
    let mut result = vec![];
    while let Some(character) = content.next() {
        match character {
            '(' => {
                let args = try!(parse_sub_fragments(content, fragments));
                if let Some(fragment) = fragments.get(&buf[..]) {
                    result.push(match try!(fragment.process(args)) {
                        ReturnType::Content(val) => InputType::Content(val),
                        ReturnType::Logic(cond) => InputType::Logic(cond),
                        _ => return Err(Cow::Borrowed("inner fragments must only return content and logic"))
                    });
                } else {
                    return Err(Cow::Owned(format!("'{}' is not a registered fragment", buf)));
                }
                buf.clear();
            },
            ',' => if buf.len() > 0 {
                result.push(InputType::Content(Content::Placeholder(buf.clone())));
                buf.clear();
            },
            ')' => break,
            '\\' => if let Some(c) = content.next() {
                buf.push(c);
            },
            c if c.is_whitespace() => {},
            c => buf.push(c)
        }
    }

    if buf.len() > 0 {
        result.push(InputType::Content(Content::Placeholder(buf)))
    }

    Ok(result)
}<|endoftext|>


<commit_before>use std::str::from_utf8;
use nom::IResult;

#[derive(Debug,PartialEq,Eq)]
pub struct PosixHeader<'a> {
    pub name:     & 'a str,
    pub mode:     & 'a str,
    pub uid:      u32,
    pub gid:      u32,
    pub size:     u32,
    pub mtime:    u32,
    pub chksum:   & 'a str,
    pub typeflag: char, /* TODO: enum */
    pub linkname: & 'a str,
    pub ustar:    Option<UStarHeader<'a>>
}

#[derive(Debug,PartialEq,Eq)]
pub struct UStarHeader<'a> {
    pub magic:    & 'a str,
    pub version:  & 'a str,
    pub uname:    & 'a str,
    pub gname:    & 'a str,
    pub devmajor: & 'a str,
    pub devminor: & 'a str,
    pub prefix:   & 'a str,
}

#[derive(Debug,PartialEq,Eq)]
pub struct TarEntry<'a> {
    pub header:   PosixHeader<'a>,
    pub contents: & 'a str
}

fn str_to_u32(s: &str, base: u32) -> u32 {
    let mut u = 0;
    let mut f = 1;

    for c in s.chars().rev() {
        u += f * ((c as u32) - ('0' as u32));
        f *= base;
    }

    u
}

pub fn octal_to_u32(o: &str) -> u32 {
    str_to_u32(o, 8)
}

pub fn decimal_to_u32(d: &str) -> u32 {
    str_to_u32(d, 10)
}

fn parse_ustar(i: &[u8]) -> IResult<&[u8], Option<UStarHeader>> {
    chain!(i,
        magic:    map_res!(take!(6),   from_utf8) ~
        version:  map_res!(take!(2),   from_utf8) ~
        uname:    map_res!(take!(32),  from_utf8) ~
        gname:    map_res!(take!(32),  from_utf8) ~
        devmajor: map_res!(take!(8),   from_utf8) ~
        devminor: map_res!(take!(8),   from_utf8) ~
        prefix:   map_res!(take!(155), from_utf8),
        ||{
            match magic {
                "ustar" => Some(UStarHeader {
                    magic:    magic,
                    version:  version,
                    uname:    uname,
                    gname:    gname,
                    devmajor: devmajor,
                    devminor: devminor,
                    prefix:   prefix
                }),
                _ => None,
            }
        }
    )
}

fn parse_header(i: &[u8]) -> IResult<&[u8], PosixHeader> {
    chain!(i,
        name:     map_res!(take!(100), from_utf8) ~
        mode:     map_res!(take!(8),   from_utf8) ~
        uid:      map_res!(take!(8),   from_utf8) ~
        gid:      map_res!(take!(8),   from_utf8) ~
        size:     map_res!(take!(12),  from_utf8) ~
        mtime:    map_res!(take!(12),  from_utf8) ~
        chksum:   map_res!(take!(8),   from_utf8) ~
        typeflag: take!(1)                        ~
        linkname: map_res!(take!(100), from_utf8) ~
        ustar:    parse_ustar                     ~
        take!(12), /* padding to 512 */
        ||{
            PosixHeader {
                name:     name,
                mode:     mode,
                uid:      decimal_to_u32(uid),
                gid:      decimal_to_u32(gid),
                size:     octal_to_u32(size),
                mtime:    octal_to_u32(mtime),
                chksum:   chksum,
                typeflag: typeflag[0] as char,
                linkname: linkname,
                ustar:    ustar
            }
        }
    )
}

fn parse_entry(i: &[u8]) -> IResult<&[u8], TarEntry> {
    chain!(i,
        header: parse_header,
        /* TODO: contents */
        ||{
            TarEntry {
                header: header,
                contents: ""
            }
        }
    )
}

pub fn parse_tar(i: &[u8]) -> IResult<&[u8], Vec<TarEntry>> {
    many0!(i, parse_entry)
}

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

    #[test]
    fn octal_to_u32_test() {
        match octal_to_u32("756") {
            494 => {},
            o => panic!("octal_to_u32 failed, expected 494 but got {}", o)
        }
        match octal_to_u32("") {
            0 => {},
            o => panic!("octal_to_u32 failed, expected 0 but got {}", o)
        }
    }

    #[test]
    fn decimal_to_u32_test() {
        match decimal_to_u32("756") {
            756 => {},
            d => panic!("decimal_to_u32 failed, expected 756 but got {}", d)
        }
        match decimal_to_u32("") {
            0 => {},
            d => panic!("decimal_to_u32 failed, expected 0 but got {}", d)
        }
    }
}
<commit_msg>add TODO<commit_after>use std::str::from_utf8;
use nom::IResult;

#[derive(Debug,PartialEq,Eq)]
pub struct PosixHeader<'a> {
    pub name:     & 'a str,
    pub mode:     & 'a str,
    pub uid:      u32,
    pub gid:      u32,
    pub size:     u32,
    pub mtime:    u32,
    pub chksum:   & 'a str,
    pub typeflag: char, /* TODO: enum */
    pub linkname: & 'a str,
    pub ustar:    Option<UStarHeader<'a>>
}

#[derive(Debug,PartialEq,Eq)]
pub struct UStarHeader<'a> {
    pub magic:    & 'a str,
    pub version:  & 'a str,
    pub uname:    & 'a str,
    pub gname:    & 'a str,
    pub devmajor: & 'a str,
    pub devminor: & 'a str,
    pub prefix:   & 'a str,
}

#[derive(Debug,PartialEq,Eq)]
pub struct TarEntry<'a> {
    pub header:   PosixHeader<'a>,
    pub contents: & 'a str
}

/* TODO: validation */
fn str_to_u32(s: &str, base: u32) -> u32 {
    let mut u = 0;
    let mut f = 1;

    for c in s.chars().rev() {
        u += f * ((c as u32) - ('0' as u32));
        f *= base;
    }

    u
}

pub fn octal_to_u32(o: &str) -> u32 {
    str_to_u32(o, 8)
}

pub fn decimal_to_u32(d: &str) -> u32 {
    str_to_u32(d, 10)
}

fn parse_ustar(i: &[u8]) -> IResult<&[u8], Option<UStarHeader>> {
    chain!(i,
        magic:    map_res!(take!(6),   from_utf8) ~
        version:  map_res!(take!(2),   from_utf8) ~
        uname:    map_res!(take!(32),  from_utf8) ~
        gname:    map_res!(take!(32),  from_utf8) ~
        devmajor: map_res!(take!(8),   from_utf8) ~
        devminor: map_res!(take!(8),   from_utf8) ~
        prefix:   map_res!(take!(155), from_utf8),
        ||{
            match magic {
                "ustar" => Some(UStarHeader {
                    magic:    magic,
                    version:  version,
                    uname:    uname,
                    gname:    gname,
                    devmajor: devmajor,
                    devminor: devminor,
                    prefix:   prefix
                }),
                _ => None,
            }
        }
    )
}

fn parse_header(i: &[u8]) -> IResult<&[u8], PosixHeader> {
    chain!(i,
        name:     map_res!(take!(100), from_utf8) ~
        mode:     map_res!(take!(8),   from_utf8) ~
        uid:      map_res!(take!(8),   from_utf8) ~
        gid:      map_res!(take!(8),   from_utf8) ~
        size:     map_res!(take!(12),  from_utf8) ~
        mtime:    map_res!(take!(12),  from_utf8) ~
        chksum:   map_res!(take!(8),   from_utf8) ~
        typeflag: take!(1)                        ~
        linkname: map_res!(take!(100), from_utf8) ~
        ustar:    parse_ustar                     ~
        take!(12), /* padding to 512 */
        ||{
            PosixHeader {
                name:     name,
                mode:     mode,
                uid:      decimal_to_u32(uid),
                gid:      decimal_to_u32(gid),
                size:     octal_to_u32(size),
                mtime:    octal_to_u32(mtime),
                chksum:   chksum,
                typeflag: typeflag[0] as char,
                linkname: linkname,
                ustar:    ustar
            }
        }
    )
}

fn parse_entry(i: &[u8]) -> IResult<&[u8], TarEntry> {
    chain!(i,
        header: parse_header,
        /* TODO: contents */
        ||{
            TarEntry {
                header: header,
                contents: ""
            }
        }
    )
}

pub fn parse_tar(i: &[u8]) -> IResult<&[u8], Vec<TarEntry>> {
    many0!(i, parse_entry)
}

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

    #[test]
    fn octal_to_u32_test() {
        match octal_to_u32("756") {
            494 => {},
            o => panic!("octal_to_u32 failed, expected 494 but got {}", o)
        }
        match octal_to_u32("") {
            0 => {},
            o => panic!("octal_to_u32 failed, expected 0 but got {}", o)
        }
    }

    #[test]
    fn decimal_to_u32_test() {
        match decimal_to_u32("756") {
            756 => {},
            d => panic!("decimal_to_u32 failed, expected 756 but got {}", d)
        }
        match decimal_to_u32("") {
            0 => {},
            d => panic!("decimal_to_u32 failed, expected 0 but got {}", d)
        }
    }
}
<|endoftext|>


<commit_before>use libc;

use crate::{Key, KeyboardControllable, MouseButton, MouseControllable};

use self::libc::{c_char, c_int, c_void, useconds_t};
use std::{borrow::Cow, ffi::CString, ptr};

const CURRENT_WINDOW: c_int = 0;
const DEFAULT_DELAY: u64 = 12000;
type Window = c_int;
type Xdo = *const c_void;

#[link(name = "xdo")]
extern "C" {
    fn xdo_free(xdo: Xdo);
    fn xdo_new(display: *const c_char) -> Xdo;

    fn xdo_click_window(xdo: Xdo, window: Window, button: c_int) -> c_int;
    fn xdo_mouse_down(xdo: Xdo, window: Window, button: c_int) -> c_int;
    fn xdo_mouse_up(xdo: Xdo, window: Window, button: c_int) -> c_int;
    fn xdo_move_mouse(xdo: Xdo, x: c_int, y: c_int, screen: c_int) -> c_int;
    fn xdo_move_mouse_relative(xdo: Xdo, x: c_int, y: c_int) -> c_int;

    fn xdo_enter_text_window(
        xdo: Xdo,
        window: Window,
        string: *const c_char,
        delay: useconds_t,
    ) -> c_int;
    fn xdo_send_keysequence_window(
        xdo: Xdo,
        window: Window,
        string: *const c_char,
        delay: useconds_t,
    ) -> c_int;
    fn xdo_send_keysequence_window_down(
        xdo: Xdo,
        window: Window,
        string: *const c_char,
        delay: useconds_t,
    ) -> c_int;
    fn xdo_send_keysequence_window_up(
        xdo: Xdo,
        window: Window,
        string: *const c_char,
        delay: useconds_t,
    ) -> c_int;
}

fn mousebutton(button: MouseButton) -> c_int {
    match button {
        MouseButton::Left => 1,
        MouseButton::Middle => 2,
        MouseButton::Right => 3,
        MouseButton::ScrollUp => 4,
        MouseButton::ScrollDown => 5,
        MouseButton::ScrollLeft => 6,
        MouseButton::ScrollRight => 7,
    }
}

/// The main struct for handling the event emitting
pub struct Enigo {
    xdo: Xdo,
    delay: u64,
}
// This is safe, we have a unique pointer.
// TODO: use Unique<c_char> once stable.
unsafe impl Send for Enigo {}

impl Default for Enigo {
    /// Create a new Enigo instance
    fn default() -> Self {
        Self {
            xdo: unsafe { xdo_new(ptr::null()) },
            delay: DEFAULT_DELAY,
        }
    }
}
impl Enigo {
    /// Get the delay per keypress.
    /// Default value is 12000.
    /// This is Linux-specific.
    pub fn delay(&self) -> u64 {
        self.delay
    }
    /// Set the delay per keypress.
    /// This is Linux-specific.
    pub fn set_delay(&mut self, delay: u64) {
        self.delay = delay;
    }
}
impl Drop for Enigo {
    fn drop(&mut self) {
        unsafe {
            xdo_free(self.xdo);
        }
    }
}
impl MouseControllable for Enigo {
    fn mouse_move_to(&mut self, x: i32, y: i32) {
        unsafe {
            xdo_move_mouse(self.xdo, x as c_int, y as c_int, 0);
        }
    }
    fn mouse_move_relative(&mut self, x: i32, y: i32) {
        unsafe {
            xdo_move_mouse_relative(self.xdo, x as c_int, y as c_int);
        }
    }
    fn mouse_down(&mut self, button: MouseButton) {
        unsafe {
            xdo_mouse_down(self.xdo, CURRENT_WINDOW, mousebutton(button));
        }
    }
    fn mouse_up(&mut self, button: MouseButton) {
        unsafe {
            xdo_mouse_up(self.xdo, CURRENT_WINDOW, mousebutton(button));
        }
    }
    fn mouse_click(&mut self, button: MouseButton) {
        unsafe {
            xdo_click_window(self.xdo, CURRENT_WINDOW, mousebutton(button));
        }
    }
    fn mouse_scroll_x(&mut self, length: i32) {
        let button;
        let mut length = length;

        if length < 0 {
            button = MouseButton::ScrollLeft;
        } else {
            button = MouseButton::ScrollRight;
        }

        if length < 0 {
            length = -length;
        }

        for _ in 0..length {
            self.mouse_click(button);
        }
    }
    fn mouse_scroll_y(&mut self, length: i32) {
        let button;
        let mut length = length;

        if length < 0 {
            button = MouseButton::ScrollUp;
        } else {
            button = MouseButton::ScrollDown;
        }

        if length < 0 {
            length = -length;
        }

        for _ in 0..length {
            self.mouse_click(button);
        }
    }
}
fn keysequence<'a>(key: Key) -> Cow<'a, str> {
    if let Key::Layout(c) = key {
        return Cow::Owned(format!("U{:X}", c as u32));
    }
    #[allow(deprecated)]
    // I mean duh, we still need to support deprecated keys until they're removed
    Cow::Borrowed(match key {
        Key::Alt => "Alt",
        Key::Backspace => "BackSpace",
        Key::CapsLock => "CapsLock",
        Key::Control => "Control",
        Key::Delete => "Delete",
        Key::DownArrow => "Down",
        Key::End => "End",
        Key::Escape => "Escape",
        Key::F1 => "F1",
        Key::F10 => "F10",
        Key::F11 => "F11",
        Key::F12 => "F12",
        Key::F2 => "F2",
        Key::F3 => "F3",
        Key::F4 => "F4",
        Key::F5 => "F5",
        Key::F6 => "F6",
        Key::F7 => "F7",
        Key::F8 => "F8",
        Key::F9 => "F9",
        Key::Home => "Home",
        Key::Layout(_) => unreachable!(),
        Key::LeftArrow => "Left",
        Key::Option => "Option",
        Key::PageDown => "PageDown",
        Key::PageUp => "PageUp",
        Key::Raw(_) => unimplemented!(),
        Key::Return => "Return",
        Key::RightArrow => "Right",
        Key::Shift => "Shift",
        Key::Space => "space",
        Key::Tab => "Tab",
        Key::UpArrow => "Up",

        Key::Command | Key::Super | Key::Windows | Key::Meta => "Meta",
    })
}
impl KeyboardControllable for Enigo {
    fn key_sequence(&mut self, sequence: &str) {
        let string = CString::new(sequence).unwrap();
        unsafe {
            xdo_enter_text_window(
                self.xdo,
                CURRENT_WINDOW,
                string.as_ptr(),
                self.delay as useconds_t,
            );
        }
    }
    fn key_down(&mut self, key: Key) {
        let string = CString::new(&*keysequence(key)).unwrap();
        unsafe {
            xdo_send_keysequence_window_down(
                self.xdo,
                CURRENT_WINDOW,
                string.as_ptr(),
                self.delay as useconds_t,
            );
        }
    }
    fn key_up(&mut self, key: Key) {
        let string = CString::new(&*keysequence(key)).unwrap();
        unsafe {
            xdo_send_keysequence_window_up(
                self.xdo,
                CURRENT_WINDOW,
                string.as_ptr(),
                self.delay as useconds_t,
            );
        }
    }
    fn key_click(&mut self, key: Key) {
        let string = CString::new(&*keysequence(key)).unwrap();
        unsafe {
            xdo_send_keysequence_window(
                self.xdo,
                CURRENT_WINDOW,
                string.as_ptr(),
                self.delay as useconds_t,
            );
        }
    }
}
<commit_msg>Fix meta key on linux<commit_after>use libc;

use crate::{Key, KeyboardControllable, MouseButton, MouseControllable};

use self::libc::{c_char, c_int, c_void, useconds_t};
use std::{borrow::Cow, ffi::CString, ptr};

const CURRENT_WINDOW: c_int = 0;
const DEFAULT_DELAY: u64 = 12000;
type Window = c_int;
type Xdo = *const c_void;

#[link(name = "xdo")]
extern "C" {
    fn xdo_free(xdo: Xdo);
    fn xdo_new(display: *const c_char) -> Xdo;

    fn xdo_click_window(xdo: Xdo, window: Window, button: c_int) -> c_int;
    fn xdo_mouse_down(xdo: Xdo, window: Window, button: c_int) -> c_int;
    fn xdo_mouse_up(xdo: Xdo, window: Window, button: c_int) -> c_int;
    fn xdo_move_mouse(xdo: Xdo, x: c_int, y: c_int, screen: c_int) -> c_int;
    fn xdo_move_mouse_relative(xdo: Xdo, x: c_int, y: c_int) -> c_int;

    fn xdo_enter_text_window(
        xdo: Xdo,
        window: Window,
        string: *const c_char,
        delay: useconds_t,
    ) -> c_int;
    fn xdo_send_keysequence_window(
        xdo: Xdo,
        window: Window,
        string: *const c_char,
        delay: useconds_t,
    ) -> c_int;
    fn xdo_send_keysequence_window_down(
        xdo: Xdo,
        window: Window,
        string: *const c_char,
        delay: useconds_t,
    ) -> c_int;
    fn xdo_send_keysequence_window_up(
        xdo: Xdo,
        window: Window,
        string: *const c_char,
        delay: useconds_t,
    ) -> c_int;
}

fn mousebutton(button: MouseButton) -> c_int {
    match button {
        MouseButton::Left => 1,
        MouseButton::Middle => 2,
        MouseButton::Right => 3,
        MouseButton::ScrollUp => 4,
        MouseButton::ScrollDown => 5,
        MouseButton::ScrollLeft => 6,
        MouseButton::ScrollRight => 7,
    }
}

/// The main struct for handling the event emitting
pub struct Enigo {
    xdo: Xdo,
    delay: u64,
}
// This is safe, we have a unique pointer.
// TODO: use Unique<c_char> once stable.
unsafe impl Send for Enigo {}

impl Default for Enigo {
    /// Create a new Enigo instance
    fn default() -> Self {
        Self {
            xdo: unsafe { xdo_new(ptr::null()) },
            delay: DEFAULT_DELAY,
        }
    }
}
impl Enigo {
    /// Get the delay per keypress.
    /// Default value is 12000.
    /// This is Linux-specific.
    pub fn delay(&self) -> u64 {
        self.delay
    }
    /// Set the delay per keypress.
    /// This is Linux-specific.
    pub fn set_delay(&mut self, delay: u64) {
        self.delay = delay;
    }
}
impl Drop for Enigo {
    fn drop(&mut self) {
        unsafe {
            xdo_free(self.xdo);
        }
    }
}
impl MouseControllable for Enigo {
    fn mouse_move_to(&mut self, x: i32, y: i32) {
        unsafe {
            xdo_move_mouse(self.xdo, x as c_int, y as c_int, 0);
        }
    }
    fn mouse_move_relative(&mut self, x: i32, y: i32) {
        unsafe {
            xdo_move_mouse_relative(self.xdo, x as c_int, y as c_int);
        }
    }
    fn mouse_down(&mut self, button: MouseButton) {
        unsafe {
            xdo_mouse_down(self.xdo, CURRENT_WINDOW, mousebutton(button));
        }
    }
    fn mouse_up(&mut self, button: MouseButton) {
        unsafe {
            xdo_mouse_up(self.xdo, CURRENT_WINDOW, mousebutton(button));
        }
    }
    fn mouse_click(&mut self, button: MouseButton) {
        unsafe {
            xdo_click_window(self.xdo, CURRENT_WINDOW, mousebutton(button));
        }
    }
    fn mouse_scroll_x(&mut self, length: i32) {
        let button;
        let mut length = length;

        if length < 0 {
            button = MouseButton::ScrollLeft;
        } else {
            button = MouseButton::ScrollRight;
        }

        if length < 0 {
            length = -length;
        }

        for _ in 0..length {
            self.mouse_click(button);
        }
    }
    fn mouse_scroll_y(&mut self, length: i32) {
        let button;
        let mut length = length;

        if length < 0 {
            button = MouseButton::ScrollUp;
        } else {
            button = MouseButton::ScrollDown;
        }

        if length < 0 {
            length = -length;
        }

        for _ in 0..length {
            self.mouse_click(button);
        }
    }
}
fn keysequence<'a>(key: Key) -> Cow<'a, str> {
    if let Key::Layout(c) = key {
        return Cow::Owned(format!("U{:X}", c as u32));
    }
    #[allow(deprecated)]
    // I mean duh, we still need to support deprecated keys until they're removed
    Cow::Borrowed(match key {
        Key::Alt => "Alt",
        Key::Backspace => "BackSpace",
        Key::CapsLock => "CapsLock",
        Key::Control => "Control",
        Key::Delete => "Delete",
        Key::DownArrow => "Down",
        Key::End => "End",
        Key::Escape => "Escape",
        Key::F1 => "F1",
        Key::F10 => "F10",
        Key::F11 => "F11",
        Key::F12 => "F12",
        Key::F2 => "F2",
        Key::F3 => "F3",
        Key::F4 => "F4",
        Key::F5 => "F5",
        Key::F6 => "F6",
        Key::F7 => "F7",
        Key::F8 => "F8",
        Key::F9 => "F9",
        Key::Home => "Home",
        Key::Layout(_) => unreachable!(),
        Key::LeftArrow => "Left",
        Key::Option => "Option",
        Key::PageDown => "PageDown",
        Key::PageUp => "PageUp",
        Key::Raw(_) => unimplemented!(),
        Key::Return => "Return",
        Key::RightArrow => "Right",
        Key::Shift => "Shift",
        Key::Space => "space",
        Key::Tab => "Tab",
        Key::UpArrow => "Up",

        Key::Command | Key::Super | Key::Windows | Key::Meta => "Super",
    })
}
impl KeyboardControllable for Enigo {
    fn key_sequence(&mut self, sequence: &str) {
        let string = CString::new(sequence).unwrap();
        unsafe {
            xdo_enter_text_window(
                self.xdo,
                CURRENT_WINDOW,
                string.as_ptr(),
                self.delay as useconds_t,
            );
        }
    }
    fn key_down(&mut self, key: Key) {
        let string = CString::new(&*keysequence(key)).unwrap();
        unsafe {
            xdo_send_keysequence_window_down(
                self.xdo,
                CURRENT_WINDOW,
                string.as_ptr(),
                self.delay as useconds_t,
            );
        }
    }
    fn key_up(&mut self, key: Key) {
        let string = CString::new(&*keysequence(key)).unwrap();
        unsafe {
            xdo_send_keysequence_window_up(
                self.xdo,
                CURRENT_WINDOW,
                string.as_ptr(),
                self.delay as useconds_t,
            );
        }
    }
    fn key_click(&mut self, key: Key) {
        let string = CString::new(&*keysequence(key)).unwrap();
        unsafe {
            xdo_send_keysequence_window(
                self.xdo,
                CURRENT_WINDOW,
                string.as_ptr(),
                self.delay as useconds_t,
            );
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashSet;

use super::{Graphics, Drawer};
use draw::resize;
use std::time::Instant;

use glium::glutin::{Event, ElementState};

pub use glium::glutin::{VirtualKeyCode, MouseButton};

/// Methods `run_until_closed()` will call
pub trait Game{
    /// Method that gets called each frame from `run_until_closed()`.
    ///
    /// Should return a `GameUpdate` specifying things the game should do.
    fn frame(&mut self, FrameInfo, Drawer) -> GameUpdate;
}

impl<F: FnMut(FrameInfo, Drawer) -> GameUpdate> Game for F{
    fn frame(&mut self, info: FrameInfo, drawer: Drawer) -> GameUpdate{
        (self)(info, drawer)
    }
}

/// This is returned each frame from an object implementing `Game`.
///
/// It describes anything the game should do, e.g. closing the game.
pub struct GameUpdate{
    /// Describes whether the game should be closed
    pub close: bool,
    _fill: ()
}

impl GameUpdate {
    /// Nothing should be changed
    #[inline]
    pub fn nothing() -> Self{
        GameUpdate{
            close: false,
            _fill: ()
        }
    }

    /// Set whether to close the game
    pub fn set_close(self, close: bool) -> Self{
        GameUpdate{
            close: close,
            .. self
        }
    }
}

/// Runs the game until the window is closed
pub fn run_until_closed<G: Game>(mut graphics: Graphics, mut g: G){
    let mut last = Instant::now();
    let mut mousepos = (0., 0.);
    let mut down_keys = HashSet::new();

    'game: loop{
        let mut keys = Vec::new();
        let mut mouses = Vec::new();

        let mut resized = None::<(u32, u32)>;

        for ev in graphics.poll_events() {
            match ev {
                Event::Closed => break 'game,
                Event::KeyboardInput(es, _, Some(vkc)) => match es{
                    ElementState::Pressed  => {
                        down_keys.insert( vkc);
                        keys.push((true , vkc));
                    },
                    ElementState::Released => {
                        down_keys.remove(&vkc);
                        keys.push((false, vkc));
                    }
                },
                Event::MouseMoved(x, y) => {
                    let (w, h) = graphics.get_h_size();

                    mousepos = (x as f32 - w, h - y as f32);
                },
                // This is only neccessary because `graphics` gets immutably borrowed for this for-loop
                Event::Resized(w, h) => resized = Some((w, h)),
                Event::MouseInput(state, button) => mouses.push((state == ElementState::Pressed, button)),
                _ => ()
            }
        }

        if let Some((w, h)) = resized{
            resize(&mut graphics, w, h);
        }

        let dur = last.elapsed();
        let delta = dur.as_secs() as f64 + dur.subsec_nanos() as f64 / 1e9;
        last = Instant::now();

        let update = FrameInfo{
            delta    : delta,
            key_events: keys,
            mouse_events: mouses,
            down_keys: &down_keys,
            mousepos : mousepos
        };

        let update = g.frame(update, Drawer::new(&graphics));

        if update.close{
            break
        }
    }
}

/// Wraps together useful data about what has happened (e.g. events)
#[derive(Debug)]
pub struct FrameInfo<'a>{
    /// The amount of time passed since last frame
    pub delta: f64,
    /// The last position of the mouse on the screen
    pub mousepos: (f32, f32),
    mouse_events: Vec<(bool, MouseButton)>,
    key_events: Vec<(bool, VirtualKeyCode)>,

    // All keys that are pressed down
    down_keys: &'a HashSet<VirtualKeyCode>
}

impl<'a> FrameInfo<'a>{
    #[inline]
    /// Returns a slice of all key events that have happened
    pub fn get_key_events(&self) -> &[(bool, VirtualKeyCode)]{
        &self.key_events
    }
    #[inline]
    /// Returns a slice of all key events that have happened
    pub fn get_mouse_events(&self) -> &[(bool, MouseButton)]{
        &self.mouse_events
    }
    #[inline]
    /// Checks whether a key is pressed down
    pub fn is_down(&self, key: &VirtualKeyCode) -> bool{
        self.down_keys.contains(key)
    }
}

/// Macro for easily doing things if particular keys are down
/// # Example
///
/// ```rust
// Sadly the macro has to written here again, becauase the example doesn't use macros from the crate
/// # macro_rules! is_down{( $info:ident; $( $( $key:ident ),+ => $b:block ),+ ) => {{$( if $( $info.is_down(&korome::VirtualKeyCode::$key) )||+ $b )+}}}
/// fn logic(player_y: &mut f64, info: korome::FrameInfo){
///     is_down!{info;
///         W, Up => {
///             *player_y -= info.delta
///         },
///         S, Down => {
///             *player_y += info.delta
///         }
///     };
/// }
/// ```
#[macro_export]
macro_rules! is_down{
    ( $info:ident; $( $( $key:ident ),+ => $b:block ),+ ) => {{
        $( if $( $info.is_down(&$crate::VirtualKeyCode::$key) )||+ $b )+
    }}
}
<commit_msg>Finally actually fix doc-test for is_down!<commit_after>use std::collections::HashSet;

use super::{Graphics, Drawer};
use draw::resize;
use std::time::Instant;

use glium::glutin::{Event, ElementState};

pub use glium::glutin::{VirtualKeyCode, MouseButton};

/// Methods `run_until_closed()` will call
pub trait Game{
    /// Method that gets called each frame from `run_until_closed()`.
    ///
    /// Should return a `GameUpdate` specifying things the game should do.
    fn frame(&mut self, FrameInfo, Drawer) -> GameUpdate;
}

impl<F: FnMut(FrameInfo, Drawer) -> GameUpdate> Game for F{
    fn frame(&mut self, info: FrameInfo, drawer: Drawer) -> GameUpdate{
        (self)(info, drawer)
    }
}

/// This is returned each frame from an object implementing `Game`.
///
/// It describes anything the game should do, e.g. closing the game.
pub struct GameUpdate{
    /// Describes whether the game should be closed
    pub close: bool,
    _fill: ()
}

impl GameUpdate {
    /// Nothing should be changed
    #[inline]
    pub fn nothing() -> Self{
        GameUpdate{
            close: false,
            _fill: ()
        }
    }

    /// Set whether to close the game
    pub fn set_close(self, close: bool) -> Self{
        GameUpdate{
            close: close,
            .. self
        }
    }
}

/// Runs the game until the window is closed
pub fn run_until_closed<G: Game>(mut graphics: Graphics, mut g: G){
    let mut last = Instant::now();
    let mut mousepos = (0., 0.);
    let mut down_keys = HashSet::new();

    'game: loop{
        let mut keys = Vec::new();
        let mut mouses = Vec::new();

        let mut resized = None::<(u32, u32)>;

        for ev in graphics.poll_events() {
            match ev {
                Event::Closed => break 'game,
                Event::KeyboardInput(es, _, Some(vkc)) => match es{
                    ElementState::Pressed  => {
                        down_keys.insert( vkc);
                        keys.push((true , vkc));
                    },
                    ElementState::Released => {
                        down_keys.remove(&vkc);
                        keys.push((false, vkc));
                    }
                },
                Event::MouseMoved(x, y) => {
                    let (w, h) = graphics.get_h_size();

                    mousepos = (x as f32 - w, h - y as f32);
                },
                // This is only neccessary because `graphics` gets immutably borrowed for this for-loop
                Event::Resized(w, h) => resized = Some((w, h)),
                Event::MouseInput(state, button) => mouses.push((state == ElementState::Pressed, button)),
                _ => ()
            }
        }

        if let Some((w, h)) = resized{
            resize(&mut graphics, w, h);
        }

        let dur = last.elapsed();
        let delta = dur.as_secs() as f64 + dur.subsec_nanos() as f64 / 1e9;
        last = Instant::now();

        let update = FrameInfo{
            delta    : delta,
            key_events: keys,
            mouse_events: mouses,
            down_keys: &down_keys,
            mousepos : mousepos
        };

        let update = g.frame(update, Drawer::new(&graphics));

        if update.close{
            break
        }
    }
}

/// Wraps together useful data about what has happened (e.g. events)
#[derive(Debug)]
pub struct FrameInfo<'a>{
    /// The amount of time passed since last frame
    pub delta: f64,
    /// The last position of the mouse on the screen
    pub mousepos: (f32, f32),
    mouse_events: Vec<(bool, MouseButton)>,
    key_events: Vec<(bool, VirtualKeyCode)>,

    // All keys that are pressed down
    down_keys: &'a HashSet<VirtualKeyCode>
}

impl<'a> FrameInfo<'a>{
    #[inline]
    /// Returns a slice of all key events that have happened
    pub fn get_key_events(&self) -> &[(bool, VirtualKeyCode)]{
        &self.key_events
    }
    #[inline]
    /// Returns a slice of all key events that have happened
    pub fn get_mouse_events(&self) -> &[(bool, MouseButton)]{
        &self.mouse_events
    }
    #[inline]
    /// Checks whether a key is pressed down
    pub fn is_down(&self, key: &VirtualKeyCode) -> bool{
        self.down_keys.contains(key)
    }
}

/// Macro for easily doing things if particular keys are down
/// # Example
///
/// ```rust
/// # #[macro_use]
/// # extern crate korome;
/// # fn main(){}
/// fn logic(player_y: &mut f64, info: korome::FrameInfo){
///     is_down!{info;
///         W, Up => {
///             *player_y -= info.delta
///         },
///         S, Down => {
///             *player_y += info.delta
///         }
///     };
/// }
/// ```
#[macro_export]
macro_rules! is_down{
    ( $info:ident; $( $( $key:ident ),+ => $b:block ),+ ) => {{
        $( if $( $info.is_down(&$crate::VirtualKeyCode::$key) )||+ $b )+
    }}
}
<|endoftext|>


<commit_before>use std::fmt;
use {SdpLine, SdpBandwidth, SdpConnection};
use attributes::SdpAttribute;
use error::SdpParserResult;

#[derive(Clone)]
pub struct SdpMediaLine {
    pub media: SdpMediaValue,
    pub port: u32,
    pub proto: SdpProtocolValue,
    pub formats: SdpFormatList
}

#[derive(Clone,Debug,PartialEq)]
pub enum SdpMediaValue {
    Audio,
    Video,
    Application
}

impl fmt::Display for SdpMediaValue {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let printable = match *self {
            SdpMediaValue::Audio       => "Audio",
            SdpMediaValue::Video       => "Video",
            SdpMediaValue::Application => "Application"
        };
        write!(f, "{}", printable)
    }
}

#[derive(Clone,Debug,PartialEq)]
pub enum SdpProtocolValue {
    UdpTlsRtpSavpf,
    TcpTlsRtpSavpf,
    DtlsSctp,
    UdpDtlsSctp,
    TcpDtlsSctp
}

impl fmt::Display for SdpProtocolValue {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let printable = match *self {
            SdpProtocolValue::UdpTlsRtpSavpf => "Udp/Tls/Rtp/Savpf",
            SdpProtocolValue::TcpTlsRtpSavpf => "Tcp/Tls/Rtp/Savpf",
            SdpProtocolValue::DtlsSctp       => "Dtls/Sctp",
            SdpProtocolValue::UdpDtlsSctp    => "Udp/Dtls/Sctp",
            SdpProtocolValue::TcpDtlsSctp    => "Tcp/Dtls/Sctp"
        };
        write!(f, "{}", printable)
    }
}

#[derive(Clone)]
pub enum SdpFormatList {
    Integers {list: Vec<u32>},
    Strings {list: Vec<String>}
}

impl fmt::Display for SdpFormatList {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            SdpFormatList::Integers { list: ref x } => write!(f, "{:?}", x),
            SdpFormatList::Strings { list: ref x } => write!(f, "{:?}", x)
        }
    }
}

pub struct SdpMedia {
    media: SdpMediaLine,
    information: Option<String>,
    connection: Option<SdpConnection>,
    bandwidth: Vec<SdpBandwidth>,
    key: Option<String>,
    attribute: Vec<SdpAttribute>,
}

impl SdpMedia {
    pub fn new(media: SdpMediaLine) -> SdpMedia {
        SdpMedia { media: media,
                   information: None,
                   connection: None,
                   bandwidth: Vec::new(),
                   key: None,
                   attribute: Vec::new()
                 }
    }

    pub fn get_type(&self) -> &SdpMediaValue {
        &self.media.media
    }

    pub fn get_port(&self) -> u32 {
        self.media.port
    }

    pub fn get_proto(&self) -> &SdpProtocolValue {
        &self.media.proto
    }

    pub fn get_formats(&self) -> &SdpFormatList {
        &self.media.formats
    }

    pub fn has_connection(&self) -> bool {
        self.connection.is_some()
    }

    pub fn has_bandwidth(&self) -> bool {
        self.bandwidth.len() > 0
    }

    pub fn has_attributes(&self) -> bool {
        self.attribute.len() > 0
    }

    pub fn add_attribute(&mut self, attr: SdpAttribute) {
        self.attribute.push(attr)
    }

    pub fn add_bandwidth(&mut self, bw: SdpBandwidth) {
        self.bandwidth.push(bw)
    }

    //TODO complain if connection is set already
    pub fn set_connection(&mut self, c: SdpConnection) {
        self.connection = Some(c);
    }

    //TODO complain if information is set already
    pub fn set_information(&mut self, i: String) {
        self.information = Some(i);
    }

    //TODO complain if key is set already
    pub fn set_key(&mut self, k: String) {
        self.key = Some(k);
    }
}
fn parse_media_token(value: &str) -> Result<SdpMediaValue, SdpParserResult> {
    Ok(match value.to_lowercase().as_ref() {
        "audio"       => SdpMediaValue::Audio,
        "video"       => SdpMediaValue::Video,
        "application" => SdpMediaValue::Application,
        _ => return Err(SdpParserResult::ParserUnsupported {
              message: "unsupported media value".to_string(),
              line: value.to_string() }),
    })
}

fn parse_protocol_token(value: &str) -> Result<SdpProtocolValue, SdpParserResult> {
    Ok(match value.to_uppercase().as_ref() {
        "UDP/TLS/RTP/SAVPF" => SdpProtocolValue::UdpTlsRtpSavpf,
        "TCP/TLS/RTP/SAVPF" => SdpProtocolValue::TcpTlsRtpSavpf,
        "DTLS/SCTP"         => SdpProtocolValue::DtlsSctp,
        "UDP/DTLS/SCTP"     => SdpProtocolValue::UdpDtlsSctp,
        "TCP/DTLS/SCTP"     => SdpProtocolValue::TcpDtlsSctp,
        _ => return Err(SdpParserResult::ParserUnsupported {
              message: "unsupported protocol value".to_string(),
              line: value.to_string() }),
    })
}

pub fn parse_media(value: &str) -> Result<SdpLine, SdpParserResult> {
    let mv: Vec<&str> = value.split_whitespace().collect();
    if mv.len() < 4 {
        return Err(SdpParserResult::ParserLineError {
            message: "media attribute must have at least four tokens".to_string(),
            line: value.to_string() });
    }
    let media = try!(parse_media_token(mv[0]));
    let port = try!(mv[1].parse::<u32>());
    if port > 65535 {
        return Err(SdpParserResult::ParserLineError {
            message: "media port token is too big".to_string(),
            line: value.to_string() })
    }
    let proto = try!(parse_protocol_token(mv[2]));
    let fmt_slice: &[&str] = &mv[3..];
    let fmt = match media {
        SdpMediaValue::Audio | SdpMediaValue::Video => {
            let mut fmt_vec: Vec<u32> = vec![];
            for num in fmt_slice {
                let fmt_num = try!(num.parse::<u32>());
                match fmt_num {
                    0 => (),           // PCMU
                    8 => (),           // PCMA
                    9 => (),           // G722
                    13 => (),          // Comfort Noise
                    96 ... 127 => (),  // dynamic range
                    _ => return Err(SdpParserResult::ParserLineError {
                          message: "format number in media line is out of range".to_string(),
                          line: value.to_string() }),
                };
                fmt_vec.push(fmt_num);
            };
            SdpFormatList::Integers { list: fmt_vec }
        },
        SdpMediaValue::Application => {
            let mut fmt_vec: Vec<String> = vec![];
            // TODO enforce length == 1 and content 'webrtc-datachannel' only?
            for token in fmt_slice {
                fmt_vec.push(String::from(*token));
            }
            SdpFormatList::Strings { list: fmt_vec }
        }
    };
    let m = SdpMediaLine { media: media,
                           port: port,
                           proto: proto,
                           formats: fmt };
    println!("media: {}, {}, {}, {}",
             m.media, m.port, m.proto, m.formats);
    Ok(SdpLine::Media { value: m })
}

#[test]
fn test_media_works() {
    assert!(parse_media("audio 9 UDP/TLS/RTP/SAVPF 109").is_ok());
    assert!(parse_media("video 9 UDP/TLS/RTP/SAVPF 126").is_ok());
    assert!(parse_media("application 9 DTLS/SCTP 5000").is_ok());
    assert!(parse_media("application 9 UDP/DTLS/SCTP webrtc-datachannel").is_ok());

    assert!(parse_media("audio 9 UDP/TLS/RTP/SAVPF 109 9 0 8").is_ok());
    assert!(parse_media("audio 0 UDP/TLS/RTP/SAVPF 8").is_ok());
}

#[test]
fn test_media_missing_token() {
    assert!(parse_media("video 9 UDP/TLS/RTP/SAVPF").is_err());
}

#[test]
fn test_media_invalid_port_number() {
    assert!(parse_media("video 75123 UDP/TLS/RTP/SAVPF 8").is_err());
}

#[test]
fn test_media_invalid_type() {
    assert!(parse_media("invalid 9 UDP/TLS/RTP/SAVPF 8").is_err());
}

#[test]
fn test_media_invalid_transport() {
    assert!(parse_media("audio 9 invalid/invalid 8").is_err());
}

#[test]
fn test_media_invalid_payload() {
    assert!(parse_media("audio 9 UDP/TLS/RTP/SAVPF 300").is_err());
}

// TODO add uni tests here
pub fn parse_media_vector(lines: &[SdpLine]) -> Result<Vec<SdpMedia>, SdpParserResult> {
    let mut media_sections: Vec<SdpMedia> = Vec::new();
    let mut sdp_media = match lines[0] {
        SdpLine::Media{value: ref v} => {SdpMedia::new(v.clone())},
        _ => return Err(SdpParserResult::ParserSequence {
            message: "first line in media section needs to be a media line".to_string(),
            line: None })
    };
    for line in lines.iter().skip(1) {
        match *line {
            SdpLine::Information{value: ref v} => {sdp_media.set_information(v.clone())},
            SdpLine::Connection{value: ref v} => {sdp_media.set_connection(v.clone())},
            SdpLine::Bandwidth{value: ref v} => {sdp_media.add_bandwidth(v.clone());},
            SdpLine::Key{value: ref v} => {sdp_media.set_key(v.clone())},
            SdpLine::Attribute{value: ref v} => {sdp_media.add_attribute(v.clone());},
            SdpLine::Media{value: ref v} => {
                media_sections.push(sdp_media);
                sdp_media = SdpMedia::new(v.clone());
            },

            SdpLine::Email{..} | SdpLine::Phone{..} | SdpLine::Origin{..} |
                SdpLine::Repeat{..} | SdpLine::Session{..} |
                SdpLine::Timing{..} | SdpLine::Uri{..} | SdpLine::Version{..} |
                SdpLine::Zone{..} => return Err(
                    SdpParserResult::ParserSequence {
                        message: "invalid type in media section".to_string(),
                        line: None})
        };
    };
    media_sections.push(sdp_media);
    Ok(media_sections)
}

<commit_msg>added more media line parser unit tests<commit_after>use std::fmt;
use {SdpLine, SdpBandwidth, SdpConnection};
use attributes::SdpAttribute;
use error::SdpParserResult;

#[derive(Clone)]
pub struct SdpMediaLine {
    pub media: SdpMediaValue,
    pub port: u32,
    pub proto: SdpProtocolValue,
    pub formats: SdpFormatList
}

#[derive(Clone,Debug,PartialEq)]
pub enum SdpMediaValue {
    Audio,
    Video,
    Application
}

impl fmt::Display for SdpMediaValue {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let printable = match *self {
            SdpMediaValue::Audio       => "Audio",
            SdpMediaValue::Video       => "Video",
            SdpMediaValue::Application => "Application"
        };
        write!(f, "{}", printable)
    }
}

#[derive(Clone,Debug,PartialEq)]
pub enum SdpProtocolValue {
    UdpTlsRtpSavpf,
    TcpTlsRtpSavpf,
    DtlsSctp,
    UdpDtlsSctp,
    TcpDtlsSctp
}

impl fmt::Display for SdpProtocolValue {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let printable = match *self {
            SdpProtocolValue::UdpTlsRtpSavpf => "Udp/Tls/Rtp/Savpf",
            SdpProtocolValue::TcpTlsRtpSavpf => "Tcp/Tls/Rtp/Savpf",
            SdpProtocolValue::DtlsSctp       => "Dtls/Sctp",
            SdpProtocolValue::UdpDtlsSctp    => "Udp/Dtls/Sctp",
            SdpProtocolValue::TcpDtlsSctp    => "Tcp/Dtls/Sctp"
        };
        write!(f, "{}", printable)
    }
}

#[derive(Clone)]
pub enum SdpFormatList {
    Integers {list: Vec<u32>},
    Strings {list: Vec<String>}
}

impl fmt::Display for SdpFormatList {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            SdpFormatList::Integers { list: ref x } => write!(f, "{:?}", x),
            SdpFormatList::Strings { list: ref x } => write!(f, "{:?}", x)
        }
    }
}

pub struct SdpMedia {
    media: SdpMediaLine,
    information: Option<String>,
    connection: Option<SdpConnection>,
    bandwidth: Vec<SdpBandwidth>,
    key: Option<String>,
    attribute: Vec<SdpAttribute>,
}

impl SdpMedia {
    pub fn new(media: SdpMediaLine) -> SdpMedia {
        SdpMedia { media: media,
                   information: None,
                   connection: None,
                   bandwidth: Vec::new(),
                   key: None,
                   attribute: Vec::new()
                 }
    }

    pub fn get_type(&self) -> &SdpMediaValue {
        &self.media.media
    }

    pub fn get_port(&self) -> u32 {
        self.media.port
    }

    pub fn get_proto(&self) -> &SdpProtocolValue {
        &self.media.proto
    }

    pub fn get_formats(&self) -> &SdpFormatList {
        &self.media.formats
    }

    pub fn has_connection(&self) -> bool {
        self.connection.is_some()
    }

    pub fn has_bandwidth(&self) -> bool {
        self.bandwidth.len() > 0
    }

    pub fn has_attributes(&self) -> bool {
        self.attribute.len() > 0
    }

    pub fn add_attribute(&mut self, attr: SdpAttribute) {
        self.attribute.push(attr)
    }

    pub fn add_bandwidth(&mut self, bw: SdpBandwidth) {
        self.bandwidth.push(bw)
    }

    //TODO complain if connection is set already
    pub fn set_connection(&mut self, c: SdpConnection) {
        self.connection = Some(c);
    }

    //TODO complain if information is set already
    pub fn set_information(&mut self, i: String) {
        self.information = Some(i);
    }

    //TODO complain if key is set already
    pub fn set_key(&mut self, k: String) {
        self.key = Some(k);
    }
}
fn parse_media_token(value: &str) -> Result<SdpMediaValue, SdpParserResult> {
    Ok(match value.to_lowercase().as_ref() {
        "audio"       => SdpMediaValue::Audio,
        "video"       => SdpMediaValue::Video,
        "application" => SdpMediaValue::Application,
        _ => return Err(SdpParserResult::ParserUnsupported {
              message: "unsupported media value".to_string(),
              line: value.to_string() }),
    })
}

#[test]
fn test_parse_media_token() {
    let audio = parse_media_token("audio");
    assert!(audio.is_ok());
    assert_eq!(audio.unwrap(), SdpMediaValue::Audio);
    let video = parse_media_token("VIDEO");
    assert!(video.is_ok());
    assert_eq!(video.unwrap(), SdpMediaValue::Video);
    let app = parse_media_token("aPplIcatIOn");
    assert!(app.is_ok());
    assert_eq!(app.unwrap(), SdpMediaValue::Application);

    assert!(parse_media_token("").is_err());
    assert!(parse_media_token("foobar").is_err());
}

fn parse_protocol_token(value: &str) -> Result<SdpProtocolValue, SdpParserResult> {
    Ok(match value.to_uppercase().as_ref() {
        "UDP/TLS/RTP/SAVPF" => SdpProtocolValue::UdpTlsRtpSavpf,
        "TCP/TLS/RTP/SAVPF" => SdpProtocolValue::TcpTlsRtpSavpf,
        "DTLS/SCTP"         => SdpProtocolValue::DtlsSctp,
        "UDP/DTLS/SCTP"     => SdpProtocolValue::UdpDtlsSctp,
        "TCP/DTLS/SCTP"     => SdpProtocolValue::TcpDtlsSctp,
        _ => return Err(SdpParserResult::ParserUnsupported {
              message: "unsupported protocol value".to_string(),
              line: value.to_string() }),
    })
}

#[test]
fn test_parse_protocol_token() {
    let udps = parse_protocol_token("udp/tls/rtp/savpf");
    assert!(udps.is_ok());
    assert_eq!(udps.unwrap(), SdpProtocolValue::UdpTlsRtpSavpf);
    let tcps = parse_protocol_token("TCP/tls/rtp/savpf");
    assert!(tcps.is_ok());
    assert_eq!(tcps.unwrap(), SdpProtocolValue::TcpTlsRtpSavpf);
    let dtls = parse_protocol_token("dtLs/ScTP");
    assert!(dtls.is_ok());
    assert_eq!(dtls.unwrap(), SdpProtocolValue::DtlsSctp);
    let usctp = parse_protocol_token("udp/DTLS/sctp");
    assert!(usctp.is_ok());
    assert_eq!(usctp.unwrap(), SdpProtocolValue::UdpDtlsSctp);
    let tsctp = parse_protocol_token("tcp/dtls/SCTP");
    assert!(tsctp.is_ok());
    assert_eq!(tsctp.unwrap(), SdpProtocolValue::TcpDtlsSctp);

    assert!(parse_protocol_token("").is_err());
    assert!(parse_protocol_token("foobar").is_err());
}

pub fn parse_media(value: &str) -> Result<SdpLine, SdpParserResult> {
    let mv: Vec<&str> = value.split_whitespace().collect();
    if mv.len() < 4 {
        return Err(SdpParserResult::ParserLineError {
            message: "media attribute must have at least four tokens".to_string(),
            line: value.to_string() });
    }
    let media = try!(parse_media_token(mv[0]));
    let port = try!(mv[1].parse::<u32>());
    if port > 65535 {
        return Err(SdpParserResult::ParserLineError {
            message: "media port token is too big".to_string(),
            line: value.to_string() })
    }
    let proto = try!(parse_protocol_token(mv[2]));
    let fmt_slice: &[&str] = &mv[3..];
    let fmt = match media {
        SdpMediaValue::Audio | SdpMediaValue::Video => {
            let mut fmt_vec: Vec<u32> = vec![];
            for num in fmt_slice {
                let fmt_num = try!(num.parse::<u32>());
                match fmt_num {
                    0 => (),           // PCMU
                    8 => (),           // PCMA
                    9 => (),           // G722
                    13 => (),          // Comfort Noise
                    96 ... 127 => (),  // dynamic range
                    _ => return Err(SdpParserResult::ParserLineError {
                          message: "format number in media line is out of range".to_string(),
                          line: value.to_string() }),
                };
                fmt_vec.push(fmt_num);
            };
            SdpFormatList::Integers { list: fmt_vec }
        },
        SdpMediaValue::Application => {
            let mut fmt_vec: Vec<String> = vec![];
            // TODO enforce length == 1 and content 'webrtc-datachannel' only?
            for token in fmt_slice {
                fmt_vec.push(String::from(*token));
            }
            SdpFormatList::Strings { list: fmt_vec }
        }
    };
    let m = SdpMediaLine { media: media,
                           port: port,
                           proto: proto,
                           formats: fmt };
    println!("media: {}, {}, {}, {}",
             m.media, m.port, m.proto, m.formats);
    Ok(SdpLine::Media { value: m })
}

#[test]
fn test_media_works() {
    assert!(parse_media("audio 9 UDP/TLS/RTP/SAVPF 109").is_ok());
    assert!(parse_media("video 9 UDP/TLS/RTP/SAVPF 126").is_ok());
    assert!(parse_media("application 9 DTLS/SCTP 5000").is_ok());
    assert!(parse_media("application 9 UDP/DTLS/SCTP webrtc-datachannel").is_ok());

    assert!(parse_media("audio 9 UDP/TLS/RTP/SAVPF 109 9 0 8").is_ok());
    assert!(parse_media("audio 0 UDP/TLS/RTP/SAVPF 8").is_ok());
}

#[test]
fn test_media_missing_token() {
    assert!(parse_media("video 9 UDP/TLS/RTP/SAVPF").is_err());
}

#[test]
fn test_media_invalid_port_number() {
    assert!(parse_media("video 75123 UDP/TLS/RTP/SAVPF 8").is_err());
}

#[test]
fn test_media_invalid_type() {
    assert!(parse_media("invalid 9 UDP/TLS/RTP/SAVPF 8").is_err());
}

#[test]
fn test_media_invalid_transport() {
    assert!(parse_media("audio 9 invalid/invalid 8").is_err());
}

#[test]
fn test_media_invalid_payload() {
    assert!(parse_media("audio 9 UDP/TLS/RTP/SAVPF 300").is_err());
}

pub fn parse_media_vector(lines: &[SdpLine]) -> Result<Vec<SdpMedia>, SdpParserResult> {
    let mut media_sections: Vec<SdpMedia> = Vec::new();
    let mut sdp_media = match lines[0] {
        SdpLine::Media{value: ref v} => {SdpMedia::new(v.clone())},
        _ => return Err(SdpParserResult::ParserSequence {
            message: "first line in media section needs to be a media line".to_string(),
            line: None })
    };
    for line in lines.iter().skip(1) {
        match *line {
            SdpLine::Information{value: ref v} => {sdp_media.set_information(v.clone())},
            SdpLine::Connection{value: ref v} => {sdp_media.set_connection(v.clone())},
            SdpLine::Bandwidth{value: ref v} => {sdp_media.add_bandwidth(v.clone());},
            SdpLine::Key{value: ref v} => {sdp_media.set_key(v.clone())},
            SdpLine::Attribute{value: ref v} => {sdp_media.add_attribute(v.clone());},
            SdpLine::Media{value: ref v} => {
                media_sections.push(sdp_media);
                sdp_media = SdpMedia::new(v.clone());
            },

            SdpLine::Email{..} | SdpLine::Phone{..} | SdpLine::Origin{..} |
                SdpLine::Repeat{..} | SdpLine::Session{..} |
                SdpLine::Timing{..} | SdpLine::Uri{..} | SdpLine::Version{..} |
                SdpLine::Zone{..} => return Err(
                    SdpParserResult::ParserSequence {
                        message: "invalid type in media section".to_string(),
                        line: None})
        };
    };
    media_sections.push(sdp_media);
    Ok(media_sections)
}
// TODO add unit tests for parse_media_vector
<|endoftext|>