<commit_before>use std::f64::consts;
use std::option::Option;
use rand::Rng;
use distribution::{Distribution, Univariate, Continuous};
use error::StatsError;
use result;

pub struct Triangular {
    min: f64,
    max: f64,
    mode: f64,
}

impl Triangular {
    pub fn new(min: f64, max: f64, mode: f64) -> result::Result<Triangular> {
        if min.is_infinite() || max.is_infinite() || mode.is_infinite() {
            return Err(StatsError::BadParams);
        }
        if min.is_nan() || max.is_nan() || mode.is_nan() {
            return Err(StatsError::BadParams);
        }
        if max < mode || mode < min {
            return Err(StatsError::BadParams);
        }
        if max == min {
            return Err(StatsError::BadParams);
        }
        Ok(Triangular {
            min: min,
            max: max,
            mode: mode,
        })
    }

    pub fn min(&self) -> f64 {
        self.min
    }

    pub fn max(&self) -> f64 {
        self.max
    }

    pub fn mode(&self) -> f64 {
        self.mode
    }
}

impl Distribution for Triangular {
    fn sample<R: Rng>(&self, r: &mut R) -> f64 {
        sample_unchecked(r, self.min, self.max, self.mode)
    }
}

impl Univariate for Triangular {
    fn mean(&self) -> f64 {
        (self.min + self.max + self.mode) / 3.0
    }

    fn variance(&self) -> f64 {
        let a = self.min;
        let b = self.max;
        let c = self.mode;
        (a * a + b * b + c * c - a * b - a * c - b * c) / 18.0
    }

    fn std_dev(&self) -> f64 {
        self.variance().sqrt()
    }

    fn entropy(&self) -> f64 {
        0.5 + ((self.max - self.min) / 2.0).ln()
    }

    fn skewness(&self) -> f64 {
        let a = self.min;
        let b = self.max;
        let c = self.mode;
        let q = consts::SQRT_2 * (a + b - 2.0 * c) * (2.0 * a - b - c) * (a - 2.0 * b + c);
        let d = 5.0 * (a * a + b * b + c * c - a * b - a * c - b * c).powf(3.0 / 2.0);
        q / d
    }

    fn median(&self) -> Option<f64> {
        let a = self.min;
        let b = self.max;
        let c = self.mode;
        if c >= (a + b) / 2.0 {
            Some(a + ((b - a) * (c - a) / 2.0).sqrt())
        } else {
            Some(b - ((b - a) * (b - c) / 2.0).sqrt())
        }
    }

    fn cdf(&self, x: f64) -> result::Result<f64> {
        let a = self.min;
        let b = self.max;
        let c = self.mode;
        if x < a {
            return Ok(0.0);
        }
        if a <= x && x <= c {
            return Ok((x - a) * (x - a) / ((b - a) * (c - a)));
        }
        if c < x && x <= b {
            return Ok(1.0 - (b - x) * (b - x) / ((b - a) * (b - c)));
        }
        Ok(1.0)
    }
}

impl Continuous for Triangular {
    fn mode(&self) -> f64 {
        self.mode
    }

    fn min(&self) -> f64 {
        self.min
    }

    fn max(&self) -> f64 {
        self.max
    }

    fn pdf(&self, x: f64) -> f64 {
        let a = self.min;
        let b = self.max;
        let c = self.mode;
        if a <= x && x <= c {
            return 2.0 * (x - a) / ((b - a) * (c - a));
        }
        if c < x && x <= b {
            return 2.0 * (b - x) / ((b - a) * (b - c));
        }
        0.0
    }

    fn ln_pdf(&self, x: f64) -> f64 {
        self.pdf(x).ln()
    }
}

fn sample_unchecked<R: Rng>(r: &mut R, min: f64, max: f64, mode: f64) -> f64 {
    let f = r.next_f64();
    if f < (mode - min) / (max - min) {
        min + (f * (max - min) * (mode - min)).sqrt()
    } else {
        max - ((1.0 - f) * (max - min) * (max - mode)).sqrt()
    }
}

#[cfg(test)]
mod test {
    use std::f64;
    use distribution::{Univariate, Continuous};
    use prec;
    use super::Triangular;

    fn try_create(min: f64, max: f64, mode: f64) -> Triangular {
        let r = Triangular::new(min, max, mode);
        assert!(r.is_ok());
        r.unwrap()
    }

    fn create_case(min: f64, max: f64, mode: f64) {
        let n = try_create(min, max, mode);
        assert_eq!(n.min(), min);
        assert_eq!(n.max(), max);
        assert_eq!(n.mode(), mode);
    }

    fn bad_create_case(min: f64, max: f64, mode: f64) {
        let n = Triangular::new(min, max, mode);
        assert!(n.is_err());
    }

    fn test_case<F>(min: f64, max: f64, mode: f64, expected: f64, eval: F)
        where F : Fn(Triangular) -> f64 {

        let n = try_create(min, max, mode);
        let x = eval(n);
        assert_eq!(expected, x);
    }

    fn test_almost<F>(min: f64, max: f64, mode: f64, expected: f64, prec: f64, eval: F)
        where F : Fn(Triangular) -> f64 {

        let n = try_create(min, max, mode);
        let x = eval(n);
        assert!(prec::almost_eq(expected, x, prec));
    }

    fn test_optional_almost<F>(min: f64, max: f64, mode: f64, expected: f64, prec: f64, eval: F)
        where F : Fn(Triangular) -> Option<f64> {

        let n = try_create(min, max, mode);
        let x = eval(n);
        assert!(x.is_some());

        let v = x.unwrap();
        assert!(prec::almost_eq(expected, v, prec));
    }

    #[test]
    fn test_create() {
        create_case(-1.0, 1.0, 0.0);
        create_case(1.0, 2.0, 1.0);
        create_case(5.0, 25.0, 25.0);
        create_case(1.0e-5, 1.0e5, 1.0e-3);
        create_case(0.0, 1.0, 0.9);
        create_case(-4.0, -0.5, -2.0);
        create_case(-13.039, 8.42, 1.17);
    }

    #[test]
    fn test_bad_create() {
        bad_create_case(0.0, 0.0, 0.0);
        bad_create_case(0.0, 1.0, -0.1);
        bad_create_case(0.0, 1.0, 1.1);
        bad_create_case(0.0, -1.0, 0.5);
        bad_create_case(2.0, 1.0, 1.5);
        bad_create_case(f64::NAN, 1.0, 0.5);
        bad_create_case(0.2, f64::NAN, 0.5);
        bad_create_case(0.5, 1.0, f64::NAN);
        bad_create_case(f64::NAN, f64::NAN, f64::NAN);
        bad_create_case(f64::NEG_INFINITY, 1.0, 0.5);
        bad_create_case(0.0, f64::INFINITY, 0.5);
    }

    #[test]
    fn test_entropy() {
        test_almost(0.0, 1.0, 0.5, -0.1931471805599453094172, 1e-16, |x| x.entropy());
        test_almost(0.0, 1.0, 0.75, -0.1931471805599453094172, 1e-16, |x| x.entropy());
        test_almost(-5.0, 8.0, -3.5, 2.371802176901591426636, 1e-16, |x| x.entropy());
        test_almost(-5.0, 8.0, 5.0, 2.371802176901591426636, 1e-16, |x| x.entropy());
        test_almost(-5.0, -3.0, -4.0, 0.5, 1e-16, |x| x.entropy());
        test_almost(15.0, 134.0, 21.0, 4.585976312551584075938, 1e-16, |x| x.entropy());
    }

    #[test]
    fn test_skewness() {
        test_case(0.0, 1.0, 0.5, 0.0, |x| x.skewness());
        test_case(0.0, 1.0, 0.75, -0.4224039833745502226059, |x| x.skewness());
        test_case(-5.0, 8.0, -3.5, 0.5375093589712976359809, |x| x.skewness());
        test_case(-5.0, 8.0, 5.0, -0.4445991743012595633537, |x| x.skewness());
        test_case(-5.0, -3.0, -4.0, 0.0, |x| x.skewness());
        test_case(15.0, 134.0, 21.0, 0.5605920922751860613217, |x| x.skewness());
    }

    #[test]
    fn test_mode() {
        test_case(0.0, 1.0, 0.5, 0.5, |x| x.mode());
        test_case(0.0, 1.0, 0.75, 0.75, |x| x.mode());
        test_case(-5.0, 8.0, -3.5, -3.5, |x| x.mode());
        test_case(-5.0, 8.0, 5.0, 5.0, |x| x.mode());
        test_case(-5.0, -3.0, -4.0, -4.0, |x| x.mode());
        test_case(15.0, 134.0, 21.0, 21.0, |x| x.mode());
    }

    #[test]
    fn test_median() {
        test_optional_almost(0.0, 1.0, 0.5, 0.5, 1e-14, |x| x.median());
        test_optional_almost(0.0, 1.0, 0.75, 0.6123724356957945245493, 1e-14, |x| x.median());
        test_optional_almost(-5.0, 8.0, -3.5, -0.6458082328952913226724, 1e-14, |x| x.median());
        test_optional_almost(-5.0, 8.0, 5.0, 3.062257748298549652367, 1e-14, |x| x.median());
        test_optional_almost(-5.0, -3.0, -4.0, -4.0, 1e-14, |x| x.median());
        test_optional_almost(15.0, 134.0, 21.0, 52.00304883716712238797, 1e-14, |x| x.median());
    }
}
<commit_msg>implement variance and std dev tests for triangular distribution<commit_after>use std::f64::consts;
use std::option::Option;
use rand::Rng;
use distribution::{Distribution, Univariate, Continuous};
use error::StatsError;
use result;

pub struct Triangular {
    min: f64,
    max: f64,
    mode: f64,
}

impl Triangular {
    pub fn new(min: f64, max: f64, mode: f64) -> result::Result<Triangular> {
        if min.is_infinite() || max.is_infinite() || mode.is_infinite() {
            return Err(StatsError::BadParams);
        }
        if min.is_nan() || max.is_nan() || mode.is_nan() {
            return Err(StatsError::BadParams);
        }
        if max < mode || mode < min {
            return Err(StatsError::BadParams);
        }
        if max == min {
            return Err(StatsError::BadParams);
        }
        Ok(Triangular {
            min: min,
            max: max,
            mode: mode,
        })
    }

    pub fn min(&self) -> f64 {
        self.min
    }

