<commit_before>use super::mpz::{mpz_struct, Mpz, mpz_ptr, mpz_srcptr};
use super::mpf::{Mpf, mpf_srcptr};
use super::sign::Sign;
use ffi::*;
use libc::{c_char, c_double, c_int, c_ulong};
use std::ffi::CString;
use std::str::FromStr;
use std::error::Error;
use std::convert::From;
use std::mem::uninitialized;
use std::fmt;
use std::cmp::Ordering::{self, Greater, Less, Equal};
use std::ops::{Div, DivAssign, Mul, MulAssign, Add, AddAssign, Sub, SubAssign, Neg};
use num_traits::{Zero, One};

#[repr(C)]
pub struct mpq_struct {
    _mp_num: mpz_struct,
    _mp_den: mpz_struct
}

pub type mpq_srcptr = *const mpq_struct;
pub type mpq_ptr = *mut mpq_struct;

#[link(name = "gmp")]
extern "C" {
    fn __gmpq_init(x: mpq_ptr);
    fn __gmpq_clear(x: mpq_ptr);
    fn __gmpq_set(rop: mpq_ptr, op: mpq_srcptr);
    fn __gmpq_set_z(rop: mpq_ptr, op: mpz_srcptr);
    fn __gmpq_set_ui(rop: mpq_ptr, op1: c_ulong, op2: c_ulong);
    fn __gmpq_set_d(rop: mpq_ptr, op: c_double);
    fn __gmpq_set_f(rop: mpq_ptr, op: mpf_srcptr);
    fn __gmpq_cmp(op1: mpq_srcptr, op2: mpq_srcptr) -> c_int;
    fn __gmpq_cmp_ui(op1: mpq_srcptr, num2: c_ulong, den2: c_ulong) -> c_int;
    fn __gmpq_equal(op1: mpq_srcptr, op2: mpq_srcptr) -> c_int;
    fn __gmpq_add(sum: mpq_ptr, addend1: mpq_srcptr, addend2: mpq_srcptr);
    fn __gmpq_sub(difference: mpq_ptr, minuend: mpq_srcptr, subtrahend: mpq_srcptr);
    fn __gmpq_mul(product: mpq_ptr, multiplier: mpq_srcptr, multiplicand: mpq_srcptr);
    fn __gmpq_div(product: mpq_ptr, multiplier: mpq_srcptr, multiplicand: mpq_srcptr);
    fn __gmpq_neg(negated_operand: mpq_ptr, operand: mpq_srcptr);
    fn __gmpq_abs(rop: mpq_ptr, op: mpq_srcptr);
    fn __gmpq_inv(inverted_number: mpq_ptr, number: mpq_srcptr);
    fn __gmpq_get_num(numerator: mpz_ptr, rational: mpq_srcptr);
    fn __gmpq_get_den(denominator: mpz_ptr, rational: mpq_srcptr);
    fn __gmpq_set_num(rational: mpq_ptr, numerator: mpz_srcptr);
    fn __gmpq_set_den(rational: mpq_ptr, denominator: mpz_srcptr);
    fn __gmpq_canonicalize(rational: mpq_ptr);
    fn __gmpq_get_d(rational: mpq_srcptr) -> c_double;
    fn __gmpq_set_str(rop: mpq_ptr, str: *const c_char, base: c_int) -> c_int;
}

pub struct Mpq {
    mpq: mpq_struct,
}

unsafe impl Send for Mpq { }
unsafe impl Sync for Mpq { }

impl Drop for Mpq {
    fn drop(&mut self) { unsafe { __gmpq_clear(&mut self.mpq) } }
}

impl Mpq {
    pub unsafe fn inner(&self) -> mpq_srcptr {
        &self.mpq
    }

    pub unsafe fn inner_mut(&mut self) -> mpq_ptr {
        &mut self.mpq
    }

    pub fn new() -> Mpq {
        unsafe {
            let mut mpq = uninitialized();
            __gmpq_init(&mut mpq);
            Mpq { mpq: mpq }
        }
    }

    pub fn ratio(num: &Mpz, den: &Mpz) -> Mpq {
        unsafe {
            let mut res = Mpq::new();
            __gmpq_set_num(&mut res.mpq, num.inner());
            __gmpq_set_den(&mut res.mpq, den.inner());
            // Not canonicalizing is unsafe
            __gmpq_canonicalize(&mut res.mpq);
            res
        }
    }

    pub fn canonicalize(&mut self) {
        unsafe {
            __gmpq_canonicalize(&mut self.mpq);
        }
    }

    pub fn from_str_radix(s: &str, base: u8) -> Result<Mpq, ParseMpqError> {
        let s = CString::new(s).map_err(|_| ParseMpqError { _priv: () })?;
        let mut res = Mpq::new();
        unsafe {
            assert!(base == 0 || (base >= 2 && base <= 62));
            let r = __gmpq_set_str(&mut res.mpq, s.as_ptr(), base as c_int);

            if r == 0 {
                // Not canonicalizing is unsafe
                __gmpq_canonicalize(&mut res.mpq);
                Ok(res)
            } else {
                Err(ParseMpqError { _priv: () })
            }
        }
    }

    pub fn set(&mut self, other: &Mpq) {
        unsafe { __gmpq_set(&mut self.mpq, &other.mpq) }
    }

    pub fn set_z(&mut self, other: &Mpz) {
        unsafe { __gmpq_set_z(&mut self.mpq, other.inner()) }
    }

    pub fn set_d(&mut self, other: f64) {
        unsafe { __gmpq_set_d(&mut self.mpq, other) }
    }

    pub fn set_f(&mut self, other: &Mpf) {
        unsafe { __gmpq_set_f(&mut self.mpq, other.inner()) }
    }

    pub fn get_num(&self) -> Mpz {
        unsafe {
            let mut res = Mpz::new();
            __gmpq_get_num(res.inner_mut(), &self.mpq);
            res
        }
    }

    pub fn get_den(&self) -> Mpz {
        unsafe {
            let mut res = Mpz::new();
            __gmpq_get_den(res.inner_mut(), &self.mpq);
            res
        }
    }

    pub fn abs(&self) -> Mpq {
        unsafe {
            let mut res = Mpq::new();
            __gmpq_abs(&mut res.mpq, &self.mpq);
            res
        }
    }

    pub fn invert(&self) -> Mpq {
        unsafe {
            if self.is_zero() {
                panic!("divide by zero")
            }

            let mut res = Mpq::new();
            __gmpq_inv(&mut res.mpq, &self.mpq);
            res
        }
    }

    pub fn floor(&self) -> Mpz {
        let mut res = Mpz::new();
        unsafe {
            __gmpz_fdiv_q(res.inner_mut(), &self.mpq._mp_num, &self.mpq._mp_den);
        }
        res
    }

    pub fn ceil(&self) -> Mpz {
        let mut res = Mpz::new();
        unsafe {
            __gmpz_cdiv_q(res.inner_mut(), &self.mpq._mp_num, &self.mpq._mp_den);
        }
        res
    }

    pub fn sign(&self) -> Sign {
        self.get_num().sign()
    }

    pub fn one() -> Mpq {
        let mut res = Mpq::new();
        unsafe { __gmpq_set_ui(&mut res.mpq, 1, 1) }
        res
    }

    pub fn zero() -> Mpq { Mpq::new() }
    pub fn is_zero(&self) -> bool {
        unsafe { __gmpq_cmp_ui(&self.mpq, 0, 1) == 0 }
    }
}

#[derive(Debug)]
pub struct ParseMpqError {
    _priv: ()
}

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

impl Error for ParseMpqError {
    fn description(&self) -> &'static str {
        "invalid rational number"
    }

    fn cause(&self) -> Option<&'static Error> {
        None
    }
}

impl Clone for Mpq {
    fn clone(&self) -> Mpq {
        let mut res = Mpq::new();
        res.set(self);
        res
    }
}

impl Eq for Mpq { }
impl PartialEq for Mpq {
    fn eq(&self, other: &Mpq) -> bool {
        unsafe { __gmpq_equal(&self.mpq, &other.mpq) != 0 }
    }
}

impl Ord for Mpq {
    fn cmp(&self, other: &Mpq) -> Ordering {
        let cmp = unsafe { __gmpq_cmp(&self.mpq, &other.mpq) };
        if cmp == 0 {
            Equal
        } else if cmp < 0 {
            Less
        } else {
            Greater
        }
    }
}
impl PartialOrd for Mpq {
    fn partial_cmp(&self, other: &Mpq) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

macro_rules! div_guard {
    (Div, $what: expr) => {
        if $what.is_zero() {
            panic!("divide by zero")
        }
    };
    ($tr: ident, $what: expr) => {}
}

macro_rules! impl_oper {
    ($tr: ident, $meth: ident, $tr_assign: ident, $meth_assign: ident, $fun: ident) => {
        impl $tr<Mpq> for Mpq {
            type Output = Mpq;
            #[inline]
            fn $meth(self, other: Mpq) -> Mpq {
                self.$meth(&other)
            }
        }

        impl<'a> $tr<&'a Mpq> for Mpq {
            type Output = Mpq;
            #[inline]
            fn $meth(mut self, other: &Mpq) -> Mpq {
                self.$meth_assign(other);
                self
            }
        }

        impl<'a> $tr<Mpq> for &'a Mpq {
            type Output = Mpq;
            #[inline]
            fn $meth(self, mut other: Mpq) -> Mpq {
                unsafe {
                    div_guard!($tr, other);
                    $fun(&mut other.mpq, &self.mpq, &other.mpq);
                    other
                }
            }
        }

        impl<'a, 'b> $tr<&'a Mpq> for &'b Mpq {
            type Output = Mpq;
            fn $meth(self, other: &Mpq) -> Mpq {
                unsafe {
                    div_guard!($tr, *other);
                    let mut res = Mpq::new();
                    $fun(&mut res.mpq, &self.mpq, &other.mpq);
                    res
                }
            }
        }

        impl<'a> $tr_assign<Mpq> for Mpq {
            #[inline]
            fn $meth_assign(&mut self, other: Mpq) {
                self.$meth_assign(&other)
            }
        }

        impl<'a> $tr_assign<&'a Mpq> for Mpq {
            #[inline]
            fn $meth_assign(&mut self, other: &Mpq) {
                unsafe {
                    div_guard!($tr, *other);
                    $fun(&mut self.mpq, &self.mpq, &other.mpq)
                }
            }
        }
    }
}

impl_oper!(Add, add, AddAssign, add_assign, __gmpq_add);
impl_oper!(Sub, sub, SubAssign, sub_assign, __gmpq_sub);
impl_oper!(Mul, mul, MulAssign, mul_assign, __gmpq_mul);
impl_oper!(Div, div, DivAssign, div_assign, __gmpq_div);

impl<'b> Neg for &'b Mpq {
    type Output = Mpq;
    fn neg(self) -> Mpq {
        unsafe {
            let mut res = Mpq::new();
            __gmpq_neg(&mut res.mpq, &self.mpq);
            res
        }
    }
}

impl Neg for Mpq {
    type Output = Mpq;
    #[inline]
    fn neg(mut self) -> Mpq {
        unsafe {
            __gmpq_neg(&mut self.mpq, &self.mpq);
            self
        }
    }
}

impl From<Mpq> for f64 {
    fn from(other: Mpq) -> f64 {
        f64::from(&other)
    }
}

impl<'a> From<&'a Mpq> for f64 {
    fn from(other: &Mpq) -> f64 {
        unsafe {
            __gmpq_get_d(&other.mpq) as f64
        }
    }
}

impl From<Mpz> for Mpq {
    fn from(other: Mpz) -> Mpq {
        Mpq::from(&other)
    }
}

impl<'a> From<&'a Mpz> for Mpq {
    fn from(other: &Mpz) -> Mpq {
        let mut res = Mpq::new();
        res.set_z(&other);
        res
    }
}

impl From<i64> for Mpq {
    fn from(other: i64) -> Mpq {
        From::<Mpz>::from(From::<i64>::from(other))
    }
}

impl From<i32> for Mpq {
    fn from(other: i32) -> Mpq {
        From::<Mpz>::from(From::<i32>::from(other))
    }
}

impl From<u64> for Mpq {
    fn from(other: u64) -> Mpq {
        From::<Mpz>::from(From::<u64>::from(other))
    }
}

impl From<u32> for Mpq {
    fn from(other: u32) -> Mpq {
        From::<Mpz>::from(From::<u32>::from(other))
    }
}

impl FromStr for Mpq {
    type Err = ParseMpqError;
    fn from_str(s: &str) -> Result<Mpq, ParseMpqError> {
        Mpq::from_str_radix(s, 10)
    }
}

impl fmt::Debug for Mpq {
    /// Renders as `numer/denom`. If denom=1, renders as numer.
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self, f)
    }
}

impl fmt::Display for Mpq {
    /// Renders as `numer/denom`. If denom=1, renders as numer.
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let numer = self.get_num();
        let denom = self.get_den();

        if denom == From::<i64>::from(1) {
            write!(f, "{}", numer)
        } else {
            write!(f, "{}/{}", numer, denom)
        }
    }
}

impl Zero for Mpq {
    #[inline]
    fn zero() -> Mpq {
        Mpq::zero()
    }

    #[inline]
    fn is_zero(&self) -> bool {
        self.is_zero()
    }
}

impl One for Mpq {
    #[inline]
    fn one() -> Mpq {
        Mpq::one()
    }
}
<commit_msg>Remove Mpq::canonicalize<commit_after>use super::mpz::{mpz_struct, Mpz, mpz_ptr, mpz_srcptr};
use super::mpf::{Mpf, mpf_srcptr};
use super::sign::Sign;
use ffi::*;
use libc::{c_char, c_double, c_int, c_ulong};
use std::ffi::CString;
use std::str::FromStr;
use std::error::Error;
use std::convert::From;
use std::mem::uninitialized;
use std::fmt;
use std::cmp::Ordering::{self, Greater, Less, Equal};
use std::ops::{Div, DivAssign, Mul, MulAssign, Add, AddAssign, Sub, SubAssign, Neg};
use num_traits::{Zero, One};

#[repr(C)]
pub struct mpq_struct {
    _mp_num: mpz_struct,
    _mp_den: mpz_struct
}

pub type mpq_srcptr = *const mpq_struct;
pub type mpq_ptr = *mut mpq_struct;

#[link(name = "gmp")]
extern "C" {
    fn __gmpq_init(x: mpq_ptr);
    fn __gmpq_clear(x: mpq_ptr);
    fn __gmpq_set(rop: mpq_ptr, op: mpq_srcptr);
    fn __gmpq_set_z(rop: mpq_ptr, op: mpz_srcptr);
    fn __gmpq_set_ui(rop: mpq_ptr, op1: c_ulong, op2: c_ulong);
    fn __gmpq_set_d(rop: mpq_ptr, op: c_double);
    fn __gmpq_set_f(rop: mpq_ptr, op: mpf_srcptr);
    fn __gmpq_cmp(op1: mpq_srcptr, op2: mpq_srcptr) -> c_int;
    fn __gmpq_cmp_ui(op1: mpq_srcptr, num2: c_ulong, den2: c_ulong) -> c_int;
    fn __gmpq_equal(op1: mpq_srcptr, op2: mpq_srcptr) -> c_int;
    fn __gmpq_add(sum: mpq_ptr, addend1: mpq_srcptr, addend2: mpq_srcptr);
    fn __gmpq_sub(difference: mpq_ptr, minuend: mpq_srcptr, subtrahend: mpq_srcptr);
    fn __gmpq_mul(product: mpq_ptr, multiplier: mpq_srcptr, multiplicand: mpq_srcptr);
    fn __gmpq_div(product: mpq_ptr, multiplier: mpq_srcptr, multiplicand: mpq_srcptr);
    fn __gmpq_neg(negated_operand: mpq_ptr, operand: mpq_srcptr);
    fn __gmpq_abs(rop: mpq_ptr, op: mpq_srcptr);
    fn __gmpq_inv(inverted_number: mpq_ptr, number: mpq_srcptr);
    fn __gmpq_get_num(numerator: mpz_ptr, rational: mpq_srcptr);
    fn __gmpq_get_den(denominator: mpz_ptr, rational: mpq_srcptr);
    fn __gmpq_set_num(rational: mpq_ptr, numerator: mpz_srcptr);
    fn __gmpq_set_den(rational: mpq_ptr, denominator: mpz_srcptr);
    fn __gmpq_canonicalize(rational: mpq_ptr);
    fn __gmpq_get_d(rational: mpq_srcptr) -> c_double;
    fn __gmpq_set_str(rop: mpq_ptr, str: *const c_char, base: c_int) -> c_int;
}

pub struct Mpq {
    mpq: mpq_struct,
}

unsafe impl Send for Mpq { }
unsafe impl Sync for Mpq { }

impl Drop for Mpq {
    fn drop(&mut self) { unsafe { __gmpq_clear(&mut self.mpq) } }
}

impl Mpq {
    pub unsafe fn inner(&self) -> mpq_srcptr {
        &self.mpq
    }

    pub unsafe fn inner_mut(&mut self) -> mpq_ptr {
        &mut self.mpq
    }

    pub fn new() -> Mpq {
        unsafe {
            let mut mpq = uninitialized();
            __gmpq_init(&mut mpq);
            Mpq { mpq: mpq }
        }
    }

    pub fn ratio(num: &Mpz, den: &Mpz) -> Mpq {
        unsafe {
            let mut res = Mpq::new();
            __gmpq_set_num(&mut res.mpq, num.inner());
            __gmpq_set_den(&mut res.mpq, den.inner());
            // Not canonicalizing is unsafe
            __gmpq_canonicalize(&mut res.mpq);
            res
        }
    }

    pub fn from_str_radix(s: &str, base: u8) -> Result<Mpq, ParseMpqError> {
        let s = CString::new(s).map_err(|_| ParseMpqError { _priv: () })?;
        let mut res = Mpq::new();
        unsafe {
            assert!(base == 0 || (base >= 2 && base <= 62));
            let r = __gmpq_set_str(&mut res.mpq, s.as_ptr(), base as c_int);

            if r == 0 {
                // Not canonicalizing is unsafe
                __gmpq_canonicalize(&mut res.mpq);
                Ok(res)
            } else {
                Err(ParseMpqError { _priv: () })
            }
        }
    }

    pub fn set(&mut self, other: &Mpq) {
        unsafe { __gmpq_set(&mut self.mpq, &other.mpq) }
    }

    pub fn set_z(&mut self, other: &Mpz) {
        unsafe { __gmpq_set_z(&mut self.mpq, other.inner()) }
    }

    pub fn set_d(&mut self, other: f64) {
        unsafe { __gmpq_set_d(&mut self.mpq, other) }
    }

    pub fn set_f(&mut self, other: &Mpf) {
        unsafe { __gmpq_set_f(&mut self.mpq, other.inner()) }
    }

    pub fn get_num(&self) -> Mpz {
        unsafe {
            let mut res = Mpz::new();
            __gmpq_get_num(res.inner_mut(), &self.mpq);
            res
        }
    }

    pub fn get_den(&self) -> Mpz {
        unsafe {
            let mut res = Mpz::new();
            __gmpq_get_den(res.inner_mut(), &self.mpq);
            res
        }
    }

    pub fn abs(&self) -> Mpq {
        unsafe {
            let mut res = Mpq::new();
            __gmpq_abs(&mut res.mpq, &self.mpq);
            res
        }
    }

    pub fn invert(&self) -> Mpq {
        unsafe {
            if self.is_zero() {
                panic!("divide by zero")
            }

            let mut res = Mpq::new();
            __gmpq_inv(&mut res.mpq, &self.mpq);
            res
        }
    }