    pub fn max(&self) -> f64 {
        self.max
    }

    pub fn mode(&self) -> f64 {
        self.mode
    }
}

impl Distribution for Triangular {
    fn sample<R: Rng>(&self, r: &mut R) -> f64 {
        sample_unchecked(r, self.min, self.max, self.mode)
    }
}

impl Univariate for Triangular {
    fn mean(&self) -> f64 {
        (self.min + self.max + self.mode) / 3.0
    }

    fn variance(&self) -> f64 {
        let a = self.min;
        let b = self.max;
        let c = self.mode;
        (a * a + b * b + c * c - a * b - a * c - b * c) / 18.0
    }

    fn std_dev(&self) -> f64 {
        self.variance().sqrt()
    }

    fn entropy(&self) -> f64 {
        0.5 + ((self.max - self.min) / 2.0).ln()
    }

    fn skewness(&self) -> f64 {
        let a = self.min;
        let b = self.max;
        let c = self.mode;
        let q = consts::SQRT_2 * (a + b - 2.0 * c) * (2.0 * a - b - c) * (a - 2.0 * b + c);
        let d = 5.0 * (a * a + b * b + c * c - a * b - a * c - b * c).powf(3.0 / 2.0);
        q / d
    }

    fn median(&self) -> Option<f64> {
        let a = self.min;
        let b = self.max;
        let c = self.mode;
        if c >= (a + b) / 2.0 {
            Some(a + ((b - a) * (c - a) / 2.0).sqrt())
        } else {
            Some(b - ((b - a) * (b - c) / 2.0).sqrt())
        }
    }

    fn cdf(&self, x: f64) -> result::Result<f64> {
        let a = self.min;
        let b = self.max;
        let c = self.mode;
        if x < a {
            return Ok(0.0);
        }
        if a <= x && x <= c {
            return Ok((x - a) * (x - a) / ((b - a) * (c - a)));
        }
        if c < x && x <= b {
            return Ok(1.0 - (b - x) * (b - x) / ((b - a) * (b - c)));
        }
        Ok(1.0)
    }
}

impl Continuous for Triangular {
    fn mode(&self) -> f64 {
        self.mode
    }

    fn min(&self) -> f64 {
        self.min
    }

    fn max(&self) -> f64 {
        self.max
    }

    fn pdf(&self, x: f64) -> f64 {
        let a = self.min;
        let b = self.max;
        let c = self.mode;
        if a <= x && x <= c {
            return 2.0 * (x - a) / ((b - a) * (c - a));
        }
        if c < x && x <= b {
            return 2.0 * (b - x) / ((b - a) * (b - c));
        }
        0.0
    }

    fn ln_pdf(&self, x: f64) -> f64 {
        self.pdf(x).ln()
    }
}

fn sample_unchecked<R: Rng>(r: &mut R, min: f64, max: f64, mode: f64) -> f64 {
    let f = r.next_f64();
    if f < (mode - min) / (max - min) {
        min + (f * (max - min) * (mode - min)).sqrt()
    } else {
        max - ((1.0 - f) * (max - min) * (max - mode)).sqrt()
    }
}

#[cfg(test)]
mod test {
    use std::f64;
    use distribution::{Univariate, Continuous};
    use prec;
    use super::Triangular;

    fn try_create(min: f64, max: f64, mode: f64) -> Triangular {
        let r = Triangular::new(min, max, mode);
        assert!(r.is_ok());
        r.unwrap()
    }

    fn create_case(min: f64, max: f64, mode: f64) {
        let n = try_create(min, max, mode);
        assert_eq!(n.min(), min);
        assert_eq!(n.max(), max);
        assert_eq!(n.mode(), mode);
    }

    fn bad_create_case(min: f64, max: f64, mode: f64) {
        let n = Triangular::new(min, max, mode);
        assert!(n.is_err());
    }

    fn test_case<F>(min: f64, max: f64, mode: f64, expected: f64, eval: F)
        where F : Fn(Triangular) -> f64 {

        let n = try_create(min, max, mode);
        let x = eval(n);
        assert_eq!(expected, x);
    }

    fn test_almost<F>(min: f64, max: f64, mode: f64, expected: f64, prec: f64, eval: F)
        where F : Fn(Triangular) -> f64 {

        let n = try_create(min, max, mode);
        let x = eval(n);
        assert!(prec::almost_eq(expected, x, prec));
    }

    fn test_optional_almost<F>(min: f64, max: f64, mode: f64, expected: f64, prec: f64, eval: F)
        where F : Fn(Triangular) -> Option<f64> {

        let n = try_create(min, max, mode);
        let x = eval(n);
        assert!(x.is_some());

        let v = x.unwrap();
        assert!(prec::almost_eq(expected, v, prec));
    }

    #[test]
    fn test_create() {
        create_case(-1.0, 1.0, 0.0);
        create_case(1.0, 2.0, 1.0);
        create_case(5.0, 25.0, 25.0);
        create_case(1.0e-5, 1.0e5, 1.0e-3);
        create_case(0.0, 1.0, 0.9);
        create_case(-4.0, -0.5, -2.0);
        create_case(-13.039, 8.42, 1.17);
    }

    #[test]
    fn test_bad_create() {
        bad_create_case(0.0, 0.0, 0.0);
        bad_create_case(0.0, 1.0, -0.1);
        bad_create_case(0.0, 1.0, 1.1);
        bad_create_case(0.0, -1.0, 0.5);
        bad_create_case(2.0, 1.0, 1.5);
        bad_create_case(f64::NAN, 1.0, 0.5);
        bad_create_case(0.2, f64::NAN, 0.5);
        bad_create_case(0.5, 1.0, f64::NAN);
        bad_create_case(f64::NAN, f64::NAN, f64::NAN);
        bad_create_case(f64::NEG_INFINITY, 1.0, 0.5);
        bad_create_case(0.0, f64::INFINITY, 0.5);
    }

    #[test]
    fn test_variance() {
        test_case(0.0, 1.0, 0.5, 0.75/18.0, |x| x.variance());
        test_case(0.0, 1.0, 0.75, 0.8125/18.0, |x| x.variance());
        test_case(-5.0, 8.0, -3.5, 151.75/18.0, |x| x.variance());
        test_case(-5.0, 8.0, 5.0, 139.0/18.0, |x| x.variance());
        test_case(-5.0, -3.0, -4.0, 3.0/18.0, |x| x.variance());
        test_case(15.0, 134.0, 21.0, 13483.0/18.0, |x| x.variance());
    }

    #[test]
    fn test_std_dev() {
        test_case(0.0, 1.0, 0.5, (0.75f64/18.0).sqrt(), |x| x.std_dev());
        test_case(0.0, 1.0, 0.75, (0.8125f64/18.0).sqrt(), |x| x.std_dev());
        test_case(-5.0, 8.0, -3.5, (151.75f64/18.0).sqrt(), |x| x.std_dev());
        test_case(-5.0, 8.0, 5.0, (139.0f64/18.0).sqrt(), |x| x.std_dev());
        test_case(-5.0, -3.0, -4.0, (3.0f64/18.0).sqrt(), |x| x.std_dev());
        test_case(15.0, 134.0, 21.0, (13483.0f64/18.0).sqrt(), |x| x.std_dev());
    }

    #[test]
    fn test_entropy() {
        test_almost(0.0, 1.0, 0.5, -0.1931471805599453094172, 1e-16, |x| x.entropy());
        test_almost(0.0, 1.0, 0.75, -0.1931471805599453094172, 1e-16, |x| x.entropy());
        test_almost(-5.0, 8.0, -3.5, 2.371802176901591426636, 1e-16, |x| x.entropy());
        test_almost(-5.0, 8.0, 5.0, 2.371802176901591426636, 1e-16, |x| x.entropy());
        test_almost(-5.0, -3.0, -4.0, 0.5, 1e-16, |x| x.entropy());
        test_almost(15.0, 134.0, 21.0, 4.585976312551584075938, 1e-16, |x| x.entropy());
    }

    #[test]
    fn test_skewness() {
        test_case(0.0, 1.0, 0.5, 0.0, |x| x.skewness());
        test_case(0.0, 1.0, 0.75, -0.4224039833745502226059, |x| x.skewness());
        test_case(-5.0, 8.0, -3.5, 0.5375093589712976359809, |x| x.skewness());
        test_case(-5.0, 8.0, 5.0, -0.4445991743012595633537, |x| x.skewness());
        test_case(-5.0, -3.0, -4.0, 0.0, |x| x.skewness());
        test_case(15.0, 134.0, 21.0, 0.5605920922751860613217, |x| x.skewness());
    }

    #[test]
    fn test_mode() {
        test_case(0.0, 1.0, 0.5, 0.5, |x| x.mode());
        test_case(0.0, 1.0, 0.75, 0.75, |x| x.mode());
        test_case(-5.0, 8.0, -3.5, -3.5, |x| x.mode());
        test_case(-5.0, 8.0, 5.0, 5.0, |x| x.mode());
        test_case(-5.0, -3.0, -4.0, -4.0, |x| x.mode());
        test_case(15.0, 134.0, 21.0, 21.0, |x| x.mode());
    }

    #[test]
    fn test_median() {
        test_optional_almost(0.0, 1.0, 0.5, 0.5, 1e-14, |x| x.median());
        test_optional_almost(0.0, 1.0, 0.75, 0.6123724356957945245493, 1e-14, |x| x.median());
        test_optional_almost(-5.0, 8.0, -3.5, -0.6458082328952913226724, 1e-14, |x| x.median());
        test_optional_almost(-5.0, 8.0, 5.0, 3.062257748298549652367, 1e-14, |x| x.median());
        test_optional_almost(-5.0, -3.0, -4.0, -4.0, 1e-14, |x| x.median());
        test_optional_almost(15.0, 134.0, 21.0, 52.00304883716712238797, 1e-14, |x| x.median());
    }
}
<|endoftext|>


<commit_before>extern crate cargo_tarpaulin;

use cargo_tarpaulin::launch_tarpaulin;
use cargo_tarpaulin::config::Config;
use std::env;

#[test]
fn incorrect_manifest_path() {
    let mut config = Config::default();
    config.manifest.push("__invalid_dir__");
    assert!(launch_tarpaulin(&config).is_err());
}

#[test]
fn simple_project_coverage() {
    let mut config = Config::default();
    config.manifest = env::current_dir().unwrap();
    config.manifest.push("tests");
    config.manifest.push("data");
    config.manifest.push("simple_project");
    config.manifest.push("Cargo.toml");
    let res = launch_tarpaulin(&config).unwrap();

    let unused_hits = res.iter()
                         .filter(|x| x.path.file_name().unwrap() == "unused.rs")
                         .fold(0, |acc, ref x| acc + x.hits);
    assert_eq!(unused_hits, 0);
}
<commit_msg>Added additional checks<commit_after>extern crate cargo_tarpaulin;

use cargo_tarpaulin::launch_tarpaulin;
use cargo_tarpaulin::config::Config;
use std::env;

#[test]
fn incorrect_manifest_path() {
    let mut config = Config::default();
    config.manifest.push("__invalid_dir__");
    assert!(launch_tarpaulin(&config).is_err());
}

#[test]
fn simple_project_coverage() {
    let mut config = Config::default();
    config.manifest = env::current_dir().unwrap();
    config.manifest.push("tests");
    config.manifest.push("data");
    config.manifest.push("simple_project");
    config.manifest.push("Cargo.toml");
    let res = launch_tarpaulin(&config).unwrap();

    let unused_hits = res.iter()
                         .filter(|x| x.path.file_name().unwrap() == "unused.rs")
                         .fold(0, |acc, ref x| acc + x.hits);
    assert_eq!(unused_hits, 0);

    let unused_hits = res.iter()
                         .filter(|x| x.path.file_name().unwrap() == "unused.rs")
                         .map(|x| x.line)
                         .collect::<Vec<_>>();

    assert_eq!(unused_hits.len(), 3);
    assert!(unused_hits.contains(&4));
    assert!(unused_hits.contains(&5));
    assert!(unused_hits.contains(&6));

    assert!(res.iter().any(|ref x| x.line == 6 &&
                                   x.hits == 0 &&
                                   x.path.file_name().unwrap() == "lib.rs"));

    assert!(res.iter().any(|ref x| x.line == 8 &&
                                   x.hits == 1 &&
                                   x.path.file_name().unwrap() == "lib.rs"));
}
<|endoftext|>


<commit_before>//! This is a simle crate module

use std::io::prelude::*;
use std::io::BufReader;
use std::io::Error;
use std::fs;
use std::path::PathBuf;
use std::process::Command;
use std::collections;
use std::env;

use toml;
use rustc_serialize::json::Json;
use rustc_serialize::json::ParserError;

use super::{DocBuilder, DocBuilderError, copy_files, command_result};

/// Really simple crate model
#[derive(Debug)]
pub struct Crate {
    /// Name of crate
    pub name: String,
    /// Versions of crate
    pub versions: Vec<String>,
}

#[derive(Debug)]
pub enum CrateOpenError {
    FileNotFound,
    ParseError(ParserError),
    IoError(Error),
    NotObject,
    NameNotFound,
    VersNotFound,
}

impl Crate {
    /// Returns a new Crate
    ///
    /// # Examples
    ///
    /// ```
    /// let crte = Crate::new("cratesfyi".to_string(),
    ///                       vec!["0.1.0".to_string()]);
    /// assert_eq!(crte.name, "cratesfyi");
    /// ```
    pub fn new(name: String, versions: Vec<String>) -> Crate {
        Crate {
            name: name,
            versions: versions,
        }
    }

    pub fn from_cargo_index_file(path: PathBuf) -> Result<Crate, CrateOpenError> {

        let mut file = match fs::File::open(path) {
            Ok(f) => BufReader::new(f),
            Err(_) => return Err(CrateOpenError::FileNotFound),
        };

        let mut line = String::new();

        let mut name = String::new();
        let mut versions = Vec::new();

        while try!(file.read_line(&mut line).map_err(CrateOpenError::IoError)) > 0 {

            let (cname, vers) = match Crate::parse_cargo_index_line(&line) {
                Ok((c, v)) => (c, v),
                Err(e) => return Err(e)
            };

            name = cname;
            versions.push(vers);

            line.clear();
        }

        versions.reverse();

        Ok(Crate {
            name: name,
            versions: versions
        })
    }

    /// Create Crate from crate name and try to find it in crates.io-index
    /// to load versions
    pub fn from_cargo_index_path(name: &str, path: &PathBuf) -> Result<Crate, CrateOpenError> {

        if !path.is_dir() {
            return Err(CrateOpenError::FileNotFound);
        }

        for file in try!(path.read_dir().map_err(CrateOpenError::IoError)) {

            let file = try!(file.map_err(CrateOpenError::IoError));

            let path = file.path();

            // skip files under .git and config.json
            if path.to_str().unwrap().contains(".git") ||
                path.file_name().unwrap() == "config.json" {
                    continue;
                }

            if path.is_dir() {
                if let Ok(c) = Crate::from_cargo_index_path(&name, &path) {
                    return Ok(c);
                }
            } else if file.file_name().into_string().unwrap() == name {
                return Crate::from_cargo_index_file(path);
            }

        }

        Err(CrateOpenError::FileNotFound)
    }

    fn parse_cargo_index_line(line: &String) -> Result<(String, String), CrateOpenError> {
        let data = try!(Json::from_str(line.trim()).map_err(CrateOpenError::ParseError));
        let obj = try!(data.as_object().ok_or(CrateOpenError::NotObject));

        let crate_name = try!(obj.get("name")
                              .and_then(|n| n.as_string())
                              .ok_or(CrateOpenError::NameNotFound));

        let vers = try!(obj.get("vers")
                        .and_then(|n| n.as_string())
                        .ok_or(CrateOpenError::VersNotFound));

        Ok((String::from(crate_name), String::from(vers)))
    }

    pub fn get_version_index(&self, requested_version: &str) -> Option<usize> {
        for i in 0..self.versions.len() {
            if self.versions[i] == requested_version {
                return Some(i);
            }
        }
        None
    }

    pub fn version_starts_with(&self, version: &str) -> Option<usize> {
        // if version is "*" return latest version index which is 0
        if version == "*" {
            return Some(0);
        }
        for i in 0..self.versions.len() {
            if self.versions[i].starts_with(&version) {
                return Some(i)
            }
        }
        None
    }

    pub fn canonical_name(&self, version_index: usize) -> String {
        format!("{}-{}", self.name, self.versions[version_index])
    }

    pub fn extract_crate(&self, version_index: usize) -> Result<String, String> {
        let crate_name = format!("{}.crate", self.canonical_name(version_index));
        command_result(Command::new("tar")
                       .arg("-xzvf")
                       .arg(crate_name)
                       .output()
                       .unwrap())
    }

    /// Downloads crate
    pub fn download_crate(&self, version_index: usize) -> Result<String, String> {
        // By default crates.io is using:
        // https://crates.io/api/v1/crates/$crate/$version/download
        // But I believe this url is increasing download count and this program is
        // downloading alot during development. I am using redirected url.
        let url = format!("https://crates-io.s3-us-west-1.amazonaws.com/crates/{}/{}-{}.crate",
                          self.name,
                          self.name,
                          self.versions[version_index]);
        // Use wget for now
        command_result(Command::new("wget")
                       .arg("-c")
                       .arg("--content-disposition")
                       .arg(url)
                       .output()
                       .unwrap())
    }

    /// Download local dependencies from crate root and place them into right place
    ///
    /// Some packages have local dependencies defined in Cargo.toml
    ///
    /// This function is intentionall written verbose
    fn download_dependencies(&self, root_dir: &PathBuf, docbuilder: &DocBuilder) -> Result<(), DocBuilderError> {

        let mut cargo_toml_path = PathBuf::from(&root_dir);
        cargo_toml_path.push("Cargo.toml");

        let mut cargo_toml_fh = try!(fs::File::open(cargo_toml_path)
                                     .map_err(DocBuilderError::LocalDependencyIoError));
        let mut cargo_toml_content = String::new();
        try!(cargo_toml_fh.read_to_string(&mut cargo_toml_content)
             .map_err(DocBuilderError::LocalDependencyIoError));

        toml::Parser::new(&cargo_toml_content[..]).parse().as_ref()
            .and_then(|cargo_toml| cargo_toml.get("dependencies"))
            .and_then(|dependencies| dependencies.as_table())
            .and_then(|dependencies_table| self.get_local_dependencies(dependencies_table, docbuilder))
            .map(|local_dependencies| self.handle_local_dependencies(local_dependencies, &root_dir))
            .unwrap_or(Ok(()))
    }

    /// Get's local_dependencies from dependencies_table
    fn get_local_dependencies(&self,
                              dependencies_table: &collections::BTreeMap<String, toml::Value>,
                              docbuilder: &DocBuilder) -> Option<Vec<(Crate, usize, String)>>  {

        let mut local_dependencies: Vec<(Crate, usize, String)> = Vec::new();

        for key in dependencies_table.keys() {

            dependencies_table.get(key)
                .and_then(|key_val| key_val.as_table())
                .map(|key_table| {
                    key_table.get("path").and_then(|p| p.as_str()).map(|path| {
                        key_table.get("version").and_then(|p| p.as_str())
                            .map(|version| {
                                // TODO: This kinda became a mess
                                //       I wonder if can use more and_then's...
                                if let Ok(dep_crate) = Crate::from_cargo_index_path(&key,
                                                            &docbuilder.crates_io_index_path) {
                                    if let Some(version_index) =
                                        dep_crate.version_starts_with(version) {
                                        local_dependencies.push((dep_crate,
                                                                 version_index,
                                                                 path.to_string()));
                                    }
                                }
                            });
                    });
                });

        }
        Some(local_dependencies)
    }

    /// Handles local dependencies
    fn handle_local_dependencies(&self,
                                 local_dependencies: Vec<(Crate, usize, String)>,
                                 root_dir: &PathBuf) -> Result<(), DocBuilderError> {
        for local_dependency in local_dependencies {
            let crte = local_dependency.0;
            let version_index = local_dependency.1;

            let mut path = PathBuf::from(&root_dir);
            path.push(local_dependency.2);

            // make sure path exists
            if !path.exists() {
                try!(fs::create_dir_all(&path).map_err(DocBuilderError::LocalDependencyIoError));
            }

            try!(crte.download_crate(version_index)
                 .map_err(DocBuilderError::LocalDependencyDownloadError));
            try!(crte.extract_crate(version_index)
                 .map_err(DocBuilderError::LocalDependencyExtractCrateError));

            let crte_download_dir = PathBuf::from(format!("{}-{}",
                                                          crte.name,
                                                          crte.versions[version_index]));

            if !crte_download_dir.exists() {
                return Err(DocBuilderError::LocalDependencyDownloadDirNotExist);
            }

            // self.extract_crate will extract crate into build_dir
            // Copy files to proper location
            try!(copy_files(&crte_download_dir, &path, false));

            // Remove download_dir
            try!(fs::remove_dir_all(&crte_download_dir)
                 .map_err(DocBuilderError::LocalDependencyIoError));

            try!(crte.remove_crate_file(version_index));
        }

        Ok(())
    }