    pub fn floor(&self) -> Mpz {
        let mut res = Mpz::new();
        unsafe {
            __gmpz_fdiv_q(res.inner_mut(), &self.mpq._mp_num, &self.mpq._mp_den);
        }
        res
    }

    pub fn ceil(&self) -> Mpz {
        let mut res = Mpz::new();
        unsafe {
            __gmpz_cdiv_q(res.inner_mut(), &self.mpq._mp_num, &self.mpq._mp_den);
        }
        res
    }

    pub fn sign(&self) -> Sign {
        self.get_num().sign()
    }

    pub fn one() -> Mpq {
        let mut res = Mpq::new();
        unsafe { __gmpq_set_ui(&mut res.mpq, 1, 1) }
        res
    }

    pub fn zero() -> Mpq { Mpq::new() }
    pub fn is_zero(&self) -> bool {
        unsafe { __gmpq_cmp_ui(&self.mpq, 0, 1) == 0 }
    }
}

#[derive(Debug)]
pub struct ParseMpqError {
    _priv: ()
}

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

impl Error for ParseMpqError {
    fn description(&self) -> &'static str {
        "invalid rational number"
    }

    fn cause(&self) -> Option<&'static Error> {
        None
    }
}

impl Clone for Mpq {
    fn clone(&self) -> Mpq {
        let mut res = Mpq::new();
        res.set(self);
        res
    }
}

impl Eq for Mpq { }
impl PartialEq for Mpq {
    fn eq(&self, other: &Mpq) -> bool {
        unsafe { __gmpq_equal(&self.mpq, &other.mpq) != 0 }
    }
}

impl Ord for Mpq {
    fn cmp(&self, other: &Mpq) -> Ordering {
        let cmp = unsafe { __gmpq_cmp(&self.mpq, &other.mpq) };
        if cmp == 0 {
            Equal
        } else if cmp < 0 {
            Less
        } else {
            Greater
        }
    }
}
impl PartialOrd for Mpq {
    fn partial_cmp(&self, other: &Mpq) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

macro_rules! div_guard {
    (Div, $what: expr) => {
        if $what.is_zero() {
            panic!("divide by zero")
        }
    };
    ($tr: ident, $what: expr) => {}
}

macro_rules! impl_oper {
    ($tr: ident, $meth: ident, $tr_assign: ident, $meth_assign: ident, $fun: ident) => {
        impl $tr<Mpq> for Mpq {
            type Output = Mpq;
            #[inline]
            fn $meth(self, other: Mpq) -> Mpq {
                self.$meth(&other)
            }
        }

        impl<'a> $tr<&'a Mpq> for Mpq {
            type Output = Mpq;
            #[inline]
            fn $meth(mut self, other: &Mpq) -> Mpq {
                self.$meth_assign(other);
                self
            }
        }

        impl<'a> $tr<Mpq> for &'a Mpq {
            type Output = Mpq;
            #[inline]
            fn $meth(self, mut other: Mpq) -> Mpq {
                unsafe {
                    div_guard!($tr, other);
                    $fun(&mut other.mpq, &self.mpq, &other.mpq);
                    other
                }
            }
        }

        impl<'a, 'b> $tr<&'a Mpq> for &'b Mpq {
            type Output = Mpq;
            fn $meth(self, other: &Mpq) -> Mpq {
                unsafe {
                    div_guard!($tr, *other);
                    let mut res = Mpq::new();
                    $fun(&mut res.mpq, &self.mpq, &other.mpq);
                    res
                }
            }
        }

        impl<'a> $tr_assign<Mpq> for Mpq {
            #[inline]
            fn $meth_assign(&mut self, other: Mpq) {
                self.$meth_assign(&other)
            }
        }

        impl<'a> $tr_assign<&'a Mpq> for Mpq {
            #[inline]
            fn $meth_assign(&mut self, other: &Mpq) {
                unsafe {
                    div_guard!($tr, *other);
                    $fun(&mut self.mpq, &self.mpq, &other.mpq)
                }
            }
        }
    }
}

impl_oper!(Add, add, AddAssign, add_assign, __gmpq_add);
impl_oper!(Sub, sub, SubAssign, sub_assign, __gmpq_sub);
impl_oper!(Mul, mul, MulAssign, mul_assign, __gmpq_mul);
impl_oper!(Div, div, DivAssign, div_assign, __gmpq_div);

impl<'b> Neg for &'b Mpq {
    type Output = Mpq;
    fn neg(self) -> Mpq {
        unsafe {
            let mut res = Mpq::new();
            __gmpq_neg(&mut res.mpq, &self.mpq);
            res
        }
    }
}

impl Neg for Mpq {
    type Output = Mpq;
    #[inline]
    fn neg(mut self) -> Mpq {
        unsafe {
            __gmpq_neg(&mut self.mpq, &self.mpq);
            self
        }
    }
}

impl From<Mpq> for f64 {
    fn from(other: Mpq) -> f64 {
        f64::from(&other)
    }
}

impl<'a> From<&'a Mpq> for f64 {
    fn from(other: &Mpq) -> f64 {
        unsafe {
            __gmpq_get_d(&other.mpq) as f64
        }
    }
}

impl From<Mpz> for Mpq {
    fn from(other: Mpz) -> Mpq {
        Mpq::from(&other)
    }
}

impl<'a> From<&'a Mpz> for Mpq {
    fn from(other: &Mpz) -> Mpq {
        let mut res = Mpq::new();
        res.set_z(&other);
        res
    }
}

impl From<i64> for Mpq {
    fn from(other: i64) -> Mpq {
        From::<Mpz>::from(From::<i64>::from(other))
    }
}

impl From<i32> for Mpq {
    fn from(other: i32) -> Mpq {
        From::<Mpz>::from(From::<i32>::from(other))
    }
}

impl From<u64> for Mpq {
    fn from(other: u64) -> Mpq {
        From::<Mpz>::from(From::<u64>::from(other))
    }
}

impl From<u32> for Mpq {
    fn from(other: u32) -> Mpq {
        From::<Mpz>::from(From::<u32>::from(other))
    }
}

impl FromStr for Mpq {
    type Err = ParseMpqError;
    fn from_str(s: &str) -> Result<Mpq, ParseMpqError> {
        Mpq::from_str_radix(s, 10)
    }
}

impl fmt::Debug for Mpq {
    /// Renders as `numer/denom`. If denom=1, renders as numer.
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self, f)
    }
}

impl fmt::Display for Mpq {
    /// Renders as `numer/denom`. If denom=1, renders as numer.
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let numer = self.get_num();
        let denom = self.get_den();

        if denom == From::<i64>::from(1) {
            write!(f, "{}", numer)
        } else {
            write!(f, "{}/{}", numer, denom)
        }
    }
}

impl Zero for Mpq {
    #[inline]
    fn zero() -> Mpq {
        Mpq::zero()
    }

    #[inline]
    fn is_zero(&self) -> bool {
        self.is_zero()
    }
}

impl One for Mpq {
    #[inline]
    fn one() -> Mpq {
        Mpq::one()
    }
}
<|endoftext|>


<commit_before>use std::error::{Error};
use std::fmt;

use base::{FromAscii};

/// An error returned when parsing a numeric value from a ascii string fails.
#[derive(Debug, Clone, PartialEq)]
pub struct ParseIntError {
    kind: IntErrorKind
}

#[derive(Debug, Clone, PartialEq)]
enum IntErrorKind {
    Empty,
    InvalidDigit(u8),
    Overflow,
    Underflow,
    InvalidRadix(u8),
}

impl IntErrorKind {
    fn description(&self) -> &str {
        match self {
            &IntErrorKind::Empty => "cannot parse integer from empty string",
            &IntErrorKind::InvalidDigit(_) => "invalid digit found in string",
            &IntErrorKind::Overflow => "number too large to fit in target type",
            &IntErrorKind::Underflow => "number too small to fit in target type",
            &IntErrorKind::InvalidRadix(_) => "radix should be in 2..36 range",
        }
    }
}

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

impl Error for ParseIntError {
    fn description(&self) -> &str {
        self.kind.description()
    }
}

#[inline]
pub fn dec_to_digit(c: u8, radix: u8) -> Option<u8> {
    let val = match c {
        b'0' ... b'9' => c - b'0',
        b'a' ... b'z' => c - b'a' + 10,
        b'A' ... b'Z' => c - b'A' + 10,
        _ => return None,
    };
    if val < radix {
        Some(val)
    } else {
        None
    }
}

trait FromAsciiHelper: {
    fn signed() -> bool;
}

/// A trait to abstract the idea of creating a new instance of a numeric type from a
/// ascii string with given radix.
///
/// It's a near clone of standard `FromStr` trait.
pub trait FromAsciiRadix: Sized {
    /// The associated error which can be returned from parsing.
    type Err;

    /// Parses a ascii string `s` to return a value of this type.
    ///
    /// If parsing succeeds, return the value inside `Ok`, otherwise
    /// when the string is ill-formatted return an error specific to the
    /// inside `Err`. The error type is specific to implementation of the trait.
    ///
    /// # Examples
    ///
    /// Basic usage with `i32`, a type that implements `FromAsciiRadix`:
    ///
    /// ```
    /// use from_ascii::FromAsciiRadix;
    ///
    /// let s = b"FA";
    /// let x = i32::from_ascii_radix(s, 16).unwrap();
    ///
    /// assert_eq!(250, x);
    /// ```
    fn from_ascii_radix(s: &[u8], radix: u8) -> Result<Self, Self::Err>;
}