    fn remove_build_dir_for_crate(&self,
                                  version_index: usize) -> Result<(), DocBuilderError> {
        let path = PathBuf::from(self.canonical_name(version_index));

        if path.exists() && path.is_dir() {
            try!(fs::remove_dir_all(&path).map_err(DocBuilderError::RemoveBuildDir));
        }

        Ok(())
    }

    pub fn build_crate_doc(&self,
                           version_index: usize,
                           docbuilder: &DocBuilder) -> Result<(), DocBuilderError> {

        let package_root = PathBuf::from(self.canonical_name(version_index));

        info!("Building documentation for {}-{}", self.name, self.versions[version_index]);

        // removing old build directory
        try!(self.remove_build_dir_for_crate(version_index));

        // Download crate
        // FIXME: Need to capture failed command outputs
        info!("Downloading crate\n{}",
              try!(self.download_crate(version_index)
                   .map_err(DocBuilderError::DownloadCrateError)));

        // Extract crate
        info!("Extracting crate\n{}",
              try!(self.extract_crate(version_index)
                   .map_err(DocBuilderError::ExtractCrateError)));

        info!("Checking local dependencies");
        try!(self.download_dependencies(&package_root, &docbuilder));

        // build docs
        info!("Building documentation");
        let (status, message) = match self.build_doc(version_index) {
            Ok(m) => (true, m),
            Err(m) => (false, m),
        };
        info!("{}", message);

        if status {
            Ok(())
        } else {
            Err(DocBuilderError::FailedToBuildCrate)
        }
    }

    fn build_doc(&self, version_index: usize) -> Result<String, String> {
        let cwd = env::current_dir().unwrap();
        let mut target = PathBuf::from(&cwd);
        target.push(self.canonical_name(version_index));
        env::set_current_dir(target).unwrap();
        let res = command_result(Command::new("cargo")
                                 .arg("doc")
                                 .arg("--verbose")
                                 .output()
                                 .unwrap());
        env::set_current_dir(cwd).unwrap();
        res
    }

    pub fn remove_crate_file(&self,
                             version_index: usize) -> Result<(), DocBuilderError>{
        let path = PathBuf::from(format!("{}.crate", self.canonical_name(version_index)));

        if path.exists() && path.is_file() {
            try!(fs::remove_file(path).map_err(DocBuilderError::RemoveCrateFile));
        }

        Ok(())
    }

}

#[cfg(test)]
mod test {
    use super::*;
    use std::env;
    use std::path::PathBuf;
    use std::fs;
    use std::thread::sleep;

    #[test]
    fn test_get_vesion_index() {
        let crte = Crate::new("cratesfyi".to_string(),
                              vec!["0.1.0".to_string(), "0.1.1".to_string()]);
        assert_eq!(crte.get_version_index("0.1.0"), Some(0));
        assert_eq!(crte.get_version_index("0.1.1"), Some(1));
        assert_eq!(crte.get_version_index("0.1.2"), None);
    }

    // Rest of the tests only works if crates.io-index is exists in:
    // ../cratesfyi-prefix/crates.io-index

    #[test]
    #[ignore]
    fn test_from_cargo_index_path() {
        let mut path = PathBuf::from(env::current_dir().unwrap());
        path.push("../cratesfyi-prefix/crates.io-index");

        if !path.exists() {
            return;
        }

        let crte = Crate::from_cargo_index_path("rand", &path).unwrap();
        assert_eq!(crte.name, "rand");
        assert!(crte.versions.len() > 0);
    }

    #[test]
    #[ignore]
    fn test_version_starts_with() {
        let mut path = PathBuf::from(env::current_dir().unwrap());
        path.push("../cratesfyi-prefix/crates.io-index");

        if !path.exists() {
            return;
        }

        let crte = Crate::from_cargo_index_path("rand", &path).unwrap();
        assert!(crte.version_starts_with("0.1").is_some());
        assert!(crte.version_starts_with("*").is_some());
        assert!(crte.version_starts_with("999.099.99").is_none());
    }

    #[test]
    fn test_download_crate() {
        let crte = Crate::new("rand".to_string(),
                              vec!["0.3.13".to_string()]);
        assert!(crte.download_crate(0).is_ok());
        // remove downloaded file
        fs::remove_file(format!("{}.crate", crte.canonical_name(0))).unwrap();
    }

    #[test]
    fn test_extract_crate() {
        let crte = Crate::new("rand".to_string(),
                              vec!["0.3.12".to_string()]);
        assert!(crte.download_crate(0).is_ok());
        assert!(crte.extract_crate(0).is_ok());

        let path = PathBuf::from(crte.canonical_name(0));
        assert!(path.exists());
        fs::remove_dir_all(crte.canonical_name(0)).unwrap();
    }

    #[test]
    fn test_remove_crate_file() {
        let crte = Crate::new("rand".to_string(),
                              vec!["0.3.11".to_string()]);
        assert!(crte.download_crate(0).is_ok());
        assert!(crte.remove_crate_file(0).is_ok());
    }

}
<commit_msg>Use --no-deps in cargo command<commit_after>//! This is a simle crate module

use std::io::prelude::*;
use std::io::BufReader;
use std::io::Error;
use std::fs;
use std::path::PathBuf;
use std::process::Command;
use std::collections;
use std::env;

use toml;
use rustc_serialize::json::Json;
use rustc_serialize::json::ParserError;

use super::{DocBuilder, DocBuilderError, copy_files, command_result};

/// Really simple crate model
#[derive(Debug)]
pub struct Crate {
    /// Name of crate
    pub name: String,
    /// Versions of crate
    pub versions: Vec<String>,
}

#[derive(Debug)]
pub enum CrateOpenError {
    FileNotFound,
    ParseError(ParserError),
    IoError(Error),
    NotObject,
    NameNotFound,
    VersNotFound,
}

impl Crate {
    /// Returns a new Crate
    ///
    /// # Examples
    ///
    /// ```
    /// let crte = Crate::new("cratesfyi".to_string(),
    ///                       vec!["0.1.0".to_string()]);
    /// assert_eq!(crte.name, "cratesfyi");
    /// ```
    pub fn new(name: String, versions: Vec<String>) -> Crate {
        Crate {
            name: name,
            versions: versions,
        }
    }

    pub fn from_cargo_index_file(path: PathBuf) -> Result<Crate, CrateOpenError> {

        let mut file = match fs::File::open(path) {
            Ok(f) => BufReader::new(f),
            Err(_) => return Err(CrateOpenError::FileNotFound),
        };

        let mut line = String::new();

        let mut name = String::new();
        let mut versions = Vec::new();

        while try!(file.read_line(&mut line).map_err(CrateOpenError::IoError)) > 0 {

            let (cname, vers) = match Crate::parse_cargo_index_line(&line) {
                Ok((c, v)) => (c, v),
                Err(e) => return Err(e)
            };

            name = cname;
            versions.push(vers);

            line.clear();
        }

        versions.reverse();

        Ok(Crate {
            name: name,
            versions: versions
        })
    }

    /// Create Crate from crate name and try to find it in crates.io-index
    /// to load versions
    pub fn from_cargo_index_path(name: &str, path: &PathBuf) -> Result<Crate, CrateOpenError> {

        if !path.is_dir() {
            return Err(CrateOpenError::FileNotFound);
        }

        for file in try!(path.read_dir().map_err(CrateOpenError::IoError)) {

            let file = try!(file.map_err(CrateOpenError::IoError));

            let path = file.path();

            // skip files under .git and config.json
            if path.to_str().unwrap().contains(".git") ||
                path.file_name().unwrap() == "config.json" {
                    continue;
                }

            if path.is_dir() {
                if let Ok(c) = Crate::from_cargo_index_path(&name, &path) {
                    return Ok(c);
                }
            } else if file.file_name().into_string().unwrap() == name {
                return Crate::from_cargo_index_file(path);
            }

        }

        Err(CrateOpenError::FileNotFound)
    }

    fn parse_cargo_index_line(line: &String) -> Result<(String, String), CrateOpenError> {
        let data = try!(Json::from_str(line.trim()).map_err(CrateOpenError::ParseError));
        let obj = try!(data.as_object().ok_or(CrateOpenError::NotObject));

        let crate_name = try!(obj.get("name")
                              .and_then(|n| n.as_string())
                              .ok_or(CrateOpenError::NameNotFound));

        let vers = try!(obj.get("vers")
                        .and_then(|n| n.as_string())
                        .ok_or(CrateOpenError::VersNotFound));

        Ok((String::from(crate_name), String::from(vers)))
    }

    pub fn get_version_index(&self, requested_version: &str) -> Option<usize> {
        for i in 0..self.versions.len() {
            if self.versions[i] == requested_version {
                return Some(i);
            }
        }
        None
    }

    pub fn version_starts_with(&self, version: &str) -> Option<usize> {
        // if version is "*" return latest version index which is 0
        if version == "*" {
            return Some(0);
        }
        for i in 0..self.versions.len() {
            if self.versions[i].starts_with(&version) {
                return Some(i)
            }
        }
        None
    }

    pub fn canonical_name(&self, version_index: usize) -> String {
        format!("{}-{}", self.name, self.versions[version_index])
    }

    pub fn extract_crate(&self, version_index: usize) -> Result<String, String> {
        let crate_name = format!("{}.crate", self.canonical_name(version_index));
        command_result(Command::new("tar")
                       .arg("-xzvf")
                       .arg(crate_name)
                       .output()
                       .unwrap())
    }