macro_rules! implement {
    ($t:ty, $signed: expr) => {
        impl FromAsciiHelper for $t {
            #[inline]
            fn signed() -> bool { $signed }
        }

        impl FromAsciiRadix for $t {
            type Err = ParseIntError;

            #[inline]
            fn from_ascii_radix(src: &[u8], radix: u8) -> Result<Self, Self::Err> {
                if radix < 2 || radix > 36 {
                    return Err(ParseIntError { kind: IntErrorKind::InvalidRadix(radix) })
                }

                let (is_positive, digits) = match src.get(0) {
                    Some(&b'+') => (true, &src[1..]),
                    Some(&b'-') if <$t>::signed() => (false, &src[1..]),
                    Some(_) => (true, src),
                    None => return Err(ParseIntError { kind: IntErrorKind::Empty }),
                };

                if digits.is_empty() {
                    return Err(ParseIntError { kind: IntErrorKind::Empty });
                }

                let mut result: $t = 0;

                if is_positive {
                    for &c in digits {
                        let x = match dec_to_digit(c, radix) {
                            Some(x) => x as $t,
                            None => return Err(ParseIntError { kind: IntErrorKind::InvalidDigit(c) } ),
                        };
                        result = match result.checked_mul(radix as $t) {
                            Some(result) => result,
                            None => return Err(ParseIntError { kind: IntErrorKind::Overflow }),
                        };
                        result = match result.checked_add(x) {
                            Some(result) => result,
                            None => return Err(ParseIntError { kind: IntErrorKind::Overflow }),
                        };
                    }
                } else {
                    for &c in digits {
                        let x = match dec_to_digit(c, radix) {
                            Some(x) => x as $t,
                            None => return Err(ParseIntError { kind: IntErrorKind::InvalidDigit(c) }),
                        };
                        result = match result.checked_mul(radix as $t) {
                            Some(result) => result,
                            None => return Err(ParseIntError { kind: IntErrorKind::Underflow }),
                        };
                        result = match result.checked_sub(x) {
                            Some(result) => result,
                            None => return Err(ParseIntError { kind: IntErrorKind::Underflow }),
                        };
                    }
                }
                Ok(result)
            }
        }

        impl FromAscii for $t {
            type Err = ParseIntError;

            #[inline]
            fn from_ascii(src: &[u8]) -> Result<Self, Self::Err> {
                <$t>::from_ascii_radix(src, 10)
            }
        }
    }
}

implement!(i8, true);
implement!(i16, true);
implement!(i32, true);
implement!(i64, true);
implement!(isize, true);
implement!(u8, false);
implement!(u16, false);
implement!(u32, false);
implement!(u64, false);
implement!(usize, false);

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

    #[test]
    fn test_from_ascii() {
        assert_eq!(i8::from_ascii(b"10"), Ok(10));
        assert_eq!(u8::from_ascii(b"10"), Ok(10));
        assert_eq!(i64::from_ascii(b"10"), Ok(10));
        assert_eq!(u64::from_ascii(b"10"), Ok(10));

        assert_eq!(i8::from_ascii(b"-10"), Ok(-10));
        assert_eq!(u8::from_ascii(b"-10").is_err(), true);
        assert_eq!(i64::from_ascii(b"-10"), Ok(-10));
        assert_eq!(u64::from_ascii(b"-10").is_err(), true);

        assert_eq!(i8::from_ascii(b"1000").is_err(), true);
        assert_eq!(u8::from_ascii(b"1000").is_err(), true);
        assert_eq!(i64::from_ascii(b"1000"), Ok(1000));
        assert_eq!(u64::from_ascii(b"1000"), Ok(1000));

        assert_eq!(i8::from_ascii(b"-1000").is_err(), true);
        assert_eq!(u8::from_ascii(b"-1000").is_err(), true);
        assert_eq!(i64::from_ascii(b"-1000"), Ok(-1000));
        assert_eq!(u64::from_ascii(b"-1000").is_err(), true);
    }
}<commit_msg>Add tests<commit_after>use std::error::{Error};
use std::fmt;

use base::{FromAscii};

/// An error returned when parsing a numeric value from a ascii string fails.
#[derive(Debug, Clone, PartialEq)]
pub struct ParseIntError {
    kind: IntErrorKind
}

#[derive(Debug, Clone, PartialEq)]
enum IntErrorKind {
    Empty,
    InvalidDigit(u8),
    Overflow,
    Underflow,
    InvalidRadix(u8),
}

impl IntErrorKind {
    fn description(&self) -> &str {
        match self {
            &IntErrorKind::Empty => "cannot parse integer from empty string",
            &IntErrorKind::InvalidDigit(_) => "invalid digit found in string",
            &IntErrorKind::Overflow => "number too large to fit in target type",
            &IntErrorKind::Underflow => "number too small to fit in target type",
            &IntErrorKind::InvalidRadix(_) => "radix should be in 2..36 range",
        }
    }
}

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

impl Error for ParseIntError {
    fn description(&self) -> &str {
        self.kind.description()
    }
}

#[inline]
pub fn dec_to_digit(c: u8, radix: u8) -> Option<u8> {
    let val = match c {
        b'0' ... b'9' => c - b'0',
        b'a' ... b'z' => c - b'a' + 10,
        b'A' ... b'Z' => c - b'A' + 10,
        _ => return None,
    };
    if val < radix {
        Some(val)
    } else {
        None
    }
}

trait FromAsciiHelper: {
    fn signed() -> bool;
}

/// A trait to abstract the idea of creating a new instance of a numeric type from a
/// ascii string with given radix.
///
/// It's a near clone of standard `FromStr` trait.
pub trait FromAsciiRadix: Sized {
    /// The associated error which can be returned from parsing.
    type Err;

    /// Parses a ascii string `s` to return a value of this type.
    ///
    /// If parsing succeeds, return the value inside `Ok`, otherwise
    /// when the string is ill-formatted return an error specific to the
    /// inside `Err`. The error type is specific to implementation of the trait.
    ///
    /// # Examples
    ///
    /// Basic usage with `i32`, a type that implements `FromAsciiRadix`:
    ///
    /// ```
    /// use from_ascii::FromAsciiRadix;
    ///
    /// let s = b"FA";
    /// let x = i32::from_ascii_radix(s, 16).unwrap();
    ///
    /// assert_eq!(250, x);
    /// ```
    fn from_ascii_radix(s: &[u8], radix: u8) -> Result<Self, Self::Err>;
}

macro_rules! implement {
    ($t:ty, $signed: expr) => {
        impl FromAsciiHelper for $t {
            #[inline]
            fn signed() -> bool { $signed }
        }

        impl FromAsciiRadix for $t {
            type Err = ParseIntError;

            #[inline]
            fn from_ascii_radix(src: &[u8], radix: u8) -> Result<Self, Self::Err> {
                if radix < 2 || radix > 36 {
                    return Err(ParseIntError { kind: IntErrorKind::InvalidRadix(radix) })
                }

                let (is_positive, digits) = match src.get(0) {
                    Some(&b'+') => (true, &src[1..]),
                    Some(&b'-') if <$t>::signed() => (false, &src[1..]),
                    Some(_) => (true, src),
                    None => return Err(ParseIntError { kind: IntErrorKind::Empty }),
                };

                if digits.is_empty() {
                    return Err(ParseIntError { kind: IntErrorKind::Empty });
                }

                let mut result: $t = 0;

                if is_positive {
                    for &c in digits {
                        let x = match dec_to_digit(c, radix) {
                            Some(x) => x as $t,
                            None => return Err(ParseIntError { kind: IntErrorKind::InvalidDigit(c) } ),
                        };
                        result = match result.checked_mul(radix as $t) {
                            Some(result) => result,
                            None => return Err(ParseIntError { kind: IntErrorKind::Overflow }),
                        };
                        result = match result.checked_add(x) {
                            Some(result) => result,
                            None => return Err(ParseIntError { kind: IntErrorKind::Overflow }),
                        };
                    }
                } else {
                    for &c in digits {
                        let x = match dec_to_digit(c, radix) {
                            Some(x) => x as $t,
                            None => return Err(ParseIntError { kind: IntErrorKind::InvalidDigit(c) }),
                        };
                        result = match result.checked_mul(radix as $t) {
                            Some(result) => result,
                            None => return Err(ParseIntError { kind: IntErrorKind::Underflow }),
                        };
                        result = match result.checked_sub(x) {
                            Some(result) => result,
                            None => return Err(ParseIntError { kind: IntErrorKind::Underflow }),
                        };
                    }
                }
                Ok(result)
            }
        }

        impl FromAscii for $t {
            type Err = ParseIntError;

            #[inline]
            fn from_ascii(src: &[u8]) -> Result<Self, Self::Err> {
                <$t>::from_ascii_radix(src, 10)
            }
        }
    }
}

implement!(i8, true);
implement!(i16, true);
implement!(i32, true);
implement!(i64, true);
implement!(isize, true);
implement!(u8, false);
implement!(u16, false);
implement!(u32, false);
implement!(u64, false);
implement!(usize, false);

#[cfg(test)]
mod tests {
    use super::super::base::FromAscii;
    use super::FromAsciiRadix;

    #[test]
    fn test_from_ascii() {
        assert_eq!(i8::from_ascii(b"10"), Ok(10));
        assert_eq!(u8::from_ascii(b"10"), Ok(10));
        assert_eq!(i64::from_ascii(b"10"), Ok(10));
        assert_eq!(u64::from_ascii(b"10"), Ok(10));

        assert_eq!(i8::from_ascii(b"-10"), Ok(-10));
        assert_eq!(u8::from_ascii(b"-10").is_err(), true);
        assert_eq!(i64::from_ascii(b"-10"), Ok(-10));
        assert_eq!(u64::from_ascii(b"-10").is_err(), true);

        assert_eq!(i8::from_ascii(b"1000").is_err(), true);
        assert_eq!(u8::from_ascii(b"1000").is_err(), true);
        assert_eq!(i64::from_ascii(b"1000"), Ok(1000));
        assert_eq!(u64::from_ascii(b"1000"), Ok(1000));

        assert_eq!(i8::from_ascii(b"-1000").is_err(), true);
        assert_eq!(u8::from_ascii(b"-1000").is_err(), true);
        assert_eq!(i64::from_ascii(b"-1000"), Ok(-1000));
        assert_eq!(u64::from_ascii(b"-1000").is_err(), true);
    }