    /// Downloads crate
    pub fn download_crate(&self, version_index: usize) -> Result<String, String> {
        // By default crates.io is using:
        // https://crates.io/api/v1/crates/$crate/$version/download
        // But I believe this url is increasing download count and this program is
        // downloading alot during development. I am using redirected url.
        let url = format!("https://crates-io.s3-us-west-1.amazonaws.com/crates/{}/{}-{}.crate",
                          self.name,
                          self.name,
                          self.versions[version_index]);
        // Use wget for now
        command_result(Command::new("wget")
                       .arg("-c")
                       .arg("--content-disposition")
                       .arg(url)
                       .output()
                       .unwrap())
    }

    /// Download local dependencies from crate root and place them into right place
    ///
    /// Some packages have local dependencies defined in Cargo.toml
    ///
    /// This function is intentionall written verbose
    fn download_dependencies(&self, root_dir: &PathBuf, docbuilder: &DocBuilder) -> Result<(), DocBuilderError> {

        let mut cargo_toml_path = PathBuf::from(&root_dir);
        cargo_toml_path.push("Cargo.toml");

        let mut cargo_toml_fh = try!(fs::File::open(cargo_toml_path)
                                     .map_err(DocBuilderError::LocalDependencyIoError));
        let mut cargo_toml_content = String::new();
        try!(cargo_toml_fh.read_to_string(&mut cargo_toml_content)
             .map_err(DocBuilderError::LocalDependencyIoError));

        toml::Parser::new(&cargo_toml_content[..]).parse().as_ref()
            .and_then(|cargo_toml| cargo_toml.get("dependencies"))
            .and_then(|dependencies| dependencies.as_table())
            .and_then(|dependencies_table| self.get_local_dependencies(dependencies_table, docbuilder))
            .map(|local_dependencies| self.handle_local_dependencies(local_dependencies, &root_dir))
            .unwrap_or(Ok(()))
    }

    /// Get's local_dependencies from dependencies_table
    fn get_local_dependencies(&self,
                              dependencies_table: &collections::BTreeMap<String, toml::Value>,
                              docbuilder: &DocBuilder) -> Option<Vec<(Crate, usize, String)>>  {

        let mut local_dependencies: Vec<(Crate, usize, String)> = Vec::new();

        for key in dependencies_table.keys() {

            dependencies_table.get(key)
                .and_then(|key_val| key_val.as_table())
                .map(|key_table| {
                    key_table.get("path").and_then(|p| p.as_str()).map(|path| {
                        key_table.get("version").and_then(|p| p.as_str())
                            .map(|version| {
                                // TODO: This kinda became a mess
                                //       I wonder if can use more and_then's...
                                if let Ok(dep_crate) = Crate::from_cargo_index_path(&key,
                                                            &docbuilder.crates_io_index_path) {
                                    if let Some(version_index) =
                                        dep_crate.version_starts_with(version) {
                                        local_dependencies.push((dep_crate,
                                                                 version_index,
                                                                 path.to_string()));
                                    }
                                }
                            });
                    });
                });

        }
        Some(local_dependencies)
    }

    /// Handles local dependencies
    fn handle_local_dependencies(&self,
                                 local_dependencies: Vec<(Crate, usize, String)>,
                                 root_dir: &PathBuf) -> Result<(), DocBuilderError> {
        for local_dependency in local_dependencies {
            let crte = local_dependency.0;
            let version_index = local_dependency.1;

            let mut path = PathBuf::from(&root_dir);
            path.push(local_dependency.2);

            // make sure path exists
            if !path.exists() {
                try!(fs::create_dir_all(&path).map_err(DocBuilderError::LocalDependencyIoError));
            }

            try!(crte.download_crate(version_index)
                 .map_err(DocBuilderError::LocalDependencyDownloadError));
            try!(crte.extract_crate(version_index)
                 .map_err(DocBuilderError::LocalDependencyExtractCrateError));

            let crte_download_dir = PathBuf::from(format!("{}-{}",
                                                          crte.name,
                                                          crte.versions[version_index]));

            if !crte_download_dir.exists() {
                return Err(DocBuilderError::LocalDependencyDownloadDirNotExist);
            }

            // self.extract_crate will extract crate into build_dir
            // Copy files to proper location
            try!(copy_files(&crte_download_dir, &path, false));

            // Remove download_dir
            try!(fs::remove_dir_all(&crte_download_dir)
                 .map_err(DocBuilderError::LocalDependencyIoError));

            try!(crte.remove_crate_file(version_index));
        }

        Ok(())
    }

    fn remove_build_dir_for_crate(&self,
                                  version_index: usize) -> Result<(), DocBuilderError> {
        let path = PathBuf::from(self.canonical_name(version_index));

        if path.exists() && path.is_dir() {
            try!(fs::remove_dir_all(&path).map_err(DocBuilderError::RemoveBuildDir));
        }

        Ok(())
    }

    pub fn build_crate_doc(&self,
                           version_index: usize,
                           docbuilder: &DocBuilder) -> Result<(), DocBuilderError> {

        let package_root = PathBuf::from(self.canonical_name(version_index));

        info!("Building documentation for {}-{}", self.name, self.versions[version_index]);

        // removing old build directory
        try!(self.remove_build_dir_for_crate(version_index));

        // Download crate
        // FIXME: Need to capture failed command outputs
        info!("Downloading crate\n{}",
              try!(self.download_crate(version_index)
                   .map_err(DocBuilderError::DownloadCrateError)));

        // Extract crate
        info!("Extracting crate\n{}",
              try!(self.extract_crate(version_index)
                   .map_err(DocBuilderError::ExtractCrateError)));

        info!("Checking local dependencies");
        try!(self.download_dependencies(&package_root, &docbuilder));

        // build docs
        info!("Building documentation");
        let (status, message) = match self.build_doc(version_index) {
            Ok(m) => (true, m),
            Err(m) => (false, m),
        };
        info!("{}", message);

        if status {
            Ok(())
        } else {
            Err(DocBuilderError::FailedToBuildCrate)
        }
    }

    fn build_doc(&self, version_index: usize) -> Result<String, String> {
        let cwd = env::current_dir().unwrap();
        let mut target = PathBuf::from(&cwd);
        target.push(self.canonical_name(version_index));
        env::set_current_dir(target).unwrap();
        let res = command_result(Command::new("cargo")
                                 .arg("doc")
                                 .arg("--no-deps")
                                 .arg("--verbose")
                                 .output()
                                 .unwrap());
        env::set_current_dir(cwd).unwrap();
        res
    }

    pub fn remove_crate_file(&self,
                             version_index: usize) -> Result<(), DocBuilderError>{
        let path = PathBuf::from(format!("{}.crate", self.canonical_name(version_index)));

        if path.exists() && path.is_file() {
            try!(fs::remove_file(path).map_err(DocBuilderError::RemoveCrateFile));
        }

        Ok(())
    }

}

#[cfg(test)]
mod test {
    use super::*;
    use std::env;
    use std::path::PathBuf;
    use std::fs;
    use std::thread::sleep;

    #[test]
    fn test_get_vesion_index() {
        let crte = Crate::new("cratesfyi".to_string(),
                              vec!["0.1.0".to_string(), "0.1.1".to_string()]);
        assert_eq!(crte.get_version_index("0.1.0"), Some(0));
        assert_eq!(crte.get_version_index("0.1.1"), Some(1));
        assert_eq!(crte.get_version_index("0.1.2"), None);
    }

    // Rest of the tests only works if crates.io-index is exists in:
    // ../cratesfyi-prefix/crates.io-index

    #[test]
    #[ignore]
    fn test_from_cargo_index_path() {
        let mut path = PathBuf::from(env::current_dir().unwrap());
        path.push("../cratesfyi-prefix/crates.io-index");

        if !path.exists() {
            return;
        }

        let crte = Crate::from_cargo_index_path("rand", &path).unwrap();
        assert_eq!(crte.name, "rand");
        assert!(crte.versions.len() > 0);
    }

    #[test]
    #[ignore]
    fn test_version_starts_with() {
        let mut path = PathBuf::from(env::current_dir().unwrap());
        path.push("../cratesfyi-prefix/crates.io-index");

        if !path.exists() {
            return;
        }

        let crte = Crate::from_cargo_index_path("rand", &path).unwrap();
        assert!(crte.version_starts_with("0.1").is_some());
        assert!(crte.version_starts_with("*").is_some());
        assert!(crte.version_starts_with("999.099.99").is_none());
    }

    #[test]
    fn test_download_crate() {
        let crte = Crate::new("rand".to_string(),
                              vec!["0.3.13".to_string()]);
        assert!(crte.download_crate(0).is_ok());
        // remove downloaded file
        fs::remove_file(format!("{}.crate", crte.canonical_name(0))).unwrap();
    }

    #[test]
    fn test_extract_crate() {
        let crte = Crate::new("rand".to_string(),
                              vec!["0.3.12".to_string()]);
        assert!(crte.download_crate(0).is_ok());
        assert!(crte.extract_crate(0).is_ok());

        let path = PathBuf::from(crte.canonical_name(0));
        assert!(path.exists());
        fs::remove_dir_all(crte.canonical_name(0)).unwrap();
    }

    #[test]
    fn test_remove_crate_file() {
        let crte = Crate::new("rand".to_string(),
                              vec!["0.3.11".to_string()]);
        assert!(crte.download_crate(0).is_ok());
        assert!(crte.remove_crate_file(0).is_ok());
    }

}
<|endoftext|>


<commit_before>// Syntax: master_server [ip address (0.0.0.0 by default)]
// This was created for HaloMD.

// If the server does not respond in at least this many seconds, it will be dropped from the list.
const DROP_TIME : i64 = 60;

// Blacklist for blocking IPs. Separate with newlines.
// Blacklisting IPs ignores heartbeat and keepalive packets from an IP address.
// That means that servers that are banned will not be immediately removed, but will time out, instead.
const BLACKLIST_FILE : &'static str = "blacklist.txt";

// Read the blacklist every x amount of seconds.
const BLACKLIST_UPDATE_TIME : u32 = 60;

// Note: The master server must have TCP 29920 open and UDP 27900 open.
const BROADCAST_PORT_UDP : u16 = 27900;
const SERVER_LIST_PORT_TCP : u16 = 29920;

use std::net::{UdpSocket,TcpListener};
use std::io::{Write,BufReader,BufRead};
use std::env;
use std::fs::File;
use std::thread;
use std::sync::{Arc, Mutex};

extern crate time;

mod halo_server;
use halo_server::HaloServer;

mod heartbeat_packet;
use heartbeat_packet::HeartbeatPacket;

fn main() {
    let count = env::args().count();
    let ip = if count == 2 {
        let j : Vec<_> = env::args().collect();
        j[1].to_string()
    }
    else if count == 1 {
        "0.0.0.0".to_string()
    }
    else {
        println!("Only one argument is allowed: the IP to bind to.");
        return;
    };

    // We need to bind on two different ports. If it failed to bind (invalid IP, port is taken), then we must make sure this is known.
    let halo_socket = match UdpSocket::bind((&ip as &str,SERVER_LIST_PORT_TCP)) {
        Err(_) => {
            println!("Error creating a UDP socket at {}:{}.",ip,SERVER_LIST_PORT_TCP);
            return;
        },
        Ok(halo_socket) => halo_socket
    };

    let client_socket = match TcpListener::bind((&ip as &str,BROADCAST_PORT_UDP)) {
        Err(_) => {
            println!("Error listening to TCP at {}:{}.",ip,BROADCAST_PORT_UDP);
            return;
        },
        Ok(client_socket) => client_socket
    };

    // Mutex for thread safety.
    let servers_halo: Vec<HaloServer> = Vec::new();
    let servers_mut_udp = Arc::new(Mutex::new(servers_halo));
    let servers_mut_tcp = servers_mut_udp.clone();
    let servers_mut_destruction = servers_mut_udp.clone();

    // Destruction thread. This will remove servers that have not broadcasted their presence in a while.
    thread::spawn(move || {
        loop {
            thread::sleep_ms(10 * 1000);
            let mut servers = servers_mut_destruction.lock().unwrap();
            let timenow = time::now().to_timespec().sec;
            servers.retain(|x| x.last_alive + DROP_TIME > timenow);
        }
    });

    // Blacklist mutex. Concurrency needs to be safe, my friend.
    let blacklist: Vec<String> = Vec::new();
    let blacklist_update = Arc::new(Mutex::new(blacklist));
    let blacklist_udp = blacklist_update.clone();

    // Blacklist read thread.
    thread::spawn(move || {
        loop {
            // Placed in a block so blacklist is unlocked before sleeping to prevent threads from being locked for too long.
            {
                let mut blacklist_ref = blacklist_update.lock().unwrap();
                match File::open(BLACKLIST_FILE) {
                    Ok(file) => {
                        let reader = BufReader::new(&file);
                        match reader.lines().collect() {
                            Err(_) => {
                                blacklist_ref.clear();
                            },
                            Ok(t) => {
                                *blacklist_ref = t;
                            }
                        }
                    },
                    Err(_) => {}
                };
            }
            thread::sleep_ms(BLACKLIST_UPDATE_TIME * 1000);
        }
    });

    // TCP server thread. This is for the HaloMD application.
    thread::spawn(move || {
        loop {
            for stream in client_socket.incoming() {
                let mut client = match stream {
                    Err(_) => continue,
                    Ok(the_stream) => the_stream
                };
                // Unwrap the IP.
                let ip = match client.peer_addr() {
                    Err(_) => continue,
                    Ok(ip) => ip.to_string().split(":").next().unwrap().to_string()
                };

                let mut ips = String::new();

                // Make servers_ref go out of scope to unlock it for other threads, since we don't need it.
                {
                    let servers_ref = servers_mut_tcp.lock().unwrap();
                    let servers = (*servers_ref).iter();

                    for j in servers {
                        ips = ips + &(j.to_string()) + "\n";
                    }
                }

                // Some number placed after the requester's IP. If you ask me, the source code was abducted by aliens, and this is a tracking number. Regardless, it's needed.
                ips = ips + &ip + ":49149:3425";

                // We may be here a while. Just in case...
                thread::spawn( move || {
                    let _ = client.write_all(ips.as_bytes());
                });
            }
        }
    });

    // UDP server is run on the main thread. Servers broadcast their presence here.

    // These are the allowed game versions. HaloMD and Halo PC 1.09 uses 01.00.09.0620, while Halo PC servers on 1.10 use 01.00.10.0621 (these are also joinable).
    let game_versions = [ "01.00.09.0620".to_string(), "01.00.10.0621".to_string() ];

    let mut buffer = [0; 2048];
    loop {
        let (length, source) = match halo_socket.recv_from(&mut buffer) {
            Err(_) => continue,
            Ok(x) => x
        };
        if length == 0 {
            continue;
        }
        let client_ip = source.to_string().split(":").next().unwrap().to_string();

        if buffer[0] == 3 || buffer[0] == 8 {
            let blacklist_ref = blacklist_udp.lock().unwrap();
            if blacklist_ref.contains(&client_ip) {
                continue;
            }

            // Heartbeat packet.
            if buffer[0] == 3 && length > 5 {

                let mut servers = servers_mut_udp.lock().unwrap();

                let packet = HeartbeatPacket::from_buffer(&buffer[5..length]);

                if packet.localport != 0 && length > 1 {
                    let updatetime = time::now().to_timespec().sec;
                    match servers.iter_mut().position(|x| x.ip == client_ip && x.port == packet.localport) {
                        None => {
                            if game_versions.contains(&packet.gamever) && &packet.gamename == "halor" {
                                let serverness = HaloServer { ip:client_ip, port: packet.localport, last_alive: updatetime };
                                (*servers).push(serverness);
                            }
                            continue;
                        }
                        Some(k) => {
                            servers[k].last_alive = updatetime;
                            if packet.statechanged == 2 {
                                servers.remove(k);
                            }
                        }
                    };
                }
            }

            // Keepalive packet. We need to rely on the origin's port for this, unfortunately.
            else if buffer[0] == 8 {
                let mut servers_ref = servers_mut_udp.lock().unwrap();
                let servers = (*servers_ref).iter_mut();

                for i in servers {
                    if i.ip == client_ip && i.port == source.port() {
                        i.last_alive = time::now().to_timespec().sec;
                        break;
                    }
                }
            }
        }
    }
}
<commit_msg>Add constants and use function for getting IP<commit_after>// Syntax: master_server [ip address (0.0.0.0 by default)]
// This was created for HaloMD.

// If the server does not respond in at least this many seconds, it will be dropped from the list.
const DROP_TIME : i64 = 60;

// Blacklist for blocking IPs. Separate with newlines.
// Blacklisting IPs ignores heartbeat and keepalive packets from an IP address.
// That means that servers that are banned will not be immediately removed, but will time out, instead.
const BLACKLIST_FILE : &'static str = "blacklist.txt";

// Read the blacklist every x amount of seconds.
const BLACKLIST_UPDATE_TIME : u32 = 60;

// Note: The master server must have TCP 29920 open and UDP 27900 open.
const BROADCAST_PORT_UDP : u16 = 27900;
const SERVER_LIST_PORT_TCP : u16 = 29920;

// Broadcast packet types.
const KEEPALIVE : u8 = 8;
const HEARTBEAT : u8 = 3;

use std::net::{UdpSocket,TcpListener,SocketAddr};
use std::net::SocketAddr::{V4,V6};
use std::io::{Write,BufReader,BufRead};
use std::env;
use std::fs::File;
use std::thread;
use std::sync::{Arc, Mutex};

extern crate time;

mod halo_server;
use halo_server::HaloServer;

mod heartbeat_packet;
use heartbeat_packet::HeartbeatPacket;

fn get_ip(addr: SocketAddr) -> String {
    match addr {
        V4(ipv4) => ipv4.ip().to_string(),
        V6(ipv6) => "[".to_string() + &ipv6.ip().to_string() + "]"
    }
}