    #[test]
    fn test_from_ascii_radix() {
        assert_eq!(i8::from_ascii_radix(b"ff", 16).is_err(), true);
        assert_eq!(u8::from_ascii_radix(b"ff", 16), Ok(255));
        assert_eq!(i8::from_ascii_radix(b"FF", 16).is_err(), true);
        assert_eq!(u8::from_ascii_radix(b"FF", 16), Ok(255));
    }
}<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014-2015 Mathias Hällman
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#![feature(collections)]
#![feature(core)]
#![feature(hash)]
#![feature(fs)]
#![feature(int_uint)]
#![feature(io)]
#![feature(libc)]
#![feature(old_io)]
#![feature(path)]
#![feature(rustc_private)]
#![feature(unicode)]

#![cfg_attr(test, feature(os))]
#![cfg_attr(test, feature(std_misc))]

#[macro_use]
extern crate bitflags;

#[cfg(not(test))]
use std::collections::HashMap;
#[cfg(not(test))]
use std::path::Path;

#[allow(dead_code, unused_imports)]  // temporary until buffer is used for real
mod buffer;
mod frame;
mod input;
mod keymap;
mod screen;
mod view;

#[cfg(not(test))]
struct Rim {
  frame: frame::Frame,
  frame_ctx: frame::FrameContext,
  windows: HashMap<frame::WindowId, view::View>,
  focus: frame::WindowId,
  buffer: buffer::Buffer,
}

#[cfg(not(test))]
impl Rim {
  fn new() -> Rim {
    let (frame, frame_ctx, first_window) = frame::Frame::new();
    let mut windows = HashMap::new();
    windows.insert(first_window.clone(), view::View::new());
    Rim {
      frame: frame,
      frame_ctx: frame_ctx,
      windows: windows,
      focus: first_window,
      buffer: buffer::Buffer::open(&Path::new("src/rim.rs")).unwrap(),
    }
  }

  fn switch_focus(&mut self, direction: frame::Direction)
      -> Option<(frame::WindowId, frame::WindowId)> {
    self.frame.get_adjacent_window(&self.frame_ctx, &self.focus, direction).
    map(|window| self.move_focus(window)).ok()
  }

  fn shift_focus(&mut self, order: frame::WindowOrder)
      -> Option<(frame::WindowId, frame::WindowId)> {
    self.frame.get_sequent_window(&self.frame_ctx, &self.focus, order, true).
    map(|window| self.move_focus(window)).ok()
  }

  fn move_focus(&mut self, window: frame::WindowId)
      -> (frame::WindowId, frame::WindowId) {
    let old_focus = std::mem::replace(&mut self.focus, window);
    (old_focus, self.focus.clone())
  }

  fn split_window(&mut self, orientation: frame::Orientation) {
    self.frame.split_window(&mut self.frame_ctx, &self.focus, orientation).
    map(|new_window| self.windows.insert(new_window, view::View::new())).
    ok().expect("Failed to split window.");
  }

  fn resize_window(&mut self, orientation: frame::Orientation, amount: int)
     -> bool {
    self.frame.resize_window(&self.frame_ctx, &self.focus, orientation, amount).
    map(|absorbed| absorbed != 0).unwrap()
  }

  fn close_window(&mut self) -> bool {
    self.frame.get_closest_neighbouring_window(&self.frame_ctx, &self.focus).
    map(|neighbour| {
      let (old_focus, _) = self.move_focus(neighbour);
      self.frame.close_window(&mut self.frame_ctx, &old_focus).unwrap();
      self.windows.remove(&old_focus); }).
    is_ok()
  }

  fn draw_window(&self, window: &frame::WindowId, screen: &mut screen::Screen) {
    self.windows.get(window).
    map(|view| self.draw_view(window, view, screen)).
    expect("Couldn't find view for window.");
    screen.flush();
  }

  fn draw_view(&self, window: &frame::WindowId, view: &view::View,
               screen: &mut screen::Screen) {
    self.frame.get_window_rect(&self.frame_ctx, window).
    map(|rect| { let screen::Rect(position, _) = rect; position }).
    map(|pos| view.draw(&self.buffer, self.focus == *window, pos, screen)).
    ok().expect("Couldn't find rect for window.");
  }

  fn draw_all(&self, screen: &mut screen::Screen) {
    screen.clear();
    self.frame.draw_borders(screen);
    for (window, view) in self.windows.iter() {
      self.draw_view(window, view, screen);
    }
    screen.flush();
  }

  fn update_view_sizes(&mut self) {
    let window_sizes: Vec<(frame::WindowId, screen::Size)> =
      self.windows.iter().
      map(|(win, _)|
        self.frame.get_window_rect(&self.frame_ctx, win).
        map(|rect| { let screen::Rect(_, size) = rect; (win.clone(), size) }).
        ok().expect("Couldn't find rect for window.")).
      collect();
    for &(ref win, size) in window_sizes.iter() {
      self.windows.remove(win).
      map(|mut view| {
        view.set_size(size, &self.buffer);
        self.windows.insert(win.clone(), view); }).
      expect("Couldn't find view for window.");
    }
  }

  fn handle_key_by_view(&mut self, key: keymap::Key) -> bool {
     self.windows.remove(&self.focus).
     map(|mut view| {
       let swallowed = match key {
         keymap::Key::Sym{sym: keymap::KeySym::Left, mods:_}  => {
           view.move_caret(view::CaretMovement::CharPrev, &self.buffer); true
         }
         keymap::Key::Sym{sym: keymap::KeySym::Right, mods:_} => {
           view.move_caret(view::CaretMovement::CharNext, &self.buffer); true
         }
         keymap::Key::Sym{sym: keymap::KeySym::Up, mods:_}    => {
           view.move_caret(view::CaretMovement::LineUp, &self.buffer); true
         }
         keymap::Key::Sym{sym: keymap::KeySym::Down, mods:_}  => {
           view.move_caret(view::CaretMovement::LineDown, &self.buffer); true
         }
         _                                                    => false,
       };
       self.windows.insert(self.focus.clone(), view);
       swallowed }).
     expect("Found no view for focused window.")
  }
}

#[cfg(not(test))]
impl Drop for Rim {
  fn drop(&mut self) {
    self.buffer.write().unwrap();  // what could go wrong!
  }
}