fn main() {
    let count = env::args().count();
    let ip = if count == 2 {
        let j : Vec<_> = env::args().collect();
        j[1].to_string()
    }
    else if count == 1 {
        "0.0.0.0".to_string()
    }
    else {
        println!("Only one argument is allowed: the IP to bind to.");
        return;
    };

    // We need to bind on two different ports. If it failed to bind (invalid IP, port is taken), then we must make sure this is known.
    let halo_socket = match UdpSocket::bind((&ip as &str,BROADCAST_PORT_UDP)) {
        Err(_) => {
            println!("Error creating a UDP socket at {}:{}.",ip,BROADCAST_PORT_UDP);
            return;
        },
        Ok(halo_socket) => halo_socket
    };

    let client_socket = match TcpListener::bind((&ip as &str,SERVER_LIST_PORT_TCP)) {
        Err(_) => {
            println!("Error listening to TCP at {}:{}.",ip,SERVER_LIST_PORT_TCP);
            return;
        },
        Ok(client_socket) => client_socket
    };

    // Mutex for thread safety.
    let servers_halo: Vec<HaloServer> = Vec::new();
    let servers_mut_udp = Arc::new(Mutex::new(servers_halo));
    let servers_mut_tcp = servers_mut_udp.clone();
    let servers_mut_destruction = servers_mut_udp.clone();

    // Destruction thread. This will remove servers that have not broadcasted their presence in a while.
    thread::spawn(move || {
        loop {
            thread::sleep_ms(10 * 1000);
            let mut servers = servers_mut_destruction.lock().unwrap();
            let timenow = time::now().to_timespec().sec;
            servers.retain(|x| x.last_alive + DROP_TIME > timenow);
        }
    });

    // Blacklist mutex. Concurrency needs to be safe, my friend.
    let blacklist: Vec<String> = Vec::new();
    let blacklist_update = Arc::new(Mutex::new(blacklist));
    let blacklist_udp = blacklist_update.clone();

    // Blacklist read thread.
    thread::spawn(move || {
        loop {
            // Placed in a block so blacklist is unlocked before sleeping to prevent threads from being locked for too long.
            {
                let mut blacklist_ref = blacklist_update.lock().unwrap();
                match File::open(BLACKLIST_FILE) {
                    Ok(file) => {
                        let reader = BufReader::new(&file);
                        match reader.lines().collect() {
                            Err(_) => {
                                blacklist_ref.clear();
                            },
                            Ok(t) => {
                                *blacklist_ref = t;
                            }
                        }
                    },
                    Err(_) => {}
                };
            }
            thread::sleep_ms(BLACKLIST_UPDATE_TIME * 1000);
        }
    });

    // TCP server thread. This is for the HaloMD application.
    thread::spawn(move || {
        loop {
            for stream in client_socket.incoming() {
                let mut client = match stream {
                    Err(_) => continue,
                    Ok(the_stream) => the_stream
                };
                // Unwrap the IP.
                let ip = match client.peer_addr() {
                    Err(_) => continue,
                    Ok(ip) => get_ip(ip)
                };

                let mut ips = String::new();

                // Make servers_ref go out of scope to unlock it for other threads, since we don't need it.
                {
                    let servers_ref = servers_mut_tcp.lock().unwrap();
                    let servers = (*servers_ref).iter();

                    for j in servers {
                        ips = ips + &(j.to_string()) + "\n";
                    }
                }

                // Some number placed after the requester's IP. If you ask me, the source code was abducted by aliens, and this is a tracking number. Regardless, it's needed.
                ips = ips + &ip + ":49149:3425";

                // We may be here a while. Just in case...
                thread::spawn( move || {
                    let _ = client.write_all(ips.as_bytes());
                });
            }
        }
    });

    // UDP server is run on the main thread. Servers broadcast their presence here.

    // These are the allowed game versions. HaloMD and Halo PC 1.09 uses 01.00.09.0620, while Halo PC servers on 1.10 use 01.00.10.0621 (these are also joinable).
    let game_versions = [ "01.00.09.0620".to_string(), "01.00.10.0621".to_string() ];

    let mut buffer = [0; 2048];
    loop {
        let (length, source) = match halo_socket.recv_from(&mut buffer) {
            Err(_) => continue,
            Ok(x) => x
        };
        if length == 0 {
            continue;
        }

        let client_ip = get_ip(source);

        if buffer[0] == KEEPALIVE || buffer[0] == HEARTBEAT {
            let blacklist_ref = blacklist_udp.lock().unwrap();
            if blacklist_ref.contains(&client_ip) {
                continue;
            }

            // Heartbeat packet. These contain null-terminated C strings and are ordered in key1[0]value1[0]key2[0]value2[0]key3[0]value3[0] where [0] is a byte equal to 0x00.
            if buffer[0] == HEARTBEAT && length > 5 {

                let mut servers = servers_mut_udp.lock().unwrap();

                let packet = HeartbeatPacket::from_buffer(&buffer[5..length]);

                if packet.localport != 0 {
                    let updatetime = time::now().to_timespec().sec;
                    match servers.iter_mut().position(|x| x.ip == client_ip && x.port == packet.localport) {
                        None => {
                            if game_versions.contains(&packet.gamever) && &packet.gamename == "halor" {
                                let serverness = HaloServer { ip:client_ip, port: packet.localport, last_alive: updatetime };
                                (*servers).push(serverness);
                            }
                        }
                        Some(k) => {
                            servers[k].last_alive = updatetime;
                            if packet.statechanged == 2 {
                                servers.remove(k);
                            }
                        }
                    };
                }
            }

            // Keepalive packet. We need to rely on the origin's port for this, unfortunately. This may mean that the source port is incorrect if the port was changed with NAT.
            else if buffer[0] == KEEPALIVE {
                let mut servers_ref = servers_mut_udp.lock().unwrap();
                let servers = (*servers_ref).iter_mut();

                for i in servers {
                    if i.ip == client_ip && i.port == source.port() {
                        i.last_alive = time::now().to_timespec().sec;
                        break;
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use graphics::*;

// Local crate.
use Vector2d = vector2d::Vector2d;

static EPSILON: f64 = 0.0000000001;

pub fn area(contour: &[Vector2d]) -> f64 {
    let n = contour.len();
    let mut A = 0.0_f64;

    for i in range(0, n) {
        let q = i;
        let p = (i + n - 1) % n;
        A += contour.get(p).unwrap().get_x() * contour.get(q).unwrap().get_y()
            - contour.get(q).unwrap().get_x() * contour.get(p).unwrap().get_y();
    }

    A * 0.5_f64
}

/// InsideTriangle decides if a point P is Inside of the triangle
/// defined by A, B, C.
pub fn inside_triangle(
    Ax: f64, Ay: f64,
    Bx: f64, By: f64,
    Cx: f64, Cy: f64,
    Px: f64, Py: f64
) -> bool {

    let ax = Cx - Bx;
    let ay = Cy - By;
    let bx = Ax - Cx;
    let by = Ay - Cy;
    let cx = Bx - Ax;
    let cy = By - Ay;
    let apx = Px - Ax;
    let apy = Py - Ay;
    let bpx = Px - Bx;
    let bpy = Py - By;
    let cpx = Px - Cx;
    let cpy = Py - Cy;

    let aCROSSbp = ax*bpy - ay*bpx;
    let cCROSSap = cx*apy - cy*apx;
    let bCROSScp = bx*cpy - by*cpx;

    (
        (aCROSSbp >= 0.0_f64)
        && (bCROSScp >= 0.0_f64)
        && (cCROSSap >= 0.0_f64)
    )
}

pub fn snip(
    contour: &[Vector2d],
    triangle: [uint, ..3],
    n: uint,
    V: &mut Vec<uint>
) -> bool {
    let u = triangle[0];
    let v = triangle[1];
    let w = triangle[2];

    let Ax = contour.get(*V.get(u)).unwrap().get_x();
    let Ay = contour.get(*V.get(u)).unwrap().get_y();

    let Bx = contour.get(*V.get(v)).unwrap().get_x();
    let By = contour.get(*V.get(v)).unwrap().get_y();

    let Cx = contour.get(*V.get(w)).unwrap().get_x();
    let Cy = contour.get(*V.get(w)).unwrap().get_y();

    if EPSILON > (((Bx-Ax)*(Cy-Ay)) - ((By-Ay)*(Cx-Ax))) {
        return false;
    }

    for p in range(0, n) {
        if (p == u) || (p == v) || (p == w) { continue; }

        let Px = contour.get(*V.get(p)).unwrap().get_x();
        let Py = contour.get(*V.get(p)).unwrap().get_y();

        if inside_triangle(Ax,Ay,Bx,By,Cx,Cy,Px,Py) { return false; }
    }

    return true;
}

pub fn process(
    contour: &[Vector2d]
) -> Option<Vec<Vector2d>> {
    /* allocate and initialize list of Vertices in polygon */

    let n = contour.len();
    if n < 3 { return None; }

    let mut result: Vec<Vector2d> = Vec::new();
    let mut vertex_indices: Vec<uint> = Vec::with_capacity(n);

    /* we want a counter-clockwise polygon in V */

    // Computes the area which has opposite signs
    // whether the polygon is clock-wise or counter-clockwise.
    if 0.0_f64 < area(contour) {
        for v in range(0, n) { vertex_indices.push(v); }
    } else {
        for v in range(0, n) { vertex_indices.push((n-1)-v); }
    }

    let mut number_of_vertices = n;

    /*  remove nv-2 Vertices, creating 1 triangle every time */

    // Used for error detection.
    let mut count = 2 * number_of_vertices;

    let mut v = number_of_vertices - 1;
    while number_of_vertices > 2 {
        /* if we loop, it is probably a non-simple polygon */
        if count <= 0 {
            //** Triangulate: ERROR - probable bad polygon!
            return None;
        }

        count -= 1;

        /* three consecutive vertices in current polygon, <u,v,w> */
        let triangle = [
            v,
            vecmath::modular_offset_index(number_of_vertices, v, 1),
            vecmath::modular_offset_index(number_of_vertices, v, 2)
        ];
        v = triangle[1];

        if snip(contour, triangle, number_of_vertices, &mut vertex_indices) {
            for &i in triangle.iter() {
                result.push(*contour.get(*vertex_indices.get(i)).unwrap());
            }

            /* remove v from remaining polygon */
            for i in range(0, number_of_vertices - 3) {
                let s = v + i;
                let t = v + i + 1;

                *vertex_indices.get_mut(s) = *vertex_indices.get(t);
            }

            // "Removed" a vertex.
            number_of_vertices -= 1;

            /* resest error detection counter */
            count = 2 * number_of_vertices;
        }
    }

    Some(result)
}

<commit_msg>Added comment<commit_after>use graphics::*;

// Local crate.
use Vector2d = vector2d::Vector2d;

static EPSILON: f64 = 0.0000000001;

pub fn area(contour: &[Vector2d]) -> f64 {
    let n = contour.len();
    let mut A = 0.0_f64;

    for i in range(0, n) {
        let q = i;
        let p = (i + n - 1) % n;
        A += contour.get(p).unwrap().get_x() * contour.get(q).unwrap().get_y()
            - contour.get(q).unwrap().get_x() * contour.get(p).unwrap().get_y();
    }

    A * 0.5_f64
}

/// InsideTriangle decides if a point P is Inside of the triangle
/// defined by A, B, C.
pub fn inside_triangle(
    Ax: f64, Ay: f64,
    Bx: f64, By: f64,
    Cx: f64, Cy: f64,
    Px: f64, Py: f64
) -> bool {

    let ax = Cx - Bx;
    let ay = Cy - By;
    let bx = Ax - Cx;
    let by = Ay - Cy;
    let cx = Bx - Ax;
    let cy = By - Ay;
    let apx = Px - Ax;
    let apy = Py - Ay;
    let bpx = Px - Bx;
    let bpy = Py - By;
    let cpx = Px - Cx;
    let cpy = Py - Cy;

    let aCROSSbp = ax*bpy - ay*bpx;
    let cCROSSap = cx*apy - cy*apx;
    let bCROSScp = bx*cpy - by*cpx;

    (
        (aCROSSbp >= 0.0_f64)
        && (bCROSScp >= 0.0_f64)
        && (cCROSSap >= 0.0_f64)
    )
}

pub fn snip(
    contour: &[Vector2d],
    triangle: [uint, ..3],
    n: uint,
    V: &mut Vec<uint>
) -> bool {
    let u = triangle[0];
    let v = triangle[1];
    let w = triangle[2];

    let Ax = contour.get(*V.get(u)).unwrap().get_x();
    let Ay = contour.get(*V.get(u)).unwrap().get_y();

    let Bx = contour.get(*V.get(v)).unwrap().get_x();
    let By = contour.get(*V.get(v)).unwrap().get_y();

    let Cx = contour.get(*V.get(w)).unwrap().get_x();
    let Cy = contour.get(*V.get(w)).unwrap().get_y();

    if EPSILON > (((Bx-Ax)*(Cy-Ay)) - ((By-Ay)*(Cx-Ax))) {
        return false;
    }

    for p in range(0, n) {
        if (p == u) || (p == v) || (p == w) { continue; }

        let Px = contour.get(*V.get(p)).unwrap().get_x();
        let Py = contour.get(*V.get(p)).unwrap().get_y();

        if inside_triangle(Ax,Ay,Bx,By,Cx,Cy,Px,Py) { return false; }
    }

    return true;
}

pub fn process(
    contour: &[Vector2d]
) -> Option<Vec<Vector2d>> {
    /* allocate and initialize list of Vertices in polygon */

    let n = contour.len();
    if n < 3 { return None; }

    let mut result: Vec<Vector2d> = Vec::new();
    let mut vertex_indices: Vec<uint> = Vec::with_capacity(n);

    /* we want a counter-clockwise polygon in V */

    // Computes the area which has opposite signs
    // whether the polygon is clock-wise or counter-clockwise.
    if 0.0_f64 < area(contour) {
        for v in range(0, n) { vertex_indices.push(v); }
    } else {
        // Add indices in reverse order.
        for v in range(0, n) { vertex_indices.push((n-1)-v); }
    }

    // Stores the number of vertices.
    // Used instead of `vertex_indices.len()` to emulate removing of vertices.
    let mut number_of_vertices = n;

    /*  remove nv-2 Vertices, creating 1 triangle every time */

    // Used for error detection.
    let mut count = 2 * number_of_vertices;

    let mut v = number_of_vertices - 1;
    while number_of_vertices > 2 {
        /* if we loop, it is probably a non-simple polygon */
        if count <= 0 {
            //** Triangulate: ERROR - probable bad polygon!
            return None;
        }

        count -= 1;

        /* three consecutive vertices in current polygon, <u,v,w> */
        let triangle = [
            v,
            vecmath::modular_offset_index(number_of_vertices, v, 1),
            vecmath::modular_offset_index(number_of_vertices, v, 2)
        ];
        v = triangle[1];

        if snip(contour, triangle, number_of_vertices, &mut vertex_indices) {
            for &i in triangle.iter() {
                result.push(*contour.get(*vertex_indices.get(i)).unwrap());
            }

            /* remove v from remaining polygon */
            for i in range(0, number_of_vertices - 3) {
                let s = v + i;
                let t = v + i + 1;

                *vertex_indices.get_mut(s) = *vertex_indices.get(t);
            }

            // "Removed" a vertex.
            number_of_vertices -= 1;

            /* resest error detection counter */
            count = 2 * number_of_vertices;
        }
    }

    Some(result)
}

<|endoftext|>


<commit_before>// 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/.

use azure::{AzScaledFontRef, AzFloat};
use azure::{struct__AzNativeFont};

use azure_hl::{BackendType,SkiaBackend};
use azure::bindgen::{AzCreateScaledFontForNativeFont, AzReleaseScaledFont};

use core::libc::c_void;

#[cfg(target_os="macos")]
priv use scaled_font::macos::*;

#[cfg(target_os="linux")]
priv use scaled_font::linux::*;

#[cfg(target_os="macos")]
pub mod macos {
    extern mod core_graphics;
    extern mod core_text;

    pub use scaled_font::macos::core_text::font::CTFontRef;
    pub use scaled_font::macos::core_graphics::font::{CGFont, CGFontRef};
}

#[cfg(target_os="linux")]
pub mod linux {
    extern mod freetype;

    pub use scaled_font::linux::freetype::freetype::{FT_Face, FT_LOAD_DEFAULT};
}

type SkTypeface = *c_void;

pub struct ScaledFont {
    azure_scaled_font: AzScaledFontRef,
}

impl Drop for ScaledFont {
    fn finalize(&self) {
        unsafe {
            AzReleaseScaledFont(self.azure_scaled_font);
        }
    }
}

impl ScaledFont {
    pub fn get_ref(&self) -> AzScaledFontRef {
        self.azure_scaled_font
    }

    #[cfg(target_os="linux")]
    pub fn new(backend: BackendType, native_font: FT_Face, size: AzFloat)
        -> ScaledFont {
        use azure::AZ_NATIVE_FONT_SKIA_FONT_FACE;
        use azure::bindgen::{AzCreateFontOptions, AzDestroyFontOptions};

        let mut azure_native_font = struct__AzNativeFont {
            mType: 0,
            mFont: ptr::null()
        };

        match backend {
            SkiaBackend => {
                unsafe {
                    // NOTE: azure style flags and freetype style flags are the same in the lowest 2 bits
                    let style = ((*native_font).style_flags & 3) as u32;
                    let options = AzCreateFontOptions((*native_font).family_name, style);
                    azure_native_font.mType = AZ_NATIVE_FONT_SKIA_FONT_FACE;
                    azure_native_font.mFont = cast::transmute(options);
                    let azure_native_font_ptr = ptr::to_unsafe_ptr(&azure_native_font);
                    let azure_scaled_font = AzCreateScaledFontForNativeFont(azure_native_font_ptr, size);
                    AzDestroyFontOptions(options);
                    ScaledFont { azure_scaled_font: azure_scaled_font }
                }
            }
            _ => { fail!(~"don't know how to make a scaled font for this backend"); }
        }
    }

    /// Mac-specific function to create a font for the given backend.
    #[cfg(target_os="macos")]
    pub fn new(backend: BackendType, native_font: &CGFont, size: AzFloat) -> ScaledFont {
        use azure::AZ_NATIVE_FONT_MAC_FONT_FACE;
        use azure_hl::{CoreGraphicsBackend,CoreGraphicsAcceleratedBackend};

        let mut azure_native_font = struct__AzNativeFont {
            mType: 0,
            mFont: ptr::null()
        };

        match backend {
            CoreGraphicsBackend | CoreGraphicsAcceleratedBackend | SkiaBackend => {
                azure_native_font.mType = AZ_NATIVE_FONT_MAC_FONT_FACE;
                unsafe {
                    azure_native_font.mFont = cast::transmute(*native_font.borrow_ref());
                }
            }
            _ => {
                fail!(~"don't know how to make a scaled font for this backend");
            }
        }

        unsafe {
            let azure_scaled_font = AzCreateScaledFontForNativeFont(&azure_native_font, size);
            ScaledFont {
                azure_scaled_font: azure_scaled_font
            }
        }
    }
}

// FIXME: Move this stuff to a rust-skia?
// FIXME: Demangle the names!!!
#[cfg(target_os="macos")]
#[link_args="-lskia"]
extern {
    pub fn _Z26SkCreateTypefaceFromCTFontPK8__CTFont(font: CTFontRef) -> *SkTypeface;
}

<commit_msg>Update for language changes<commit_after>// 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/.

use azure::{AzScaledFontRef, AzFloat};
use azure::{struct__AzNativeFont};

use azure_hl::{BackendType,SkiaBackend};
use azure::bindgen::{AzCreateScaledFontForNativeFont, AzReleaseScaledFont};

use core::libc::c_void;

#[cfg(target_os="macos")]
priv use scaled_font::macos::*;

#[cfg(target_os="linux")]
priv use scaled_font::linux::*;

#[cfg(target_os="macos")]
pub mod macos {
    extern mod core_graphics;
    extern mod core_text;

    pub use scaled_font::macos::core_text::font::CTFontRef;
    pub use scaled_font::macos::core_graphics::font::{CGFont, CGFontRef};
}

#[cfg(target_os="linux")]
pub mod linux {
    extern mod freetype;

    pub use scaled_font::linux::freetype::freetype::{FT_Face, FT_LOAD_DEFAULT};
}

type SkTypeface = *c_void;

pub struct ScaledFont {
    azure_scaled_font: AzScaledFontRef,
}

impl Drop for ScaledFont {
    fn finalize(&self) {
        unsafe {
            AzReleaseScaledFont(self.azure_scaled_font);
        }
    }
}

impl ScaledFont {
    pub fn get_ref(&self) -> AzScaledFontRef {
        self.azure_scaled_font
    }

    #[cfg(target_os="linux")]
    pub fn new(backend: BackendType, native_font: FT_Face, size: AzFloat)
        -> ScaledFont {
        use azure::AZ_NATIVE_FONT_SKIA_FONT_FACE;
        use azure::bindgen::{AzCreateFontOptions, AzDestroyFontOptions};

        let mut azure_native_font = struct__AzNativeFont {
            mType: 0,
            mFont: ptr::null()
        };

        match backend {
            SkiaBackend => {
                unsafe {
                    // NOTE: azure style flags and freetype style flags are the same in the lowest 2 bits
                    let style = ((*native_font).style_flags & 3) as u32;
                    let options = AzCreateFontOptions((*native_font).family_name, style);
                    azure_native_font.mType = AZ_NATIVE_FONT_SKIA_FONT_FACE;
                    azure_native_font.mFont = cast::transmute(options);
                    let azure_native_font_ptr = ptr::to_unsafe_ptr(&azure_native_font);
                    let azure_scaled_font = AzCreateScaledFontForNativeFont(azure_native_font_ptr, size);
                    AzDestroyFontOptions(options);
                    ScaledFont { azure_scaled_font: azure_scaled_font }
                }
            }
            _ => { fail!(~"don't know how to make a scaled font for this backend"); }
        }
    }

    /// Mac-specific function to create a font for the given backend.
    #[cfg(target_os="macos")]
    pub fn new(backend: BackendType, native_font: &CGFont, size: AzFloat) -> ScaledFont {
        use azure::AZ_NATIVE_FONT_MAC_FONT_FACE;
        use azure_hl::{CoreGraphicsBackend,CoreGraphicsAcceleratedBackend};

        let mut azure_native_font = struct__AzNativeFont {
            mType: 0,
            mFont: ptr::null()
        };

        match backend {
            CoreGraphicsBackend | CoreGraphicsAcceleratedBackend | SkiaBackend => {
                azure_native_font.mType = AZ_NATIVE_FONT_MAC_FONT_FACE;
                unsafe {
                    azure_native_font.mFont = cast::transmute(*native_font.contents.borrow_ref());
                }
            }
            _ => {
                fail!(~"don't know how to make a scaled font for this backend");
            }
        }

        unsafe {
            let azure_scaled_font = AzCreateScaledFontForNativeFont(&azure_native_font, size);
            ScaledFont {
                azure_scaled_font: azure_scaled_font
            }
        }
    }
}

// FIXME: Move this stuff to a rust-skia?
// FIXME: Demangle the names!!!
#[cfg(target_os="macos")]
#[link_args="-lskia"]
extern {
    pub fn _Z26SkCreateTypefaceFromCTFontPK8__CTFont(font: CTFontRef) -> *SkTypeface;
}

<|endoftext|>