#[cfg(not(test))]
fn main() {
  let mut screen = screen::Screen::setup().unwrap();

  let mut rim = Rim::new();

  let (key_tx, key_rx) = std::sync::mpsc::channel();
  let _term_input = input::start(key_tx);

  let draw_window_pair =
    |&: (win1, win2), rim: &Rim, screen: &mut screen::Screen|
      { rim.draw_window(&win1, screen); rim.draw_window(&win2, screen); };

  loop {
    match key_rx.try_recv().ok().and_then(|key|
        if !rim.handle_key_by_view(key) { Some(key) }
        else { rim.draw_window(&rim.focus, &mut screen); None }) {
      Some(keymap::Key::Unicode{codepoint: 'h', mods:_}) => {
        rim.switch_focus(frame::Direction::Left).
        map(|pair| draw_window_pair(pair, &rim, &mut screen));
      }
      Some(keymap::Key::Unicode{codepoint: 'l', mods:_}) => {
        rim.switch_focus(frame::Direction::Right).
        map(|pair| draw_window_pair(pair, &rim, &mut screen));
      }
      Some(keymap::Key::Unicode{codepoint: 'k', mods:_}) => {
        rim.switch_focus(frame::Direction::Up).
        map(|pair| draw_window_pair(pair, &rim, &mut screen));
      }
      Some(keymap::Key::Unicode{codepoint: 'j', mods:_}) => {
        rim.switch_focus(frame::Direction::Down).
        map(|pair| draw_window_pair(pair, &rim, &mut screen));
      }
      Some(keymap::Key::Unicode{codepoint: 'n', mods:_}) => {
        rim.shift_focus(frame::WindowOrder::NextWindow).
        map(|pair| draw_window_pair(pair, &rim, &mut screen));
      }
      Some(keymap::Key::Unicode{codepoint: 'N', mods:_}) => {
        rim.shift_focus(frame::WindowOrder::PreviousWindow).
        map(|pair| draw_window_pair(pair, &rim, &mut screen));
      }
      Some(keymap::Key::Unicode{codepoint: '=', mods:_}) => {
        rim.frame.reset_layout();
        rim.update_view_sizes();
        rim.draw_all(&mut screen);
      }
      Some(keymap::Key::Unicode{codepoint: 'v', mods:_}) => {
        rim.split_window(frame::Orientation::Vertical);
        rim.update_view_sizes();
        rim.draw_all(&mut screen);
      }
      Some(keymap::Key::Unicode{codepoint: 's', mods:_}) => {
        rim.split_window(frame::Orientation::Horizontal);
        rim.update_view_sizes();
        rim.draw_all(&mut screen);
      }
      Some(keymap::Key::Unicode{codepoint: 'y', mods})   => {
        let amount = if mods.contains(keymap::MOD_CTRL) { -10 }
                     else                               { 10 };
        if rim.resize_window(frame::Orientation::Horizontal, amount) {
          rim.update_view_sizes();
          rim.draw_all(&mut screen);
        }
      }
      Some(keymap::Key::Unicode{codepoint: 'u', mods})   => {
        let amount = if mods.contains(keymap::MOD_CTRL) { -10 }
                     else                               { 10 };
        if rim.resize_window(frame::Orientation::Vertical, amount) {
          rim.update_view_sizes();
          rim.draw_all(&mut screen);
        }
      }
      Some(keymap::Key::Unicode{codepoint: 'c', mods:_}) =>
        if rim.close_window() {
          rim.update_view_sizes();
          rim.draw_all(&mut screen);
        },
      Some(keymap::Key::Unicode{codepoint: 'q', mods:_}) =>
        break,
      _                                                  =>
        (),
    }

    if screen.update_size() {
      rim.frame.set_size(screen.size());
      rim.update_view_sizes();
      rim.draw_all(&mut screen);
    }
  }
}
<commit_msg>Undeclare unused or unknown feature<commit_after>/*
 * Copyright (c) 2014-2015 Mathias Hällman
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#![feature(collections)]
#![feature(core)]
#![feature(fs)]
#![feature(int_uint)]
#![feature(io)]
#![feature(libc)]
#![feature(old_io)]
#![feature(path)]
#![feature(rustc_private)]
#![feature(unicode)]

#![cfg_attr(test, feature(os))]
#![cfg_attr(test, feature(std_misc))]

#[macro_use]
extern crate bitflags;

#[cfg(not(test))]
use std::collections::HashMap;
#[cfg(not(test))]
use std::path::Path;

#[allow(dead_code, unused_imports)]  // temporary until buffer is used for real
mod buffer;
mod frame;
mod input;
mod keymap;
mod screen;
mod view;

#[cfg(not(test))]
struct Rim {
  frame: frame::Frame,
  frame_ctx: frame::FrameContext,
  windows: HashMap<frame::WindowId, view::View>,
  focus: frame::WindowId,
  buffer: buffer::Buffer,
}

#[cfg(not(test))]
impl Rim {
  fn new() -> Rim {
    let (frame, frame_ctx, first_window) = frame::Frame::new();
    let mut windows = HashMap::new();
    windows.insert(first_window.clone(), view::View::new());
    Rim {
      frame: frame,
      frame_ctx: frame_ctx,
      windows: windows,
      focus: first_window,
      buffer: buffer::Buffer::open(&Path::new("src/rim.rs")).unwrap(),
    }
  }

  fn switch_focus(&mut self, direction: frame::Direction)
      -> Option<(frame::WindowId, frame::WindowId)> {
    self.frame.get_adjacent_window(&self.frame_ctx, &self.focus, direction).
    map(|window| self.move_focus(window)).ok()
  }

  fn shift_focus(&mut self, order: frame::WindowOrder)
      -> Option<(frame::WindowId, frame::WindowId)> {
    self.frame.get_sequent_window(&self.frame_ctx, &self.focus, order, true).
    map(|window| self.move_focus(window)).ok()
  }

  fn move_focus(&mut self, window: frame::WindowId)
      -> (frame::WindowId, frame::WindowId) {
    let old_focus = std::mem::replace(&mut self.focus, window);
    (old_focus, self.focus.clone())
  }

  fn split_window(&mut self, orientation: frame::Orientation) {
    self.frame.split_window(&mut self.frame_ctx, &self.focus, orientation).
    map(|new_window| self.windows.insert(new_window, view::View::new())).
    ok().expect("Failed to split window.");
  }

  fn resize_window(&mut self, orientation: frame::Orientation, amount: int)
     -> bool {
    self.frame.resize_window(&self.frame_ctx, &self.focus, orientation, amount).
    map(|absorbed| absorbed != 0).unwrap()
  }

  fn close_window(&mut self) -> bool {
    self.frame.get_closest_neighbouring_window(&self.frame_ctx, &self.focus).
    map(|neighbour| {
      let (old_focus, _) = self.move_focus(neighbour);
      self.frame.close_window(&mut self.frame_ctx, &old_focus).unwrap();
      self.windows.remove(&old_focus); }).
    is_ok()
  }

  fn draw_window(&self, window: &frame::WindowId, screen: &mut screen::Screen) {
    self.windows.get(window).
    map(|view| self.draw_view(window, view, screen)).
    expect("Couldn't find view for window.");
    screen.flush();
  }

  fn draw_view(&self, window: &frame::WindowId, view: &view::View,
               screen: &mut screen::Screen) {
    self.frame.get_window_rect(&self.frame_ctx, window).
    map(|rect| { let screen::Rect(position, _) = rect; position }).
    map(|pos| view.draw(&self.buffer, self.focus == *window, pos, screen)).
    ok().expect("Couldn't find rect for window.");
  }

  fn draw_all(&self, screen: &mut screen::Screen) {
    screen.clear();
    self.frame.draw_borders(screen);
    for (window, view) in self.windows.iter() {
      self.draw_view(window, view, screen);
    }
    screen.flush();
  }

  fn update_view_sizes(&mut self) {
    let window_sizes: Vec<(frame::WindowId, screen::Size)> =
      self.windows.iter().
      map(|(win, _)|
        self.frame.get_window_rect(&self.frame_ctx, win).
        map(|rect| { let screen::Rect(_, size) = rect; (win.clone(), size) }).
        ok().expect("Couldn't find rect for window.")).
      collect();
    for &(ref win, size) in window_sizes.iter() {
      self.windows.remove(win).
      map(|mut view| {
        view.set_size(size, &self.buffer);
        self.windows.insert(win.clone(), view); }).
      expect("Couldn't find view for window.");
    }
  }

  fn handle_key_by_view(&mut self, key: keymap::Key) -> bool {
     self.windows.remove(&self.focus).
     map(|mut view| {
       let swallowed = match key {
         keymap::Key::Sym{sym: keymap::KeySym::Left, mods:_}  => {
           view.move_caret(view::CaretMovement::CharPrev, &self.buffer); true
         }
         keymap::Key::Sym{sym: keymap::KeySym::Right, mods:_} => {
           view.move_caret(view::CaretMovement::CharNext, &self.buffer); true
         }
         keymap::Key::Sym{sym: keymap::KeySym::Up, mods:_}    => {
           view.move_caret(view::CaretMovement::LineUp, &self.buffer); true
         }
         keymap::Key::Sym{sym: keymap::KeySym::Down, mods:_}  => {
           view.move_caret(view::CaretMovement::LineDown, &self.buffer); true
         }
         _                                                    => false,
       };
       self.windows.insert(self.focus.clone(), view);
       swallowed }).
     expect("Found no view for focused window.")
  }
}

#[cfg(not(test))]
impl Drop for Rim {
  fn drop(&mut self) {
    self.buffer.write().unwrap();  // what could go wrong!
  }
}

#[cfg(not(test))]
fn main() {
  let mut screen = screen::Screen::setup().unwrap();

  let mut rim = Rim::new();

  let (key_tx, key_rx) = std::sync::mpsc::channel();
  let _term_input = input::start(key_tx);

  let draw_window_pair =
    |&: (win1, win2), rim: &Rim, screen: &mut screen::Screen|
      { rim.draw_window(&win1, screen); rim.draw_window(&win2, screen); };

  loop {
    match key_rx.try_recv().ok().and_then(|key|
        if !rim.handle_key_by_view(key) { Some(key) }
        else { rim.draw_window(&rim.focus, &mut screen); None }) {
      Some(keymap::Key::Unicode{codepoint: 'h', mods:_}) => {
        rim.switch_focus(frame::Direction::Left).
        map(|pair| draw_window_pair(pair, &rim, &mut screen));
      }
      Some(keymap::Key::Unicode{codepoint: 'l', mods:_}) => {
        rim.switch_focus(frame::Direction::Right).
        map(|pair| draw_window_pair(pair, &rim, &mut screen));
      }
      Some(keymap::Key::Unicode{codepoint: 'k', mods:_}) => {
        rim.switch_focus(frame::Direction::Up).
        map(|pair| draw_window_pair(pair, &rim, &mut screen));
      }
      Some(keymap::Key::Unicode{codepoint: 'j', mods:_}) => {
        rim.switch_focus(frame::Direction::Down).
        map(|pair| draw_window_pair(pair, &rim, &mut screen));
      }
      Some(keymap::Key::Unicode{codepoint: 'n', mods:_}) => {
        rim.shift_focus(frame::WindowOrder::NextWindow).
        map(|pair| draw_window_pair(pair, &rim, &mut screen));
      }
      Some(keymap::Key::Unicode{codepoint: 'N', mods:_}) => {
        rim.shift_focus(frame::WindowOrder::PreviousWindow).
        map(|pair| draw_window_pair(pair, &rim, &mut screen));
      }
      Some(keymap::Key::Unicode{codepoint: '=', mods:_}) => {
        rim.frame.reset_layout();
        rim.update_view_sizes();
        rim.draw_all(&mut screen);
      }
      Some(keymap::Key::Unicode{codepoint: 'v', mods:_}) => {
        rim.split_window(frame::Orientation::Vertical);
        rim.update_view_sizes();
        rim.draw_all(&mut screen);
      }
      Some(keymap::Key::Unicode{codepoint: 's', mods:_}) => {
        rim.split_window(frame::Orientation::Horizontal);
        rim.update_view_sizes();
        rim.draw_all(&mut screen);
      }
      Some(keymap::Key::Unicode{codepoint: 'y', mods})   => {
        let amount = if mods.contains(keymap::MOD_CTRL) { -10 }
                     else                               { 10 };
        if rim.resize_window(frame::Orientation::Horizontal, amount) {
          rim.update_view_sizes();
          rim.draw_all(&mut screen);
        }
      }
      Some(keymap::Key::Unicode{codepoint: 'u', mods})   => {
        let amount = if mods.contains(keymap::MOD_CTRL) { -10 }
                     else                               { 10 };
        if rim.resize_window(frame::Orientation::Vertical, amount) {
          rim.update_view_sizes();
          rim.draw_all(&mut screen);
        }
      }
      Some(keymap::Key::Unicode{codepoint: 'c', mods:_}) =>
        if rim.close_window() {
          rim.update_view_sizes();
          rim.draw_all(&mut screen);
        },
      Some(keymap::Key::Unicode{codepoint: 'q', mods:_}) =>
        break,
      _                                                  =>
        (),
    }

    if screen.update_size() {
      rim.frame.set_size(screen.size());
      rim.update_view_sizes();
      rim.draw_all(&mut screen);
    }
  }
}
<|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 {
            ::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,
}

/// 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) {
                                // value string is copied into the attribute
                                let new_val = CString::new(value.replacen(&from_pt_string, &to_pt_string, 1)).unwrap();
                                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 an owned C-style string.
    pub fn to_glibstring(&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 fmt::Debug for Sdp {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Sdp {{ {} }}", self.to_glibstring().to_string_lossy())
    }
}

impl Serialize for Sdp {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
        self.to_glibstring().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>Add interface to SDP m-line attributes<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,
}

/// 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),
            }
        }
    }

    /// Adds an attribute for the m-line with the given payload type.
    pub fn add_attribute(&mut self, pt: i32, name: &CStr, contents: &CStr) {
        for (_media, m_lines) in self.get_mlines() {
            unsafe {
                for m_line in m_lines {
                    if !glib::g_list_find(m_line.ptypes, pt as *const _).is_null() {
                        let attr = ffi::sdp::janus_sdp_attribute_create(name.as_ptr(), contents.as_ptr());
                        ffi::sdp::janus_sdp_attribute_add_to_mline(m_line as *mut _, attr as *mut _);
                    }
                }
            }
        }
    }

    /// 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) {
                                // value string is copied into the attribute
                                let new_val = CString::new(value.replacen(&from_pt_string, &to_pt_string, 1)).unwrap();
                                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 an owned C-style string.
    pub fn to_glibstring(&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 fmt::Debug for Sdp {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Sdp {{ {} }}", self.to_glibstring().to_string_lossy())
    }
}

impl Serialize for Sdp {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
        self.to_glibstring().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>trait StrParseExt {
    fn end_of_start_rest<F1, F2>(&self, is_first: F1, is_rest: F2) -> Option<usize>
        where F1: Fn(char) -> bool,
              F2: Fn(char) -> bool;
}

impl<'a> StrParseExt for &'a str {
    fn end_of_start_rest<F1, F2>(&self, is_first: F1, is_rest: F2) -> Option<usize>
        where F1: Fn(char) -> bool,
              F2: Fn(char) -> bool,
    {
        let mut positions = self.char_indices();

        match positions.next() {
            Some((_, c)) if is_first(c) => (),
            Some((_, _)) => return None,
            None => return None,
        };

        let mut positions = positions.skip_while(|&(_, c)| is_rest(c));
        match positions.next() {
            Some((offset, _)) => Some(offset),
            None => Some(self.len()),
        }
    }
}

pub trait XmlStr {
    /// Find the end of the quoted attribute value, not including the quote
    fn end_of_attribute(&self, quote: &str) -> Option<usize>;
    /// Find the end of the direct character data
    fn end_of_char_data(&self) -> Option<usize>;
    /// Find the end of the CData section, not including the ]]>
    fn end_of_cdata(&self) -> Option<usize>;
    /// Find the end of a run of decimal characters
    fn end_of_decimal_chars(&self) -> Option<usize>;
    /// Find the end of a run of hexidecimal characters
    fn end_of_hex_chars(&self) -> Option<usize>;
    /// Find the end of the comment, not including the -->
    fn end_of_comment(&self) -> Option<usize>;
    /// Find the end of the processing instruction, not including the ?>
    fn end_of_pi_value(&self) -> Option<usize>;
    /// Find the end of the [Name](http://www.w3.org/TR/xml/#NT-Name)
    fn end_of_name(&self) -> Option<usize>;
    /// Find the end of the [NCName](http://www.w3.org/TR/REC-xml-names/#NT-NCName)
    fn end_of_ncname(&self) -> Option<usize>;
    /// Find the end of a run of space characters
    fn end_of_space(&self) -> Option<usize>;
    /// Find the end of the starting tag
    fn end_of_start_tag(&self) -> Option<usize>;
}

impl<'a> XmlStr for &'a str {
    fn end_of_attribute(&self, quote: &str) -> Option<usize> {
        if self.len() == 0 ||
           self.starts_with("&") ||
           self.starts_with("<") ||
           self.starts_with(quote)
        {
            return None;
        }

        let quote_char = quote.chars().next().expect("Cant have null quote");

        let mut positions = self.char_indices().skip_while(|&(_, c)| c != '&' && c != '<' && c != quote_char);

        match positions.next() {
            Some((offset, _)) => Some(offset),
            None => Some(self.len())
        }
    }

    fn end_of_char_data(&self) -> Option<usize> {
        if self.starts_with("<") ||
           self.starts_with("&") ||
           self.starts_with("]]>")
        {
            return None
        }

        let mut positions = self.char_indices().skip_while(|&(_, c)| {
            c != '<' && c != '&' && c != ']'
        });

        loop {
            match positions.next() {
                None => return Some(self.len()),
                Some((offset, c)) if c == '<' || c == '&' => return Some(offset),
                Some((offset, _)) => {
                    let tail = &self[offset..];
                    if tail.starts_with("]]>") {
                        return Some(offset)
                    } else {
                        // False alarm, resume scanning
                        continue;
                    }
                },
            }
        }
    }

    fn end_of_cdata(&self) -> Option<usize> {
        match self.find("]]>") {
            None => None,
            Some(offset) => Some(offset),
        }
    }

    fn end_of_decimal_chars(&self) -> Option<usize> {
        self.end_of_start_rest(|c| c.is_decimal_char(),
                               |c| c.is_decimal_char())
    }

    fn end_of_hex_chars(&self) -> Option<usize> {
        self.end_of_start_rest(|c| c.is_hex_char(),
                               |c| c.is_hex_char())
    }

    fn end_of_comment(&self) -> Option<usize> {
        // This deliberately does not include the >. -- is not allowed
        // in a comment, so we can just test the end if it matches the
        // complete close delimiter.
        match self.find("--") {
            None => None,
            Some(offset) => Some(offset),
        }
    }

    fn end_of_pi_value(&self) -> Option<usize> {
        match self.find("?>") {
            None => None,
            Some(offset) => Some(offset),
        }
    }

    fn end_of_name(&self) -> Option<usize> {
        self.end_of_start_rest(|c| c.is_name_start_char(), |c| c.is_name_char())
    }

    fn end_of_ncname(&self) -> Option<usize> {
        self.end_of_start_rest(|c| c.is_ncname_start_char(), |c| c.is_ncname_char())
    }

    fn end_of_space(&self) -> Option<usize> {
        self.end_of_start_rest(|c| c.is_space_char(), |c| c.is_space_char())
    }

    fn end_of_start_tag(&self) -> Option<usize> {
        let mut positions = self.char_indices();

        match positions.next() {
            Some((_, c)) if '<' == c => (),
            _ => return None,
        };

        match positions.next() {
            Some((offset, c)) =>
                match c {
                    '?' | '!' | '/' => None,
                    _ => Some(offset),
                },
            None => Some(self.len()),
        }
    }
}

/// Predicates used when parsing an characters in an XML document.
pub trait XmlChar {
    /// Is this a [NameStartChar](http://www.w3.org/TR/xml/#NT-NameStartChar)?
    fn is_name_start_char(self) -> bool;
    /// Is this a [NameChar](http://www.w3.org/TR/xml/#NT-NameChar)?
    fn is_name_char(self) -> bool;
    /// Does this start a [NCName](http://www.w3.org/TR/REC-xml-names/#NT-NCName)?
    fn is_ncname_start_char(self) -> bool;
    /// Is this a component of a [NCName](http://www.w3.org/TR/REC-xml-names/#NT-NCName)?
    fn is_ncname_char(self) -> bool;
    /// Is this an [XML space](http://www.w3.org/TR/xml/#NT-S)?
    fn is_space_char(self) -> bool;
    fn is_decimal_char(self) -> bool;
    fn is_hex_char(self) -> bool;
}

impl XmlChar for char {
    fn is_name_start_char(self) -> bool {
        self == ':' || self.is_ncname_start_char()
    }

    fn is_name_char(self) -> bool {
        self.is_name_start_char() || self.is_ncname_char()
    }

    fn is_ncname_start_char(self) -> bool {
        match self {
            'A'...'Z'                   |
            '_'                         |
            'a'...'z'                   |
            '\u{0000C0}'...'\u{0000D6}' |
            '\u{0000D8}'...'\u{0000F6}' |
            '\u{0000F8}'...'\u{0002FF}' |
            '\u{000370}'...'\u{00037D}' |
            '\u{00037F}'...'\u{001FFF}' |
            '\u{00200C}'...'\u{00200D}' |
            '\u{002070}'...'\u{00218F}' |
            '\u{002C00}'...'\u{002FEF}' |
            '\u{003001}'...'\u{00D7FF}' |
            '\u{00F900}'...'\u{00FDCF}' |
            '\u{00FDF0}'...'\u{00FFFD}' |
            '\u{010000}'...'\u{0EFFFF}' => true,
            _ => false,
        }
    }

    fn is_ncname_char(self) -> bool {
        if self.is_ncname_start_char() { return true; }
        match self {
            '-'                     |
            '.'                     |
            '0'...'9'               |
            '\u{00B7}'              |
            '\u{0300}'...'\u{036F}' |
            '\u{203F}'...'\u{2040}' => true,
            _ => false
        }
    }

    fn is_space_char(self) -> bool {
        match self {
            '\x20' |
            '\x09' |
            '\x0D' |
            '\x0A' => true,
            _ => false,
        }
    }

    fn is_decimal_char(self) -> bool {
        match self {
            '0'...'9' => true,
            _ => false,
        }
    }

    fn is_hex_char(self) -> bool {
        match self {
            '0'...'9' |
            'a'...'f' |
            'A'...'F' => true,
            _ => false,
        }
    }
}

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

    #[test]
    fn end_of_char_data_leading_ampersand() {
        assert_eq!("&".end_of_char_data(), None);
    }

    #[test]
    fn end_of_char_data_leading_less_than() {
        assert_eq!("<".end_of_char_data(), None);
    }

    #[test]
    fn end_of_char_data_leading_cdata_end() {
        assert_eq!("]]>".end_of_char_data(), None);
    }

    #[test]
    fn end_of_char_data_until_ampersand() {
        assert_eq!("hello&world".end_of_char_data(), Some("hello".len()));
    }

    #[test]
    fn end_of_char_data_until_less_than() {
        assert_eq!("hello<world".end_of_char_data(), Some("hello".len()));
    }

    #[test]
    fn end_of_char_data_until_cdata_end() {
        assert_eq!("hello]]>world".end_of_char_data(), Some("hello".len()));
    }

    #[test]
    fn end_of_char_data_includes_right_square() {
        assert_eq!("hello]world".end_of_char_data(), Some("hello]world".len()));
    }
}
<commit_msg>Don't step character-by-character after matching a false end<commit_after>trait StrParseExt {
    fn end_of_start_rest<F1, F2>(&self, is_first: F1, is_rest: F2) -> Option<usize>
        where F1: Fn(char) -> bool,
              F2: Fn(char) -> bool;
}

impl<'a> StrParseExt for &'a str {
    fn end_of_start_rest<F1, F2>(&self, is_first: F1, is_rest: F2) -> Option<usize>
        where F1: Fn(char) -> bool,
              F2: Fn(char) -> bool,
    {
        let mut positions = self.char_indices();

        match positions.next() {
            Some((_, c)) if is_first(c) => (),
            Some((_, _)) => return None,
            None => return None,
        };

        let mut positions = positions.skip_while(|&(_, c)| is_rest(c));
        match positions.next() {
            Some((offset, _)) => Some(offset),
            None => Some(self.len()),
        }
    }
}

pub trait XmlStr {
    /// Find the end of the quoted attribute value, not including the quote
    fn end_of_attribute(&self, quote: &str) -> Option<usize>;
    /// Find the end of the direct character data
    fn end_of_char_data(&self) -> Option<usize>;
    /// Find the end of the CData section, not including the ]]>
    fn end_of_cdata(&self) -> Option<usize>;
    /// Find the end of a run of decimal characters
    fn end_of_decimal_chars(&self) -> Option<usize>;
    /// Find the end of a run of hexidecimal characters
    fn end_of_hex_chars(&self) -> Option<usize>;
    /// Find the end of the comment, not including the -->
    fn end_of_comment(&self) -> Option<usize>;
    /// Find the end of the processing instruction, not including the ?>
    fn end_of_pi_value(&self) -> Option<usize>;
    /// Find the end of the [Name](http://www.w3.org/TR/xml/#NT-Name)
    fn end_of_name(&self) -> Option<usize>;
    /// Find the end of the [NCName](http://www.w3.org/TR/REC-xml-names/#NT-NCName)
    fn end_of_ncname(&self) -> Option<usize>;
    /// Find the end of a run of space characters
    fn end_of_space(&self) -> Option<usize>;
    /// Find the end of the starting tag
    fn end_of_start_tag(&self) -> Option<usize>;
}

impl<'a> XmlStr for &'a str {
    fn end_of_attribute(&self, quote: &str) -> Option<usize> {
        if self.len() == 0 ||
           self.starts_with("&") ||
           self.starts_with("<") ||
           self.starts_with(quote)
        {
            return None;
        }

        let quote_char = quote.chars().next().expect("Cant have null quote");

        let mut positions = self.char_indices().skip_while(|&(_, c)| c != '&' && c != '<' && c != quote_char);

        match positions.next() {
            Some((offset, _)) => Some(offset),
            None => Some(self.len())
        }
    }

    fn end_of_char_data(&self) -> Option<usize> {
        if self.starts_with("<") ||
           self.starts_with("&") ||
           self.starts_with("]]>")
        {
            return None
        }

        let mut indices = self.char_indices();

        loop {
            let mut positions = indices.by_ref().skip_while(|&(_, c)| {
                c != '<' && c != '&' && c != ']'
            });

            match positions.next() {
                None => return Some(self.len()),
                Some((offset, c)) if c == '<' || c == '&' => return Some(offset),
                Some((offset, _)) => {
                    let tail = &self[offset..];
                    if tail.starts_with("]]>") {
                        return Some(offset)
                    } else {
                        // False alarm, resume scanning
                        continue;
                    }
                },
            }
        }
    }

    fn end_of_cdata(&self) -> Option<usize> {
        match self.find("]]>") {
            None => None,
            Some(offset) => Some(offset),
        }
    }

    fn end_of_decimal_chars(&self) -> Option<usize> {
        self.end_of_start_rest(|c| c.is_decimal_char(),
                               |c| c.is_decimal_char())
    }

    fn end_of_hex_chars(&self) -> Option<usize> {
        self.end_of_start_rest(|c| c.is_hex_char(),
                               |c| c.is_hex_char())
    }

    fn end_of_comment(&self) -> Option<usize> {
        // This deliberately does not include the >. -- is not allowed
        // in a comment, so we can just test the end if it matches the
        // complete close delimiter.
        match self.find("--") {
            None => None,
            Some(offset) => Some(offset),
        }
    }

    fn end_of_pi_value(&self) -> Option<usize> {
        match self.find("?>") {
            None => None,
            Some(offset) => Some(offset),
        }
    }

    fn end_of_name(&self) -> Option<usize> {
        self.end_of_start_rest(|c| c.is_name_start_char(), |c| c.is_name_char())
    }

    fn end_of_ncname(&self) -> Option<usize> {
        self.end_of_start_rest(|c| c.is_ncname_start_char(), |c| c.is_ncname_char())
    }

    fn end_of_space(&self) -> Option<usize> {
        self.end_of_start_rest(|c| c.is_space_char(), |c| c.is_space_char())
    }

    fn end_of_start_tag(&self) -> Option<usize> {
        let mut positions = self.char_indices();

        match positions.next() {
            Some((_, c)) if '<' == c => (),
            _ => return None,
        };

        match positions.next() {
            Some((offset, c)) =>
                match c {
                    '?' | '!' | '/' => None,
                    _ => Some(offset),
                },
            None => Some(self.len()),
        }
    }
}

/// Predicates used when parsing an characters in an XML document.
pub trait XmlChar {
    /// Is this a [NameStartChar](http://www.w3.org/TR/xml/#NT-NameStartChar)?
    fn is_name_start_char(self) -> bool;
    /// Is this a [NameChar](http://www.w3.org/TR/xml/#NT-NameChar)?
    fn is_name_char(self) -> bool;
    /// Does this start a [NCName](http://www.w3.org/TR/REC-xml-names/#NT-NCName)?
    fn is_ncname_start_char(self) -> bool;
    /// Is this a component of a [NCName](http://www.w3.org/TR/REC-xml-names/#NT-NCName)?
    fn is_ncname_char(self) -> bool;
    /// Is this an [XML space](http://www.w3.org/TR/xml/#NT-S)?
    fn is_space_char(self) -> bool;
    fn is_decimal_char(self) -> bool;
    fn is_hex_char(self) -> bool;
}

impl XmlChar for char {
    fn is_name_start_char(self) -> bool {
        self == ':' || self.is_ncname_start_char()
    }

    fn is_name_char(self) -> bool {
        self.is_name_start_char() || self.is_ncname_char()
    }

    fn is_ncname_start_char(self) -> bool {
        match self {
            'A'...'Z'                   |
            '_'                         |
            'a'...'z'                   |
            '\u{0000C0}'...'\u{0000D6}' |
            '\u{0000D8}'...'\u{0000F6}' |
            '\u{0000F8}'...'\u{0002FF}' |
            '\u{000370}'...'\u{00037D}' |
            '\u{00037F}'...'\u{001FFF}' |
            '\u{00200C}'...'\u{00200D}' |
            '\u{002070}'...'\u{00218F}' |
            '\u{002C00}'...'\u{002FEF}' |
            '\u{003001}'...'\u{00D7FF}' |
            '\u{00F900}'...'\u{00FDCF}' |
            '\u{00FDF0}'...'\u{00FFFD}' |
            '\u{010000}'...'\u{0EFFFF}' => true,
            _ => false,
        }
    }

    fn is_ncname_char(self) -> bool {
        if self.is_ncname_start_char() { return true; }
        match self {
            '-'                     |
            '.'                     |
            '0'...'9'               |
            '\u{00B7}'              |
            '\u{0300}'...'\u{036F}' |
            '\u{203F}'...'\u{2040}' => true,
            _ => false
        }
    }

    fn is_space_char(self) -> bool {
        match self {
            '\x20' |
            '\x09' |
            '\x0D' |
            '\x0A' => true,
            _ => false,
        }
    }

    fn is_decimal_char(self) -> bool {
        match self {
            '0'...'9' => true,
            _ => false,
        }
    }

    fn is_hex_char(self) -> bool {
        match self {
            '0'...'9' |
            'a'...'f' |
            'A'...'F' => true,
            _ => false,
        }
    }
}

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

    #[test]
    fn end_of_char_data_leading_ampersand() {
        assert_eq!("&".end_of_char_data(), None);
    }

    #[test]
    fn end_of_char_data_leading_less_than() {
        assert_eq!("<".end_of_char_data(), None);
    }

    #[test]
    fn end_of_char_data_leading_cdata_end() {
        assert_eq!("]]>".end_of_char_data(), None);
    }

    #[test]
    fn end_of_char_data_until_ampersand() {
        assert_eq!("hello&world".end_of_char_data(), Some("hello".len()));
    }

    #[test]
    fn end_of_char_data_until_less_than() {
        assert_eq!("hello<world".end_of_char_data(), Some("hello".len()));
    }

    #[test]
    fn end_of_char_data_until_cdata_end() {
        assert_eq!("hello]]>world".end_of_char_data(), Some("hello".len()));
    }

    #[test]
    fn end_of_char_data_includes_right_square() {
        assert_eq!("hello]world".end_of_char_data(), Some("hello]world".len()));
    }

    #[test]
    fn end_of_char_data_includes_multiple_right_squares() {
        assert_eq!("hello]]world".end_of_char_data(), Some("hello]]world".len()));
    }
}
<|endoftext|>