<commit_before>
use std::sync::{Barrier, RwLock, Arc, RwLockWriteGuard};
use std::sync::atomic::{AtomicBool, Ordering};

extern crate rand;
use rand::{ThreadRng, Rng};
use rand::distributions::{IndependentSample, Range};

extern crate gr;
use gr::{GI, width_box, split_box, midpoint_box, eps_tol};

extern crate gelpia_utils;
use gelpia_utils::{Quple, INF, NINF, Flt, Parameters};

extern crate function;
use function::FuncObj;

#[derive(Clone)]
pub struct Individual {
    pub solution: Vec<GI>,
    pub fitness: Flt,
}

pub fn ea(x_e: Vec<GI>,
          param: Parameters,
          population: Arc<RwLock<Vec<Individual>>>,
          f_bestag: Arc<RwLock<Flt>>,
          x_bestbb: Arc<RwLock<Vec<GI>>>,
          b1: Arc<Barrier>,
          b2: Arc<Barrier>,
          stop: Arc<AtomicBool>,
          sync: Arc<AtomicBool>,
          fo_c: FuncObj) -> (Flt, Vec<GI>, bool) {

    let ref mut population = population.write().unwrap();
    let input = ea_core(&x_e, &param, population, &fo_c);
    let ans = fo_c.call(&input).upper();

    (ans, input, true)
}

fn ea_core(x_e: &Vec<GI>, param: &Parameters,
           population: &mut Vec<Individual>, fo_c: &FuncObj)
           -> (Vec<GI>) {
    let mut rng = rand::thread_rng();
    let dimension = Range::new(0, x_e.len());
    let mut ranges = Vec::new();
    for g in x_e {
        ranges.push(Range::new(g.lower(), g.upper()));
    }

    let mut times = Vec::new();
    for i in 0..100 {
        population.truncate(0);
        sample(param.population, population, fo_c, &ranges, &mut rng);
        let mut flag = true;
        for iteration in 0..10000 {
            population.sort_by(|a, b| b.fitness.partial_cmp(&a.fitness).unwrap());

            //let unfit = population.split_off(param.elitism);
            population.truncate(param.elitism);

            if population[0].fitness > 200000_f64 {
                times.push(iteration);
                flag = false;
                break;
            }
            //println!("iteration: {} best fitness: {}", iteration, population[0].fitness);
            // for d in population[0].solution.clone() {
            //     print!("{} ", d.to_string());
            // }
            // println!("");

            for _ in 0..param.selection {
                //population.push(rng.choose(&unfit).unwrap().clone());
                population.push(rand_individual(fo_c, &ranges, &mut rng));
            }
            next_generation(param.population, population, fo_c, param.mutation,
                            param.crossover, &dimension, &ranges, &mut rng);
        }

        assert!(flag == false);
        println!("{}", times.iter().fold(0, |mut sum, &val| {sum += val; sum})/times.len());
    }

    println!("Average: {}", times.iter().fold(0, |mut sum, &val| {sum += val; sum})/times.len());
    population[0].solution.clone()
}

fn sample(population_size: usize, population: &mut Vec<Individual>,
          fo_c: &FuncObj, ranges: &Vec<Range<f64>>, rng: &mut ThreadRng)
          -> () {
    for _ in 0..population_size-population.len() {
        population.push(rand_individual(fo_c, ranges, rng));
    }

    ()
}

fn rand_individual(fo_c: &FuncObj, ranges: &Vec<Range<f64>>, rng: &mut ThreadRng)
                   -> (Individual) {
    let mut new_sol = Vec::new();
    for r in ranges {
        new_sol.push(GI::new_p(r.ind_sample(rng)));
    }
    let fitness = fo_c.call(&new_sol).lower();

    Individual{solution:new_sol, fitness:fitness}
}

fn next_generation(population_size:usize, population: &mut Vec<Individual>,
                   fo_c: &FuncObj, mut_rate: f64, crossover: f64,
                   dimension: &Range<usize>, ranges: &Vec<Range<f64>>,
                   rng: &mut ThreadRng)
                   -> () {

    let elites = population.clone();

    for _ in 0..population_size-population.len() {
        if rng.gen::<f64>() < crossover {
            population.push(breed(rng.choose(&elites).unwrap(),
                                  rng.choose(&elites).unwrap(),
                                  fo_c,
                                  dimension, rng));
        } else {
            population.push(mutate(rng.choose(&elites).unwrap(), fo_c, mut_rate,
                                   dimension, ranges, rng));
        }
    }

    ()
}

fn mutate(input: &Individual, fo_c: &FuncObj, mut_rate: f64,
          dimension: &Range<usize>, ranges: &Vec<Range<f64>>, rng: &mut ThreadRng)
          -> (Individual) {
    let mut output_sol = Vec::new();

    for (r, &ind) in ranges.iter().zip(input.solution.iter()) {
        output_sol.push(
            if rng.gen::<f64>() < mut_rate {
                ind
            } else {
                GI::new_p(r.ind_sample(rng))
            });
    }

    let fitness = fo_c.call(&output_sol).lower();

    Individual{solution: output_sol, fitness: fitness}
}

fn breed(parent1: &Individual, parent2: &Individual, fo_c: &FuncObj,
         dimention: &Range<usize>, rng: &mut ThreadRng) -> (Individual) {
    // let mut child = parent1.clone();
    // let crossover_point = dimention.ind_sample(rng);
    // child.solution.truncate(crossover_point);
    // let mut rest = parent2.clone().solution.split_off(crossover_point);
    // child.solution.append(&mut rest);
    // child.fitness = fo_c.call(&child.solution).lower();
    // child
    let (dumb_parent, smart_parent) = if parent1.fitness < parent2.fitness
    {(parent1, parent2)}else{(parent2, parent1)};

    let child = smart_parent.solution.iter().zip(dumb_parent.solution.iter()).map(|(&sm, &du)| GI::new_p(2)*sm-du).collect();

    let fitness = fo_c.call(&child).lower();

    Individual{solution: child, fitness: fitness}
}
<commit_msg>Hand in glove<commit_after>
use std::sync::{Barrier, RwLock, Arc, RwLockWriteGuard};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::atomic::Ordering as AtOrd;
extern crate rand;
use rand::{ThreadRng, Rng};
use rand::distributions::{IndependentSample, Range};

extern crate gr;
use gr::{GI, width_box, split_box, midpoint_box, eps_tol};

extern crate gelpia_utils;
use gelpia_utils::{Quple, INF, NINF, Flt, Parameters};

extern crate function;
use function::FuncObj;

#[derive(Clone)]
pub struct Individual {
    pub solution: Vec<GI>,
    pub fitness: Flt,
}

pub fn ea(x_e: Vec<GI>,
          param: Parameters,
          population: Arc<RwLock<Vec<Individual>>>,
          f_bestag: Arc<RwLock<Flt>>,
          x_bestbb: Arc<RwLock<Vec<GI>>>,
          b1: Arc<Barrier>,
          b2: Arc<Barrier>,
          stop: Arc<AtomicBool>,
          sync: Arc<AtomicBool>,
          fo_c: FuncObj) -> (Flt, Vec<GI>, bool) {

    let ref mut population = population.write().unwrap();
    let input = ea_core(&x_e, &param, &stop, &sync, &b1, &b2, &f_bestag,
                        &x_bestbb, population, &fo_c);
    let ans = fo_c.call(&input).upper();

    (ans, input, true)
}

fn ea_core(x_e: &Vec<GI>, param: &Parameters, stop: &Arc<AtomicBool>,
           sync: &Arc<AtomicBool>, b1: &Arc<Barrier>, b2: &Arc<Barrier>,
           f_bestag: &Arc<RwLock<Flt>>,
           x_bestbb: &Arc<RwLock<Vec<GI>>>,
           population: &mut Vec<Individual>, fo_c: &FuncObj)
           -> (Vec<GI>) {
    let mut rng = rand::thread_rng();
    let dimension = Range::new(0, x_e.len());
    let mut ranges = Vec::new();
    for g in x_e {
        ranges.push(Range::new(g.lower(), g.upper()));
    }

    while !stop.load(AtOrd::Acquire) {
        if sync.load(AtOrd::Acquire) {
            b1.wait();
            b2.wait();
        }

        sample(param.population, population, fo_c, &ranges, &mut rng);

        population.sort_by(|a, b| b.fitness.partial_cmp(&a.fitness).unwrap());

        for iteration in 0..100 {
            population.truncate(param.elitism);

            for _ in 0..param.selection {
                population.push(rand_individual(fo_c, &ranges, &mut rng));
            }

            next_generation(param.population, population, fo_c, param.mutation,
                            param.crossover, &dimension, &ranges, &mut rng);
            population.sort_by(|a, b| b.fitness.partial_cmp(&a.fitness).unwrap());

            // Report fittest of the fit.
            {
                let mut fbest = f_bestag.write().unwrap();

                *fbest =
                    if *fbest < population[0].fitness { population[0].fitness }
                else { *fbest };
            }

            // Kill worst of the worst
            let mut ftg = Vec::new();
            {
                let bestbb = x_bestbb.read().unwrap();
                // From The Gods
                for i in 0..bestbb.len() {
                    ftg.push(Range::new(bestbb[i].lower(), bestbb[i].upper()));
                }
            }
            let worst_ind = population.len() - 1;
            population[worst_ind] = rand_individual(fo_c,
                                                    &ftg,
                                                    &mut rng);
            population.sort_by(|a, b| b.fitness.partial_cmp(&a.fitness).unwrap());
        }

        // Report fittest of the fit
/*        {
            let mut fbest = f_bestag.write().unwrap();

            *fbest =
                if *fbest < population[0].fitness { population[0].fitness }
            else { *fbest };
        }

        // Kill worst of the worst
        let mut ftg = Vec::new();
        {
            let bestbb = x_bestbb.read().unwrap();
            // From The Gods
            for i in 0..bestbb.len() {
                ftg.push(Range::new(bestbb[i].lower(), bestbb[i].upper()));
            }
        }
        let worst_ind = population.len() - 1;
        population[worst_ind] = rand_individual(fo_c,
                                                &ftg,
                                                &mut rng);
        population.sort_by(|a, b| b.fitness.partial_cmp(&a.fitness).unwrap());*/
    }
    let result = population[0].solution.clone();

    result
}

fn sample(population_size: usize, population: &mut Vec<Individual>,
          fo_c: &FuncObj, ranges: &Vec<Range<f64>>, rng: &mut ThreadRng)
          -> () {
    for _ in 0..population_size-population.len() {
        population.push(rand_individual(fo_c, ranges, rng));
    }

    ()
}

fn rand_individual(fo_c: &FuncObj, ranges: &Vec<Range<f64>>, rng: &mut ThreadRng)
                   -> (Individual) {
    let mut new_sol = Vec::new();
    for r in ranges {
        new_sol.push(GI::new_p(r.ind_sample(rng)));
    }
    let fitness = fo_c.call(&new_sol).lower();

    Individual{solution:new_sol, fitness:fitness}
}

fn next_generation(population_size:usize, population: &mut Vec<Individual>,
                   fo_c: &FuncObj, mut_rate: f64, crossover: f64,
                   dimension: &Range<usize>, ranges: &Vec<Range<f64>>,
                   rng: &mut ThreadRng)
                   -> () {

    let elites = population.clone();

    for _ in 0..population_size-population.len() {
        if rng.gen::<f64>() < crossover {
            population.push(breed(rng.choose(&elites).unwrap(),
                                  rng.choose(&elites).unwrap(),
                                  fo_c,
                                  dimension, rng));
        } else {
            population.push(mutate(rng.choose(&elites).unwrap(), fo_c, mut_rate,
                                   dimension, ranges, rng));
        }
    }

    ()
}

fn mutate(input: &Individual, fo_c: &FuncObj, mut_rate: f64,
          dimension: &Range<usize>, ranges: &Vec<Range<f64>>, rng: &mut ThreadRng)
          -> (Individual) {
    let mut output_sol = Vec::new();

    for (r, &ind) in ranges.iter().zip(input.solution.iter()) {
        output_sol.push(
            if rng.gen::<f64>() < mut_rate {
                ind
            } else {
                GI::new_p(r.ind_sample(rng))
            });
    }

    let fitness = fo_c.call(&output_sol).lower();

    Individual{solution: output_sol, fitness: fitness}
}

fn breed(parent1: &Individual, parent2: &Individual, fo_c: &FuncObj,
         dimention: &Range<usize>, rng: &mut ThreadRng) -> (Individual) {
    let mut child = parent1.clone();
    let crossover_point = dimention.ind_sample(rng);
    child.solution.truncate(crossover_point);
    let mut rest = parent2.clone().solution.split_off(crossover_point);
    child.solution.append(&mut rest);
    child.fitness = fo_c.call(&child.solution).lower();
    child
}
<|endoftext|>


<commit_before>use io;
use std::fs::File;

pub struct MultiFile {
    files_iterator: Box<Iterator<Item = String>>,
    current_file: Option<File>,
}

impl MultiFile {
    pub fn new(filenames: Vec<String>) -> MultiFile {
        MultiFile {
            files_iterator: Box::new(filenames.into_iter()),
            current_file: None,
        }
    }
}

impl io::Read for MultiFile {
    fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
        let read_size;
        {
            let mut file = match self.current_file {
                Some(ref file) => file,
                None => {
                    self.current_file = Some(File::open(&self.files_iterator.next().unwrap())?);
                    return self.read(&mut buf);
                }
            };
            read_size = file.read(buf);
        }

        match read_size {
            Ok(0) => {
                self.current_file = match &self.files_iterator.next() {
                    Some(file) => Some(File::open(file).unwrap()),
                    None => {
                        return Ok(0);
                    }
                };
                return self.read(&mut buf);
            }
            Ok(read_size) => Ok(read_size),
            Err(err) => Err(err),
        }
    }
}

#[cfg(test)]
mod tests {
    use std::io;
    use std::io::Read;
    use std::io::BufRead;

    use super::*;

    #[test]
    fn test_read_all() {
        let filenames = vec![
            String::from("src/test/simple-1.log"),
            String::from("src/test/simple-2.log"),
        ];
        let input = MultiFile::new(filenames);

        let reader = io::BufReader::new(input);

        let result = reader.lines().count();
        assert_eq!(result, 8);
    }

    #[test]
    fn test_read_non_existent() {
        let filenames = vec![String::from("src/test/non-existent.log")];
        let mut input = MultiFile::new(filenames);
        let mut buffer = [0; 10];
        let result = input.read(&mut buffer);

        assert!(result.is_err());
    }
}
<commit_msg>Handle the case of an empty file list<commit_after>use io;
use std::fs::File;

pub struct MultiFile {
    files_iterator: Box<Iterator<Item = String>>,
    current_file: Option<File>,
}

impl MultiFile {
    pub fn new(filenames: Vec<String>) -> MultiFile {
        MultiFile {
            files_iterator: Box::new(filenames.into_iter()),
            current_file: None,
        }
    }
}

impl io::Read for MultiFile {
    fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
        let read_size;
        {
            let mut file = match self.current_file {
                Some(ref file) => file,
                None => {
                    self.current_file = match &self.files_iterator.next() {
                        Some(file) => Some(File::open(file)?),
                        None => {
                            return Ok(0);
                        }
                    };
                    return self.read(&mut buf);
                }
            };
            read_size = file.read(buf);
        }

        match read_size {
            Ok(0) => {
                self.current_file = match &self.files_iterator.next() {
                    Some(file) => Some(File::open(file).unwrap()),
                    None => {
                        return Ok(0);
                    }
                };
                return self.read(&mut buf);
            }
            Ok(read_size) => Ok(read_size),
            Err(err) => Err(err),
        }
    }
}

#[cfg(test)]
mod tests {
    use std::io;
    use std::io::Read;
    use std::io::BufRead;

    use super::*;

    #[test]
    fn test_read_all() {
        let filenames = vec![
            String::from("src/test/simple-1.log"),
            String::from("src/test/simple-2.log"),
        ];
        let input = MultiFile::new(filenames);

        let reader = io::BufReader::new(input);

        let result = reader.lines().count();
        assert_eq!(result, 8);
    }

    #[test]
    fn test_read_non_existent() {
        let filenames = vec![String::from("src/test/non-existent.log")];
        let mut input = MultiFile::new(filenames);
        let mut buffer = [0; 10];
        let result = input.read(&mut buffer);

        assert!(result.is_err());
    }

    #[test]
    fn test_read_no_files_means_eof() {
        let filenames = vec![];
        let mut input = MultiFile::new(filenames);
        let mut buffer = [0; 10];
        let result = input.read(&mut buffer);

        assert!(result.is_ok());
        assert_eq!(result.unwrap(), 0);
    }
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::iter::repeat;

fn split_path(path: &str) -> Vec<&str> {
    let mut last_idx = 0;
    let mut rv = vec![];
    for (idx, _) in path.match_indices(&['/', '\\'][..]) {
        rv.push(&path[last_idx..idx]);
        last_idx = idx;
    }
    if last_idx < path.len() {
        rv.push(&path[last_idx..]);
    }
    rv
}

fn is_abs_path(s: &str) -> bool {
    if s.starts_with('/') {
        return true;
    } else if s.len() > 3 {
        let b = s.as_bytes();
        if b[1] == b':' && (b[2] == b'/' || b[2] == b'\\') &&
           ((b[0] >= b'a' && b[0] <= b'z') || (b[0] >= b'A' && b[0] <= b'Z')) {
            return true;
        }
    }
    false
}

fn find_common_prefix_of_sorted_vec<'a>(items: &'a [Cow<'a, [&'a str]>])
    -> Option<&'a [&'a str]>
{
    if items.is_empty() {
        return None;
    }

    let shortest = &items[0];
    let mut max_idx = None;
    for seq in items.iter() {
        let mut seq_max_idx = None;
        for (idx, &comp) in shortest.iter().enumerate() {
            if seq.get(idx) != Some(&comp) {
                break;
            }
            seq_max_idx = Some(idx);
        }
        if max_idx.is_none() || seq_max_idx < max_idx {
            max_idx = seq_max_idx;
        }
    }

    if let Some(max_idx) = max_idx {
        Some(&shortest[..max_idx + 1])
    } else {
        None
    }
}

pub fn find_common_prefix<'a, I: Iterator<Item = &'a str>>(iter: I) -> Option<String> {
    let mut items: Vec<Cow<[&str]>> = iter
        .filter(|x| is_abs_path(x))
        .map(|x| Cow::Owned(split_path(x)))
        .collect();
    items.sort_by_key(|x| x.len());

    if let Some(slice) = find_common_prefix_of_sorted_vec(&items) {
        let rv = slice.join("");
        if !rv.is_empty() && &rv != "/" {
            return Some(rv);
        }
    }

    None
}

/// Helper function to calculate the path from a base file to a target file.
///
/// This is intended to caculate the path from a minified JavaScript file
/// to a sourcemap if they are both on the same server.
///
/// Example:
///
/// ```
/// #use sourcemap::make_relative_path;
/// assert_eq!(&make_relative_path(
///     "/foo/bar/baz.js", "/foo/baz.map").unwrap(), "../baz.map");
/// ```
pub fn make_relative_path<'a>(base: &str, target: &str) -> Option<String> {
    let target_path: Vec<_> = target.split(&['/', '\\'][..]).filter(|x| x.len() > 0).collect();
    let mut base_path: Vec<_> = base.split(&['/', '\\'][..]).filter(|x| x.len() > 0).collect();
    base_path.pop();

    let mut items = vec![
        Cow::Borrowed(target_path.as_slice()),
        Cow::Borrowed(base_path.as_slice()),
    ];
    items.sort_by_key(|x| x.len());

    let prefix = find_common_prefix_of_sorted_vec(&items).map(|x| x.len()).unwrap_or(0);
    let mut rel_list: Vec<_> = repeat("../").take(base_path.len() - prefix).collect();
    rel_list.extend_from_slice(&target_path[prefix..]);
    if rel_list.is_empty() {
        Some(".".into())
    } else {
        Some(rel_list.join(""))
    }
}

#[test]
fn test_is_abs_path() {
    assert_eq!(is_abs_path("C:\\foo.txt"), true);
    assert_eq!(is_abs_path("d:/foo.txt"), true);
    assert_eq!(is_abs_path("foo.txt"), false);
    assert_eq!(is_abs_path("/foo.txt"), true);
    assert_eq!(is_abs_path("/"), true);
}

#[test]
fn test_split_path() {
    assert_eq!(split_path("/foo/bar/baz"), &["", "/foo", "/bar", "/baz"]);
}

#[test]
fn test_find_common_prefix() {
    let rv = find_common_prefix(vec!["/foo/bar/baz", "/foo/bar/baz/blah"].into_iter());
    assert_eq!(rv, Some("/foo/bar/baz".into()));

    let rv = find_common_prefix(vec!["/foo/bar/baz", "/foo/bar/baz/blah", "/meh"].into_iter());
    assert_eq!(rv, None);

    let rv = find_common_prefix(vec!["/foo/bar/baz", "/foo/bar/baz/blah", "/foo"].into_iter());
    assert_eq!(rv, Some("/foo".into()));

    let rv = find_common_prefix(vec!["/foo/bar/baz", "/foo/bar/baz/blah", "foo"].into_iter());
    assert_eq!(rv, Some("/foo/bar/baz".into()));

    let rv = find_common_prefix(vec!["/foo/bar/baz", "/foo/bar/baz/blah", "/blah", "foo"]
        .into_iter());
    assert_eq!(rv, None);

    let rv = find_common_prefix(vec!["/foo/bar/baz", "/foo/bar/baz/blah", "/blah", "foo"]
        .into_iter());
    assert_eq!(rv, None);
}

#[test]
fn test_make_relative_path() {
    assert_eq!(&make_relative_path("/foo/bar/baz.js", "/foo/bar/baz.map").unwrap(), "baz.map");
    assert_eq!(&make_relative_path("/foo/bar/.", "/foo/bar/baz.map").unwrap(), "baz.map");
    assert_eq!(&make_relative_path("/foo/bar/baz.js", "/foo/baz.map").unwrap(), "../baz.map");
    assert_eq!(&make_relative_path("foo.txt", "foo.js").unwrap(), "foo.js");
    assert_eq!(&make_relative_path("blah/foo.txt", "foo.js").unwrap(), "../foo.js");
}
<commit_msg>No need for option on join<commit_after>use std::borrow::Cow;
use std::iter::repeat;

fn split_path(path: &str) -> Vec<&str> {
    let mut last_idx = 0;
    let mut rv = vec![];
    for (idx, _) in path.match_indices(&['/', '\\'][..]) {
        rv.push(&path[last_idx..idx]);
        last_idx = idx;
    }
    if last_idx < path.len() {
        rv.push(&path[last_idx..]);
    }
    rv
}

fn is_abs_path(s: &str) -> bool {
    if s.starts_with('/') {
        return true;
    } else if s.len() > 3 {
        let b = s.as_bytes();
        if b[1] == b':' && (b[2] == b'/' || b[2] == b'\\') &&
           ((b[0] >= b'a' && b[0] <= b'z') || (b[0] >= b'A' && b[0] <= b'Z')) {
            return true;
        }
    }
    false
}

fn find_common_prefix_of_sorted_vec<'a>(items: &'a [Cow<'a, [&'a str]>])
    -> Option<&'a [&'a str]>
{
    if items.is_empty() {
        return None;
    }

    let shortest = &items[0];
    let mut max_idx = None;
    for seq in items.iter() {
        let mut seq_max_idx = None;
        for (idx, &comp) in shortest.iter().enumerate() {
            if seq.get(idx) != Some(&comp) {
                break;
            }
            seq_max_idx = Some(idx);
        }
        if max_idx.is_none() || seq_max_idx < max_idx {
            max_idx = seq_max_idx;
        }
    }

    if let Some(max_idx) = max_idx {
        Some(&shortest[..max_idx + 1])
    } else {
        None
    }
}

pub fn find_common_prefix<'a, I: Iterator<Item = &'a str>>(iter: I) -> Option<String> {
    let mut items: Vec<Cow<[&str]>> = iter
        .filter(|x| is_abs_path(x))
        .map(|x| Cow::Owned(split_path(x)))
        .collect();
    items.sort_by_key(|x| x.len());

    if let Some(slice) = find_common_prefix_of_sorted_vec(&items) {
        let rv = slice.join("");
        if !rv.is_empty() && &rv != "/" {
            return Some(rv);
        }
    }

    None
}

/// Helper function to calculate the path from a base file to a target file.
///
/// This is intended to caculate the path from a minified JavaScript file
/// to a sourcemap if they are both on the same server.
///
/// Example:
///
/// ```
/// # use sourcemap::make_relative_path;
/// assert_eq!(&make_relative_path(
///     "/foo/bar/baz.js", "/foo/baz.map"), "../baz.map");
/// ```
pub fn make_relative_path<'a>(base: &str, target: &str) -> String {
    let target_path: Vec<_> = target.split(&['/', '\\'][..]).filter(|x| x.len() > 0).collect();
    let mut base_path: Vec<_> = base.split(&['/', '\\'][..]).filter(|x| x.len() > 0).collect();
    base_path.pop();

    let mut items = vec![
        Cow::Borrowed(target_path.as_slice()),
        Cow::Borrowed(base_path.as_slice()),
    ];
    items.sort_by_key(|x| x.len());

    let prefix = find_common_prefix_of_sorted_vec(&items).map(|x| x.len()).unwrap_or(0);
    let mut rel_list: Vec<_> = repeat("../").take(base_path.len() - prefix).collect();
    rel_list.extend_from_slice(&target_path[prefix..]);
    if rel_list.is_empty() {
        ".".into()
    } else {
        rel_list.join("")
    }
}

#[test]
fn test_is_abs_path() {
    assert_eq!(is_abs_path("C:\\foo.txt"), true);
    assert_eq!(is_abs_path("d:/foo.txt"), true);
    assert_eq!(is_abs_path("foo.txt"), false);
    assert_eq!(is_abs_path("/foo.txt"), true);
    assert_eq!(is_abs_path("/"), true);
}

#[test]
fn test_split_path() {
    assert_eq!(split_path("/foo/bar/baz"), &["", "/foo", "/bar", "/baz"]);
}

#[test]
fn test_find_common_prefix() {
    let rv = find_common_prefix(vec!["/foo/bar/baz", "/foo/bar/baz/blah"].into_iter());
    assert_eq!(rv, Some("/foo/bar/baz".into()));

    let rv = find_common_prefix(vec!["/foo/bar/baz", "/foo/bar/baz/blah", "/meh"].into_iter());
    assert_eq!(rv, None);

    let rv = find_common_prefix(vec!["/foo/bar/baz", "/foo/bar/baz/blah", "/foo"].into_iter());
    assert_eq!(rv, Some("/foo".into()));

    let rv = find_common_prefix(vec!["/foo/bar/baz", "/foo/bar/baz/blah", "foo"].into_iter());
    assert_eq!(rv, Some("/foo/bar/baz".into()));

    let rv = find_common_prefix(vec!["/foo/bar/baz", "/foo/bar/baz/blah", "/blah", "foo"]
        .into_iter());
    assert_eq!(rv, None);

    let rv = find_common_prefix(vec!["/foo/bar/baz", "/foo/bar/baz/blah", "/blah", "foo"]
        .into_iter());
    assert_eq!(rv, None);
}

#[test]
fn test_make_relative_path() {
    assert_eq!(&make_relative_path("/foo/bar/baz.js", "/foo/bar/baz.map"), "baz.map");
    assert_eq!(&make_relative_path("/foo/bar/.", "/foo/bar/baz.map"), "baz.map");
    assert_eq!(&make_relative_path("/foo/bar/baz.js", "/foo/baz.map"), "../baz.map");
    assert_eq!(&make_relative_path("foo.txt", "foo.js"), "foo.js");
    assert_eq!(&make_relative_path("blah/foo.txt", "foo.js"), "../foo.js");
}
<|endoftext|>


<commit_before>// Copyright 2015 Nicholas Bishop
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::ops::{Add, Div, Mul, Sub};
use util::CastF32;
use vec3f::Vec3f;

/// Vector with two f32 components
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Vec2f {
    pub x: f32,
    pub y: f32
}

impl Vec2f {
    /// Create a Vec2f from two components
    pub fn new(x: f32, y: f32) -> Vec2f {
        Vec2f { x: x,
                y: y }
    }

    pub fn cross(self, other: Vec2f) -> f32 {
        self.x * other.y - self.y * other.x
    }

    pub fn lerp(self, other: Vec2f, t: f32) -> Vec2f {
        self * (1.0 - t) + other * t
    }

    pub fn dot(self, other: Vec2f) -> f32 {
        self.x * other.x + self.y * other.y
    }

    pub fn vec3f(self) -> Vec3f {
        Vec3f::new(self.x, self.y, 0.0)
    }

    pub fn magnitude_squared(self) -> f32 {
        self.dot(self)
    }

    pub fn magnitude(self) -> f32 {
        self.magnitude_squared().sqrt()
    }

    pub fn normalized(self) -> Option<Vec2f> {
        let m = self.magnitude();
        if m == 0.0 {
            None
        }
        else {
            Some(Vec2f::new(self.x / m, self.y / m))
        }
    }
}

impl Add for Vec2f {
    type Output = Vec2f;
    fn add(self, v: Vec2f) -> Vec2f {
        Vec2f::new(self.x + v.x, self.y + v.y)
    }
}

impl Sub for Vec2f {
    type Output = Vec2f;
    fn sub(self, v: Vec2f) -> Vec2f {
        Vec2f::new(self.x - v.x, self.y - v.y)
    }
}

impl Mul<f32> for Vec2f {
    type Output = Vec2f;
    fn mul(self, s: f32) -> Vec2f {
        Vec2f::new(self.x * s, self.y * s)
    }
}

impl Div<f32> for Vec2f {
    type Output = Vec2f;
    fn div(self, s: f32) -> Vec2f {
        Vec2f::new(self.x / s, self.y / s)
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Line2f {
    pub points: (Vec2f, Vec2f)
}

impl Line2f {
    pub fn new(a: Vec2f, b: Vec2f) -> Line2f {
        Line2f { points: (a, b) }
    }

    pub fn closest_parametric_point(self, point: Vec2f) -> f32 {
        let p0p = point - self.points.0;
        let p0p1 = self.points.1 - self.points.0;

        p0p.dot(p0p1) / p0p1.dot(p0p1)
    }
}

/// Create a Vec2f from x and y inputs
///
/// This is a convenience function that provides a little more
/// flexibility than Vec2f::new in that it will happily take numbers
/// that aren't f32 (including a mix of different types for each
/// component). Vec2f is such a common type that it seems reasonable
/// to provide a little extra ease of use.
pub fn vec2f<X: CastF32, Y: CastF32>(x: X, y: Y) -> Vec2f {
    Vec2f { x: x.as_f32(), y: y.as_f32() }
}
<commit_msg>Add Vec2f::cart_from_para<commit_after>// Copyright 2015 Nicholas Bishop
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::ops::{Add, Div, Mul, Sub};
use util::CastF32;
use vec3f::Vec3f;

/// Vector with two f32 components
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Vec2f {
    pub x: f32,
    pub y: f32
}

impl Vec2f {
    /// Create a Vec2f from two components
    pub fn new(x: f32, y: f32) -> Vec2f {
        Vec2f { x: x,
                y: y }
    }

    pub fn cross(self, other: Vec2f) -> f32 {
        self.x * other.y - self.y * other.x
    }

    pub fn lerp(self, other: Vec2f, t: f32) -> Vec2f {
        self * (1.0 - t) + other * t
    }

    pub fn dot(self, other: Vec2f) -> f32 {
        self.x * other.x + self.y * other.y
    }

    pub fn vec3f(self) -> Vec3f {
        Vec3f::new(self.x, self.y, 0.0)
    }

    pub fn magnitude_squared(self) -> f32 {
        self.dot(self)
    }

    pub fn magnitude(self) -> f32 {
        self.magnitude_squared().sqrt()
    }

    pub fn normalized(self) -> Option<Vec2f> {
        let m = self.magnitude();
        if m == 0.0 {
            None
        }
        else {
            Some(Vec2f::new(self.x / m, self.y / m))
        }
    }
}

impl Add for Vec2f {
    type Output = Vec2f;
    fn add(self, v: Vec2f) -> Vec2f {
        Vec2f::new(self.x + v.x, self.y + v.y)
    }
}

impl Sub for Vec2f {
    type Output = Vec2f;
    fn sub(self, v: Vec2f) -> Vec2f {
        Vec2f::new(self.x - v.x, self.y - v.y)
    }
}

impl Mul<f32> for Vec2f {
    type Output = Vec2f;
    fn mul(self, s: f32) -> Vec2f {
        Vec2f::new(self.x * s, self.y * s)
    }
}

impl Div<f32> for Vec2f {
    type Output = Vec2f;
    fn div(self, s: f32) -> Vec2f {
        Vec2f::new(self.x / s, self.y / s)
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Line2f {
    pub points: (Vec2f, Vec2f)
}

impl Line2f {
    pub fn new(a: Vec2f, b: Vec2f) -> Line2f {
        Line2f { points: (a, b) }
    }

    pub fn closest_parametric_point(self, point: Vec2f) -> f32 {
        let p0p = point - self.points.0;
        let p0p1 = self.points.1 - self.points.0;

        p0p.dot(p0p1) / p0p1.dot(p0p1)
    }

    pub fn cart_from_para(self, t: f32) -> Vec2f {
        self.points.0.lerp(self.points.1, t)
    }
}

/// Create a Vec2f from x and y inputs
///
/// This is a convenience function that provides a little more
/// flexibility than Vec2f::new in that it will happily take numbers
/// that aren't f32 (including a mix of different types for each
/// component). Vec2f is such a common type that it seems reasonable
/// to provide a little extra ease of use.
pub fn vec2f<X: CastF32, Y: CastF32>(x: X, y: Y) -> Vec2f {
    Vec2f { x: x.as_f32(), y: y.as_f32() }
}
<|endoftext|>


<commit_before>use std::future::Future;
use std::pin::Pin;
use std::sync::Mutex;
use std::sync::{Arc, Once};
use std::time::Duration;

use reqwest;
use tbot::{
    connectors::Https,
    contexts::{Command, Text},
    types::{input_file, parameters},
};

use crate::data::{DataError, Database};
use crate::feed::RSS;

mod opml;

pub const TELEGRAM_MAX_MSG_LEN: usize = 4096;
const RESP_SIZE_LIMIT: usize = 2 * 1024 * 1024;

#[derive(Debug, Copy, Clone)]
struct MsgTarget {
    chat_id: tbot::types::chat::Id,
    message_id: tbot::types::message::Id,
    first_time: bool,
}

impl MsgTarget {
    fn new(chat_id: tbot::types::chat::Id, message_id: tbot::types::message::Id) -> Self {
        MsgTarget {
            chat_id,
            message_id,
            first_time: false,
        }
    }
    fn update(self, message_id: tbot::types::message::Id) -> Self {
        MsgTarget { message_id, ..self }
    }
}

fn client() -> Arc<reqwest::Client> {
    static mut CLIENT: Option<Arc<reqwest::Client>> = None;
    static INIT: Once = Once::new();

    INIT.call_once(|| {
        let mut headers = reqwest::header::HeaderMap::new();
        let ua = format!(
            concat!(
                env!("CARGO_PKG_NAME"),
                "/",
                env!("CARGO_PKG_VERSION"),
                " (+https://t.me/{})"
            ),
            crate::BOT_NAME.get().expect("BOT_NAME not initialized")
        );
        headers.insert(
            reqwest::header::USER_AGENT,
            reqwest::header::HeaderValue::from_str(&ua).unwrap(),
        );
        let client = reqwest::Client::builder()
            .timeout(Duration::from_secs(10))
            .default_headers(headers)
            .redirect(reqwest::redirect::Policy::limited(5))
            .build()
            .unwrap();

        unsafe {
            CLIENT = Some(Arc::new(client));
        }
    });

    unsafe { CLIENT.clone() }.unwrap()
}

pub async fn rss(db: Arc<Mutex<Database>>, cmd: Arc<Command<Text<Https>>>) -> anyhow::Result<()> {
    let chat_id = cmd.chat.id;
    let channel = &cmd.text.value;
    let mut target_id = chat_id;

    if !channel.is_empty() {
        let user_id = cmd.from.as_ref().unwrap().id;
        let channel_id = check_channel_permission(
            &cmd.bot,
            channel,
            MsgTarget::new(chat_id, cmd.message_id),
            user_id,
        )
        .await?;
        if channel_id.is_none() {
            return Ok(());
        }
        target_id = channel_id.unwrap();
    }

    let feeds = db.lock().unwrap().subscribed_feeds(target_id.0);
    let msgs = if let Some(feeds) = feeds {
        format_and_split_msgs("订阅列表：".to_string(), &feeds, |feed| {
            format!(
                "{}",
                Escape(&feed.link),
                Escape(&feed.title)
            )
        })
    } else {
        vec!["订阅列表为空".to_string()]
    };

    let mut prev_msg = cmd.message_id;
    for msg in msgs {
        let text = parameters::Text::html(&msg);
        let msg = cmd
            .bot
            .send_message(chat_id, text)
            .reply_to_message_id(prev_msg)
            .call()
            .await?;
        prev_msg = msg.id;
    }
    Ok(())
}

pub async fn sub(db: Arc<Mutex<Database>>, cmd: Arc<Command<Text<Https>>>) -> anyhow::Result<()> {
    let chat_id = cmd.chat.id;
    let text = &cmd.text.value;
    let args = text.split_whitespace().collect::<Vec<_>>();
    let mut target_id = chat_id;
    let feed_url;

    match &*args {
        [url] => feed_url = url,
        [channel, url] => {
            let user_id = cmd.from.as_ref().unwrap().id;
            let channel_id = check_channel_permission(
                &cmd.bot,
                channel,
                MsgTarget::new(chat_id, cmd.message_id),
                user_id,
            )
            .await?;
            if channel_id.is_none() {
                return Ok(());
            }
            target_id = channel_id.unwrap();
            feed_url = url;
        }
        [..] => {
            return Ok(());
        }
    };
    let msg = match pull_feed(feed_url).await {
        Ok(feed) => {
            if db.lock().unwrap().subscribe(target_id.0, feed_url, &feed) {
                format!("{} 订阅成功", feed.title)
            } else {
                "已订阅过的 RSS".into()
            }
        }
        Err(e) => format!("订阅失败 {}", Escape(&e.to_string())),
    };
    update_response(
        &cmd.bot,
        MsgTarget::new(chat_id, cmd.message_id),
        parameters::Text::html(&msg),
    )
    .await?;
    Ok(())
}

pub async fn unsub(db: Arc<Mutex<Database>>, cmd: Arc<Command<Text<Https>>>) -> anyhow::Result<()> {
    let chat_id = cmd.chat.id;
    let text = &cmd.text.value;
    let args = text.split_whitespace().collect::<Vec<_>>();
    let mut target_id = chat_id;
    let feed_url;

    match &*args {
        [url] => feed_url = url,
        [channel, url] => {
            let user_id = cmd.from.as_ref().unwrap().id;
            let channel_id = check_channel_permission(
                &cmd.bot,
                channel,
                MsgTarget::new(chat_id, cmd.message_id),
                user_id,
            )
            .await?;
            if channel_id.is_none() {
                return Ok(());
            }
            target_id = channel_id.unwrap();
            feed_url = url;
        }
        [..] => {
            return Ok(());
        }
    };
    let msg = if let Some(feed) = db.lock().unwrap().unsubscribe(target_id.0, feed_url) {
        format!("{} 退订成功", feed.title)
    } else {
        "未订阅过的 RSS".into()
    };
    update_response(
        &cmd.bot,
        MsgTarget::new(chat_id, cmd.message_id),
        parameters::Text::html(&msg),
    )
    .await?;
    Ok(())
}

pub async fn export(
    db: Arc<Mutex<Database>>,
    cmd: Arc<Command<Text<Https>>>,
) -> anyhow::Result<()> {
    let chat_id = cmd.chat.id;
    let channel = &cmd.text.value;
    let mut target_id = chat_id;

    if !channel.is_empty() {
        let user_id = cmd.from.as_ref().unwrap().id;
        let channel_id = check_channel_permission(
            &cmd.bot,
            channel,
            MsgTarget::new(chat_id, cmd.message_id),
            user_id,
        )
            .await?;
        if channel_id.is_none() {
            return Ok(());
        }
        target_id = channel_id.unwrap();
    }

    let feeds = db.lock().unwrap().subscribed_feeds(target_id.0);
    if feeds.is_none() {
        update_response(
            &cmd.bot,
            MsgTarget::new(chat_id, cmd.message_id),
            parameters::Text::plain("订阅列表为空"),
        )
            .await?;
        return Ok(());
    }
    let opml = opml::into_opml(feeds.unwrap());

    cmd.bot.send_document(
        chat_id,
        input_file::Document::bytes("feeds.opml", opml.as_bytes()),
    )
           .reply_to_message_id(cmd.message_id)
           .call()
           .await?;
    Ok(())
}

async fn pull_feed(url: &str) -> anyhow::Result<RSS> {
    let mut resp = client().get(url).send().await?.error_for_status()?;
    if let Some(len) = resp.content_length() {
        if len > RESP_SIZE_LIMIT as u64 {
            return Err(anyhow::format_err!("too big"));
        }
    }
    let mut buf = Vec::new(); // TODO: capacity?
    while let Some(bytes) = resp.chunk().await? {
        if buf.len() + bytes.len() > RESP_SIZE_LIMIT {
            return Err(anyhow::format_err!("too big"));
        }
        buf.extend_from_slice(&bytes);
    }

    crate::feed::parse(std::io::Cursor::new(buf))
}

async fn update_response(
    bot: &tbot::Bot<Https>,
    target: MsgTarget,
    message: parameters::Text<'_>,
) -> Result<MsgTarget, tbot::errors::MethodCall> {
    let msg = if target.first_time {
        bot.send_message(target.chat_id, message)
            .reply_to_message_id(target.message_id)
            .call()
            .await?
    } else {
        bot.edit_message_text(target.chat_id, target.message_id, message)
            .call()
            .await?
    };
    Ok(target.update(msg.id))
}

async fn check_channel_permission(
    bot: &tbot::Bot<Https>,
    channel: &str,
    target: MsgTarget,
    user_id: tbot::types::user::Id,
) -> Result<Option<tbot::types::chat::Id>, tbot::errors::MethodCall> {
    let channel_id = channel
        .parse::<i64>()
        .map(|id| parameters::ChatId::Id(id.into()))
        .unwrap_or_else(|_| {
            if channel.starts_with('@') {
                parameters::ChatId::Username(&channel[1..])
            } else {
                parameters::ChatId::Username(channel)
            }
        });

    let chat = bot.get_chat(channel_id).call().await?;
    if !chat.kind.is_channel() {
        update_response(bot, target, parameters::Text::plain("目标需为 Channel")).await?;
        return Ok(None);
    }
    let admins = bot.get_chat_administrators(channel_id).call().await?;
    let user_is_admin = admins
        .iter()
        .find(|member| member.user.id == user_id)
        .is_some();
    if !user_is_admin {
        update_response(
            bot,
            target,
            parameters::Text::plain("该命令只能由 Channel 管理员使用"),
        )
        .await?;
        return Ok(None);
    }
    let bot_is_admin = admins
        .iter()
        .find(|member| member.user.id == *crate::BOT_ID.get().unwrap())
        .is_some();
    if !bot_is_admin {
        update_response(
            bot,
            target,
            parameters::Text::plain("请将本 Bot 设为管理员"),
        )
        .await?;
        return Ok(None);
    }
    Ok(Some(chat.id))
}

pub fn format_and_split_msgs<T, F>(head: String, data: &[T], line_format_fn: F) -> Vec<String>
where
    F: Fn(&T) -> String,
{
    let mut msgs = vec![head];
    for item in data {
        let line = line_format_fn(item);
        if msgs.last_mut().unwrap().len() + line.len() > TELEGRAM_MAX_MSG_LEN {
            msgs.push(line);
        } else {
            let msg = msgs.last_mut().unwrap();
            msg.push('\n');
            msg.push_str(&line);
        }
    }
    msgs
}

pub struct Escape<'a>(pub &'a str);

impl<'a> ::std::fmt::Display for Escape<'a> {
    fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        // https://core.telegram.org/bots/api#html-style
        let Escape(s) = *self;
        let pile_o_bits = s;
        let mut last = 0;
        for (i, ch) in s.bytes().enumerate() {
            match ch as char {
                '<' | '>' | '&' | '"' => {
                    fmt.write_str(&pile_o_bits[last..i])?;
                    let s = match ch as char {
                        '>' => ">",
                        '<' => "<",
                        '&' => "&",
                        '"' => """,
                        _ => unreachable!(),
                    };
                    fmt.write_str(s)?;
                    last = i + 1;
                }
                _ => {}
            }
        }

        if last < s.len() {
            fmt.write_str(&pile_o_bits[last..])?;
        }
        Ok(())
    }
}
<commit_msg>Fix response message<commit_after>use std::future::Future;
use std::pin::Pin;
use std::sync::Mutex;
use std::sync::{Arc, Once};
use std::time::Duration;

use reqwest;
use tbot::{
    connectors::Https,
    contexts::{Command, Text},
    types::{input_file, parameters},
};

use crate::data::{DataError, Database};
use crate::feed::RSS;

mod opml;

pub const TELEGRAM_MAX_MSG_LEN: usize = 4096;
const RESP_SIZE_LIMIT: usize = 2 * 1024 * 1024;

#[derive(Debug, Copy, Clone)]
struct MsgTarget {
    chat_id: tbot::types::chat::Id,
    message_id: tbot::types::message::Id,
    first_time: bool,
}

impl MsgTarget {
    fn new(chat_id: tbot::types::chat::Id, message_id: tbot::types::message::Id) -> Self {
        MsgTarget {
            chat_id,
            message_id,
            first_time: true,
        }
    }
    fn update(self, message_id: tbot::types::message::Id) -> Self {
        MsgTarget { message_id, ..self }
    }
}

fn client() -> Arc<reqwest::Client> {
    static mut CLIENT: Option<Arc<reqwest::Client>> = None;
    static INIT: Once = Once::new();

    INIT.call_once(|| {
        let mut headers = reqwest::header::HeaderMap::new();
        let ua = format!(
            concat!(
                env!("CARGO_PKG_NAME"),
                "/",
                env!("CARGO_PKG_VERSION"),
                " (+https://t.me/{})"
            ),
            crate::BOT_NAME.get().expect("BOT_NAME not initialized")
        );
        headers.insert(
            reqwest::header::USER_AGENT,
            reqwest::header::HeaderValue::from_str(&ua).unwrap(),
        );
        let client = reqwest::Client::builder()
            .timeout(Duration::from_secs(10))
            .default_headers(headers)
            .redirect(reqwest::redirect::Policy::limited(5))
            .build()
            .unwrap();

        unsafe {
            CLIENT = Some(Arc::new(client));
        }
    });

    unsafe { CLIENT.clone() }.unwrap()
}

pub async fn rss(db: Arc<Mutex<Database>>, cmd: Arc<Command<Text<Https>>>) -> anyhow::Result<()> {
    let chat_id = cmd.chat.id;
    let channel = &cmd.text.value;
    let mut target_id = chat_id;

    if !channel.is_empty() {
        let user_id = cmd.from.as_ref().unwrap().id;
        let channel_id = check_channel_permission(
            &cmd.bot,
            channel,
            MsgTarget::new(chat_id, cmd.message_id),
            user_id,
        )
        .await?;
        if channel_id.is_none() {
            return Ok(());
        }
        target_id = channel_id.unwrap();
    }

    let feeds = db.lock().unwrap().subscribed_feeds(target_id.0);
    let msgs = if let Some(feeds) = feeds {
        format_and_split_msgs("订阅列表：".to_string(), &feeds, |feed| {
            format!(
                "{}",
                Escape(&feed.link),
                Escape(&feed.title)
            )
        })
    } else {
        vec!["订阅列表为空".to_string()]
    };

    let mut prev_msg = cmd.message_id;
    for msg in msgs {
        let text = parameters::Text::html(&msg);
        let msg = cmd
            .bot
            .send_message(chat_id, text)
            .reply_to_message_id(prev_msg)
            .call()
            .await?;
        prev_msg = msg.id;
    }
    Ok(())
}

pub async fn sub(db: Arc<Mutex<Database>>, cmd: Arc<Command<Text<Https>>>) -> anyhow::Result<()> {
    let chat_id = cmd.chat.id;
    let text = &cmd.text.value;
    let args = text.split_whitespace().collect::<Vec<_>>();
    let mut target_id = chat_id;
    let feed_url;

    match &*args {
        [url] => feed_url = url,
        [channel, url] => {
            let user_id = cmd.from.as_ref().unwrap().id;
            let channel_id = check_channel_permission(
                &cmd.bot,
                channel,
                MsgTarget::new(chat_id, cmd.message_id),
                user_id,
            )
            .await?;
            if channel_id.is_none() {
                return Ok(());
            }
            target_id = channel_id.unwrap();
            feed_url = url;
        }
        [..] => {
            return Ok(());
        }
    };
    let msg = match pull_feed(feed_url).await {
        Ok(feed) => {
            if db.lock().unwrap().subscribe(target_id.0, feed_url, &feed) {
                format!("{} 订阅成功", feed.title)
            } else {
                "已订阅过的 RSS".into()
            }
        }
        Err(e) => format!("订阅失败 {}", Escape(&e.to_string())),
    };
    update_response(
        &cmd.bot,
        MsgTarget::new(chat_id, cmd.message_id),
        parameters::Text::html(&msg),
    )
    .await?;
    Ok(())
}

pub async fn unsub(db: Arc<Mutex<Database>>, cmd: Arc<Command<Text<Https>>>) -> anyhow::Result<()> {
    let chat_id = cmd.chat.id;
    let text = &cmd.text.value;
    let args = text.split_whitespace().collect::<Vec<_>>();
    let mut target_id = chat_id;
    let feed_url;

    match &*args {
        [url] => feed_url = url,
        [channel, url] => {
            let user_id = cmd.from.as_ref().unwrap().id;
            let channel_id = check_channel_permission(
                &cmd.bot,
                channel,
                MsgTarget::new(chat_id, cmd.message_id),
                user_id,
            )
            .await?;
            if channel_id.is_none() {
                return Ok(());
            }
            target_id = channel_id.unwrap();
            feed_url = url;
        }
        [..] => {
            return Ok(());
        }
    };
    let msg = if let Some(feed) = db.lock().unwrap().unsubscribe(target_id.0, feed_url) {
        format!("{} 退订成功", feed.title)
    } else {
        "未订阅过的 RSS".into()
    };
    update_response(
        &cmd.bot,
        MsgTarget::new(chat_id, cmd.message_id),
        parameters::Text::html(&msg),
    )
    .await?;
    Ok(())
}

pub async fn export(
    db: Arc<Mutex<Database>>,
    cmd: Arc<Command<Text<Https>>>,
) -> anyhow::Result<()> {
    let chat_id = cmd.chat.id;
    let channel = &cmd.text.value;
    let mut target_id = chat_id;

    if !channel.is_empty() {
        let user_id = cmd.from.as_ref().unwrap().id;
        let channel_id = check_channel_permission(
            &cmd.bot,
            channel,
            MsgTarget::new(chat_id, cmd.message_id),
            user_id,
        )
            .await?;
        if channel_id.is_none() {
            return Ok(());
        }
        target_id = channel_id.unwrap();
    }

    let feeds = db.lock().unwrap().subscribed_feeds(target_id.0);
    if feeds.is_none() {
        update_response(
            &cmd.bot,
            MsgTarget::new(chat_id, cmd.message_id),
            parameters::Text::plain("订阅列表为空"),
        )
            .await?;
        return Ok(());
    }
    let opml = opml::into_opml(feeds.unwrap());

    cmd.bot.send_document(
        chat_id,
        input_file::Document::bytes("feeds.opml", opml.as_bytes()),
    )
           .reply_to_message_id(cmd.message_id)
           .call()
           .await?;
    Ok(())
}

async fn pull_feed(url: &str) -> anyhow::Result<RSS> {
    let mut resp = client().get(url).send().await?.error_for_status()?;
    if let Some(len) = resp.content_length() {
        if len > RESP_SIZE_LIMIT as u64 {
            return Err(anyhow::format_err!("too big"));
        }
    }
    let mut buf = Vec::new(); // TODO: capacity?
    while let Some(bytes) = resp.chunk().await? {
        if buf.len() + bytes.len() > RESP_SIZE_LIMIT {
            return Err(anyhow::format_err!("too big"));
        }
        buf.extend_from_slice(&bytes);
    }

    crate::feed::parse(std::io::Cursor::new(buf))
}

async fn update_response(
    bot: &tbot::Bot<Https>,
    target: MsgTarget,
    message: parameters::Text<'_>,
) -> Result<MsgTarget, tbot::errors::MethodCall> {
    let msg = if target.first_time {
        bot.send_message(target.chat_id, message)
            .reply_to_message_id(target.message_id)
            .call()
            .await?
    } else {
        bot.edit_message_text(target.chat_id, target.message_id, message)
            .call()
            .await?
    };
    Ok(target.update(msg.id))
}

async fn check_channel_permission(
    bot: &tbot::Bot<Https>,
    channel: &str,
    target: MsgTarget,
    user_id: tbot::types::user::Id,
) -> Result<Option<tbot::types::chat::Id>, tbot::errors::MethodCall> {
    let channel_id = channel
        .parse::<i64>()
        .map(|id| parameters::ChatId::Id(id.into()))
        .unwrap_or_else(|_| {
            if channel.starts_with('@') {
                parameters::ChatId::Username(&channel[1..])
            } else {
                parameters::ChatId::Username(channel)
            }
        });

    let chat = bot.get_chat(channel_id).call().await?;
    if !chat.kind.is_channel() {
        update_response(bot, target, parameters::Text::plain("目标需为 Channel")).await?;
        return Ok(None);
    }
    let admins = bot.get_chat_administrators(channel_id).call().await?;
    let user_is_admin = admins
        .iter()
        .find(|member| member.user.id == user_id)
        .is_some();
    if !user_is_admin {
        update_response(
            bot,
            target,
            parameters::Text::plain("该命令只能由 Channel 管理员使用"),
        )
        .await?;
        return Ok(None);
    }
    let bot_is_admin = admins
        .iter()
        .find(|member| member.user.id == *crate::BOT_ID.get().unwrap())
        .is_some();
    if !bot_is_admin {
        update_response(
            bot,
            target,
            parameters::Text::plain("请将本 Bot 设为管理员"),
        )
        .await?;
        return Ok(None);
    }
    Ok(Some(chat.id))
}

pub fn format_and_split_msgs<T, F>(head: String, data: &[T], line_format_fn: F) -> Vec<String>
where
    F: Fn(&T) -> String,
{
    let mut msgs = vec![head];
    for item in data {
        let line = line_format_fn(item);
        if msgs.last_mut().unwrap().len() + line.len() > TELEGRAM_MAX_MSG_LEN {
            msgs.push(line);
        } else {
            let msg = msgs.last_mut().unwrap();
            msg.push('\n');
            msg.push_str(&line);
        }
    }
    msgs
}

pub struct Escape<'a>(pub &'a str);

impl<'a> ::std::fmt::Display for Escape<'a> {
    fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        // https://core.telegram.org/bots/api#html-style
        let Escape(s) = *self;
        let pile_o_bits = s;
        let mut last = 0;
        for (i, ch) in s.bytes().enumerate() {
            match ch as char {
                '<' | '>' | '&' | '"' => {
                    fmt.write_str(&pile_o_bits[last..i])?;
                    let s = match ch as char {
                        '>' => ">",
                        '<' => "<",
                        '&' => "&",
                        '"' => """,
                        _ => unreachable!(),
                    };
                    fmt.write_str(s)?;
                    last = i + 1;
                }
                _ => {}
            }
        }

        if last < s.len() {
            fmt.write_str(&pile_o_bits[last..])?;
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>extern crate rgen3_save;
extern crate rgen3_string;
extern crate rand;

use rand::{Rng, thread_rng};
use rgen3_save::{Pokemon, SaveSections, TrainerInfo};
use std::collections::HashSet;

static PREFIX_NAMES: [&'static str; 52] =
    ["Acid", "Axel", "Baal", "Bike", "Bull", "Bald", "Cave", "Diet", "Dray", "Duke", "Easy",
     "Fact", "Face", "Fowl", "Fuzz", "Goat", "Hair", "Head", "Hiss", "Idea", "Iris", "Iron",
     "Jack", "John", "Kart", "Lady", "Limb", "Lime", "Mace", "Mars", "Naga", "Nuke", "Nude",
     "Omen", "Orca", "Poke", "Pyre", "Quiz", "Risk", "Road", "Sock", "Swag", "Teal", "Tree",
     "Ugly", "Vamp", "Vibe", "Wolf", "Xray", "Yoga", "Zeta", "Zoom"];

static SUFFIX_NAMES: [&'static str; 23] = ["Adder", "Baker", "Biter", "Coder", "Curer", "Diver",
                                           "Eater", "Faker", "Flier", "Frier", "Gamer", "Gazer",
                                           "Giver", "Laser", "Lover", "Maker", "Order", "Racer",
                                           "Taker", "Tamer", "Voter", "Waker", "Zoner"];

fn gen_pokemon<R: Rng>(rng: &mut R,
                       chosen_names: &mut HashSet<String>,
                       trainer: &TrainerInfo)
                       -> Pokemon {
    let mut pokemon = Pokemon::default();
    let mut name;
    loop {
        let prefix = rng.choose(&PREFIX_NAMES).unwrap();
        let suffix = rng.choose(&SUFFIX_NAMES).unwrap();
        name = format!("{} {}", prefix, suffix);
        // Make sure we don't use the same name twice in our team
        if chosen_names.get(&name).is_none() {
            break;
        }
    }
    rgen3_string::encode_string(&name, &mut pokemon.nickname.0);
    chosen_names.insert(name);
    {
        if pokemon.active_data.is_none() {
            pokemon.active_data = Some(Default::default());
        }
        let active_data = pokemon.active_data.as_mut().unwrap();
        pokemon.personality = rng.gen();
        active_data.level = rng.gen_range(80, 100);
        active_data.total_hp = rng.gen_range(800, 999);
        active_data.current_hp = active_data.total_hp;
        active_data.attack = rng.gen_range(800, 999);
        active_data.defense = rng.gen_range(800, 999);
        active_data.speed = rng.gen_range(800, 999);
        active_data.sp_attack = rng.gen_range(800, 999);
        active_data.sp_defense = rng.gen_range(800, 999);
        pokemon.data.growth.species = rng.gen_range(0, 386);
        pokemon.data.growth.experience = std::u32::MAX / 2;
        pokemon.data.attacks.move1 = rng.gen_range(0, 354);
        pokemon.data.attacks.move2 = rng.gen_range(0, 354);
        pokemon.data.attacks.move3 = rng.gen_range(0, 354);
        pokemon.data.attacks.move4 = rng.gen_range(0, 354);
        pokemon.data.attacks.pp1 = 99;
        pokemon.data.attacks.pp2 = 99;
        pokemon.data.attacks.pp3 = 99;
        pokemon.data.attacks.pp4 = 99;
        pokemon.ot_id = trainer.full_id();
        pokemon.ot_name = trainer.name;
    }
    pokemon
}

fn main() {
    let mut args = std::env::args().skip(1);
    let path = args.next().expect("Need path to save as first arg");
    let mut save = rgen3_save::Save::load_from_file(&path).unwrap();
    let mut rng = thread_rng();
    {
        let SaveSections { trainer, team, pc_boxes } = save.sections();
        let mut chosen_names = HashSet::new();
        team.clear();
        for _ in 0..6 {
            team.push(gen_pokemon(&mut rng, &mut chosen_names, trainer));
        }
        for b in pc_boxes.iter_mut() {
            for p in &mut b.pokemon {
                *p = Some(gen_pokemon(&mut rng, &mut chosen_names, trainer));
            }
        }
    }
    save.save_to_file(path).unwrap();
}
<commit_msg>Check if set contains item more idiomatically<commit_after>extern crate rgen3_save;
extern crate rgen3_string;
extern crate rand;

use rand::{Rng, thread_rng};
use rgen3_save::{Pokemon, SaveSections, TrainerInfo};
use std::collections::HashSet;

static PREFIX_NAMES: [&'static str; 52] =
    ["Acid", "Axel", "Baal", "Bike", "Bull", "Bald", "Cave", "Diet", "Dray", "Duke", "Easy",
     "Fact", "Face", "Fowl", "Fuzz", "Goat", "Hair", "Head", "Hiss", "Idea", "Iris", "Iron",
     "Jack", "John", "Kart", "Lady", "Limb", "Lime", "Mace", "Mars", "Naga", "Nuke", "Nude",
     "Omen", "Orca", "Poke", "Pyre", "Quiz", "Risk", "Road", "Sock", "Swag", "Teal", "Tree",
     "Ugly", "Vamp", "Vibe", "Wolf", "Xray", "Yoga", "Zeta", "Zoom"];

static SUFFIX_NAMES: [&'static str; 23] = ["Adder", "Baker", "Biter", "Coder", "Curer", "Diver",
                                           "Eater", "Faker", "Flier", "Frier", "Gamer", "Gazer",
                                           "Giver", "Laser", "Lover", "Maker", "Order", "Racer",
                                           "Taker", "Tamer", "Voter", "Waker", "Zoner"];

fn gen_pokemon<R: Rng>(rng: &mut R,
                       chosen_names: &mut HashSet<String>,
                       trainer: &TrainerInfo)
                       -> Pokemon {
    let mut pokemon = Pokemon::default();
    let mut name;
    loop {
        let prefix = rng.choose(&PREFIX_NAMES).unwrap();
        let suffix = rng.choose(&SUFFIX_NAMES).unwrap();
        name = format!("{} {}", prefix, suffix);
        // Make sure we don't use the same name twice in our team
        if !chosen_names.contains(&name) {
            break;
        }
    }
    rgen3_string::encode_string(&name, &mut pokemon.nickname.0);
    chosen_names.insert(name);
    {
        if pokemon.active_data.is_none() {
            pokemon.active_data = Some(Default::default());
        }
        let active_data = pokemon.active_data.as_mut().unwrap();
        pokemon.personality = rng.gen();
        active_data.level = rng.gen_range(80, 100);
        active_data.total_hp = rng.gen_range(800, 999);
        active_data.current_hp = active_data.total_hp;
        active_data.attack = rng.gen_range(800, 999);
        active_data.defense = rng.gen_range(800, 999);
        active_data.speed = rng.gen_range(800, 999);
        active_data.sp_attack = rng.gen_range(800, 999);
        active_data.sp_defense = rng.gen_range(800, 999);
        pokemon.data.growth.species = rng.gen_range(0, 386);
        pokemon.data.growth.experience = std::u32::MAX / 2;
        pokemon.data.attacks.move1 = rng.gen_range(0, 354);
        pokemon.data.attacks.move2 = rng.gen_range(0, 354);
        pokemon.data.attacks.move3 = rng.gen_range(0, 354);
        pokemon.data.attacks.move4 = rng.gen_range(0, 354);
        pokemon.data.attacks.pp1 = 99;
        pokemon.data.attacks.pp2 = 99;
        pokemon.data.attacks.pp3 = 99;
        pokemon.data.attacks.pp4 = 99;
        pokemon.ot_id = trainer.full_id();
        pokemon.ot_name = trainer.name;
    }
    pokemon
}

fn main() {
    let mut args = std::env::args().skip(1);
    let path = args.next().expect("Need path to save as first arg");
    let mut save = rgen3_save::Save::load_from_file(&path).unwrap();
    let mut rng = thread_rng();
    {
        let SaveSections { trainer, team, pc_boxes } = save.sections();
        let mut chosen_names = HashSet::new();
        team.clear();
        for _ in 0..6 {
            team.push(gen_pokemon(&mut rng, &mut chosen_names, trainer));
        }
        for b in pc_boxes.iter_mut() {
            for p in &mut b.pokemon {
                *p = Some(gen_pokemon(&mut rng, &mut chosen_names, trainer));
            }
        }
    }
    save.save_to_file(path).unwrap();
}
<|endoftext|>


<commit_before>//! A few random tests.
//! Knowing how much I neglect this aspect of software development,
//! there probably won't be a significant coverage.

extern crate libxml;

use std::fs::File;
use std::io::Read;

use libxml::tree::{Document, Node, Namespace, NodeType};
use libxml::xpath::Context;
use libxml::parser::Parser;

#[test]
/// Build a hello world XML doc
fn hello_builder() {
  let doc_result = Document::new();
  assert!(doc_result.is_ok());
  let mut doc = doc_result.unwrap();

  let doc_node = doc.get_root_element();
  assert_eq!(doc_node.get_type(), Some(NodeType::DocumentNode));

  let hello_element_result = Node::new("hello", None, &doc);
  assert!(hello_element_result.is_ok());
  let mut hello_element = hello_element_result.unwrap();

  doc.set_root_element(&mut hello_element);

  hello_element.set_content("world!");

  let added = hello_element.new_child(None, "child");
  assert!(added.is_ok());
  let new_child = added.unwrap();

  new_child.set_content("set content");

  let node_string = doc.node_to_string(&hello_element);
  assert!(node_string.len() > 1);

  let doc_string = doc.to_string();
  assert!(doc_string.len() > 1);
  assert!(doc.save_file("tests/results/helloworld.xml").is_ok());
}

#[test]
fn create_pi() {
  let doc_result = Document::new();
  assert!(doc_result.is_ok());
  let mut doc = doc_result.unwrap();
  // Add a PI
  let node_ok = doc.create_processing_instruction("piname", "picontent");
  assert!(node_ok.is_ok());
  let doc_string = doc.to_string();
  println!("{:?}", doc_string);
  assert!(doc_string.len() > 1);
}

#[test]
/// Duplicate an xml file
fn duplicate_file() {
  let parser = Parser::default();
  {
    let doc_result = parser.parse_file("tests/resources/file01.xml");
    assert!(doc_result.is_ok());

    let doc = doc_result.unwrap();
    doc.save_file("tests/results/copy.xml").unwrap();
  }
}

#[test]
// Can parse an xml string in memory
fn can_parse_xml_string() {
  let mut file = File::open("tests/resources/file01.xml").unwrap();
  let mut xml_string = String::new();
  file.read_to_string(&mut xml_string).unwrap();
  let parser = Parser::default();
  let doc = parser.parse_string(&xml_string).unwrap();
  assert_eq!(doc.get_root_element().get_name(), "root");
}

#[test]
/// Can load an HTML file
fn can_load_html_file() {
  let parser = Parser::default_html();
  {
    let doc_result = parser.parse_file("tests/resources/example.html");
    assert!(doc_result.is_ok());

    let doc = doc_result.unwrap();
    let root = doc.get_root_element();
    assert_eq!(root.get_name(), "html");
  }
}

#[test]
/// Root node and first child of root node are different
/// (There is a tiny chance this might fail for a correct program)
fn child_of_root_has_different_hash() {
  let parser = Parser::default();
  {
    let doc_result = parser.parse_file("tests/resources/file01.xml");
    assert!(doc_result.is_ok());
    let doc = doc_result.unwrap();
    let root = doc.get_root_element();
    assert!(!root.is_text_node());
    if let Some(child) = root.get_first_child() {
      assert!(root != child);
    } else {
      assert!(false);   //test failed - child doesn't exist
    }
  }
}

#[test]
/// Siblings basic unit tests
fn sibling_unit_tests() {
  let mut doc = Document::new().unwrap();
  let hello_element_result = Node::new("hello", None, &doc);
  assert!(hello_element_result.is_ok());
  let mut hello_element = hello_element_result.unwrap();
  doc.set_root_element(&mut hello_element);

  let new_sibling = Node::new("sibling", None, &doc).unwrap();
  assert!(hello_element.add_prev_sibling(new_sibling).is_some());
}

#[test]
/// Test the evaluation of an xpath expression yields the correct number of nodes
fn test_xpath_result_number_correct() {
  let parser = Parser::default();
  let doc_result = parser.parse_file("tests/resources/file01.xml");
  assert!(doc_result.is_ok());
  let doc = doc_result.unwrap();
  let context = Context::new(&doc).unwrap();

  let result1 = context.evaluate("//child").unwrap();
  assert_eq!(result1.get_number_of_nodes(), 2);
  assert_eq!(result1.get_nodes_as_vec().len(), 2);

  let result2 = context.evaluate("//nonexistent").unwrap();
  assert_eq!(result2.get_number_of_nodes(), 0);
  assert_eq!(result2.get_nodes_as_vec().len(), 0);
}

#[test]
/// Test xpath with namespaces
fn test_xpath_with_namespaces() {
  let parser = Parser::default();
  let doc_result = parser.parse_file("tests/resources/simple_namespaces.xml");
  assert!(doc_result.is_ok());

  let doc = doc_result.unwrap();
  let context = Context::new(&doc).unwrap();
  assert!(context.register_namespace("h", "http://example.com/ns/hello").is_ok());
  assert!(context.register_namespace("f", "http://example.com/ns/farewell").is_ok());
  assert!(context.register_namespace("r", "http://example.com/ns/root").is_ok());
  let result_h_td = context.evaluate("//h:td").unwrap();
  assert_eq!(result_h_td.get_number_of_nodes(), 3);
  assert_eq!(result_h_td.get_nodes_as_vec().len(), 3);

  let result_h_table = context.evaluate("//h:table").unwrap();
  assert_eq!(result_h_table.get_number_of_nodes(), 2);
  assert_eq!(result_h_table.get_nodes_as_vec().len(), 2);

  let result_f_footer = context.evaluate("//f:footer").unwrap();
  assert_eq!(result_f_footer.get_number_of_nodes(), 2);
  assert_eq!(result_f_footer.get_nodes_as_vec().len(), 2);

  let result_r = context.evaluate("//r:*").unwrap();
  assert_eq!(result_r.get_number_of_nodes(), 1);
  assert_eq!(result_r.get_nodes_as_vec().len(), 1);

  let result_h = context.evaluate("//h:*").unwrap();
  assert_eq!(result_h.get_number_of_nodes(), 7);
  assert_eq!(result_h.get_nodes_as_vec().len(), 7);

  let result_f = context.evaluate("//f:*").unwrap();
  assert_eq!(result_f.get_number_of_nodes(), 4);
  assert_eq!(result_f.get_nodes_as_vec().len(), 4);

  let result_all = context.evaluate("//*").unwrap();
  assert_eq!(result_all.get_number_of_nodes(), 12);
  assert_eq!(result_all.get_nodes_as_vec().len(), 12);

}

#[test]
/// Test that an xpath expression finds the correct node and
/// that the class names are interpreted correctly.
fn test_class_names() {
  let parser = Parser::default_html();
  let doc_result = parser.parse_file("tests/resources/file02.xml");
  assert!(doc_result.is_ok());
  let doc = doc_result.unwrap();
  let context = Context::new(&doc).unwrap();

  let p_result = context.evaluate("/html/body/p");
  assert!(p_result.is_ok());
  let p = p_result.unwrap();
  assert_eq!(p.get_number_of_nodes(), 1);

  let node = &p.get_nodes_as_vec()[0];
  let names = node.get_class_names();
  assert_eq!(names.len(), 2);
  assert!(names.contains("paragraph"));
  assert!(names.contains("important"));
  assert!(!names.contains("nonsense"));
}

#[test]
/// Test that an xpath string() function processed correctly
fn test_xpath_string_function() {
  let parser = Parser::default_html();
  let doc_result = parser.parse_file("tests/resources/file01.xml");
  assert!(doc_result.is_ok());
  let doc = doc_result.unwrap();
  let context = Context::new(&doc).unwrap();

  let p_result = context.evaluate("string(//root//child[1]/@attribute)");
  assert!(p_result.is_ok());
  let p = p_result.unwrap();
  // Not a node really
  assert_eq!(p.get_number_of_nodes(), 0);
  let content = p.to_string();
  assert_eq!(content, "value");
}

#[test]
/// Test well-formedness of a Rust string
/// IMPORTANT: Currenlty NOT THREAD-SAFE, use in single-threaded apps only!
fn test_well_formed_html() {
  let parser = Parser::default_html();

  let trivial_well_formed = parser.is_well_formed_html("<!DOCTYPE html>\n");
  assert!(trivial_well_formed);

  let trivial_ill_formed = parser.is_well_formed_html("garbage");
  assert!(!trivial_ill_formed);

  let should_ill_formed = parser.is_well_formed_html("<broken <markup>> </boom>");
  assert!(!should_ill_formed);

  let should_well_formed = parser.is_well_formed_html("<!DOCTYPE html>\n<title>Test</title>\n
Tiny
<math><mn>2</mn></math>");
  assert!(should_well_formed);
}

#[test]
/// Can mock a node object (useful for defaults that will be overridden)
fn test_can_mock_node() {
  let node_mock = Node::mock();
  assert!(!node_mock.is_text_node());
}

#[test]
/// Can make a mock node hashable
fn test_can_hash_mock_node() {
  let node_mock = Node::mock();
  assert!(node_mock.to_hashable() > 0);
}

#[test]
/// Can set and get attributes
fn test_can_manage_attributes() {
  let mut doc = Document::new().unwrap();
  let hello_element_result = Node::new("hello", None, &doc);
  assert!(hello_element_result.is_ok());
  let mut hello_element = hello_element_result.unwrap();
  doc.set_root_element(&mut hello_element);

  let key = "examplekey";
  let value = "examplevalue";
  let pre_value = hello_element.get_attribute(key);
  assert_eq!(pre_value, None);
  let pre_prop_value = hello_element.get_property(key);
  assert_eq!(pre_prop_value, None);

  hello_element.set_attribute(key, value);
  let new_value = hello_element.get_attribute(key);
  assert_eq!(new_value, Some(value.to_owned()));
}

#[test]
/// Basic namespace workflow
fn test_can_work_with_namespaces() {
  let mut doc = Document::new().unwrap();
  let root_node = Node::new("root", None, &doc).unwrap();
  doc.set_root_element(&root_node);

  let initial_namespace_list = root_node.get_namespaces(&doc);
  assert_eq!(initial_namespace_list.len(), 0);

  let mock_ns_result = Namespace::new("mock", "http://example.com/ns/mock", root_node.clone());
  assert!(mock_ns_result.is_ok());
  // try to attach this namespace to a node
  root_node.set_namespace(mock_ns_result.unwrap());

  // now get all namespaces for the node and check we have ours
  let namespace_list = root_node.get_namespaces(&doc);
  assert_eq!(namespace_list.len(), 1);

  // let namespace = namespace_list.pop();

}<commit_msg>needless print in test<commit_after>//! A few random tests.
//! Knowing how much I neglect this aspect of software development,
//! there probably won't be a significant coverage.

extern crate libxml;

use std::fs::File;
use std::io::Read;

use libxml::tree::{Document, Node, Namespace, NodeType};
use libxml::xpath::Context;
use libxml::parser::Parser;

#[test]
/// Build a hello world XML doc
fn hello_builder() {
  let doc_result = Document::new();
  assert!(doc_result.is_ok());
  let mut doc = doc_result.unwrap();

  let doc_node = doc.get_root_element();
  assert_eq!(doc_node.get_type(), Some(NodeType::DocumentNode));

  let hello_element_result = Node::new("hello", None, &doc);
  assert!(hello_element_result.is_ok());
  let mut hello_element = hello_element_result.unwrap();

  doc.set_root_element(&mut hello_element);

  hello_element.set_content("world!");

  let added = hello_element.new_child(None, "child");
  assert!(added.is_ok());
  let new_child = added.unwrap();

  new_child.set_content("set content");

  let node_string = doc.node_to_string(&hello_element);
  assert!(node_string.len() > 1);

  let doc_string = doc.to_string();
  assert!(doc_string.len() > 1);
  assert!(doc.save_file("tests/results/helloworld.xml").is_ok());
}

#[test]
fn create_pi() {
  let doc_result = Document::new();
  assert!(doc_result.is_ok());
  let mut doc = doc_result.unwrap();
  // Add a PI
  let node_ok = doc.create_processing_instruction("piname", "picontent");
  assert!(node_ok.is_ok());
  let doc_string = doc.to_string();
  assert!(doc_string.len() > 1);
}

#[test]
/// Duplicate an xml file
fn duplicate_file() {
  let parser = Parser::default();
  {
    let doc_result = parser.parse_file("tests/resources/file01.xml");
    assert!(doc_result.is_ok());

    let doc = doc_result.unwrap();
    doc.save_file("tests/results/copy.xml").unwrap();
  }
}

#[test]
// Can parse an xml string in memory
fn can_parse_xml_string() {
  let mut file = File::open("tests/resources/file01.xml").unwrap();
  let mut xml_string = String::new();
  file.read_to_string(&mut xml_string).unwrap();
  let parser = Parser::default();
  let doc = parser.parse_string(&xml_string).unwrap();
  assert_eq!(doc.get_root_element().get_name(), "root");
}

#[test]
/// Can load an HTML file
fn can_load_html_file() {
  let parser = Parser::default_html();
  {
    let doc_result = parser.parse_file("tests/resources/example.html");
    assert!(doc_result.is_ok());

    let doc = doc_result.unwrap();
    let root = doc.get_root_element();
    assert_eq!(root.get_name(), "html");
  }
}

#[test]
/// Root node and first child of root node are different
/// (There is a tiny chance this might fail for a correct program)
fn child_of_root_has_different_hash() {
  let parser = Parser::default();
  {
    let doc_result = parser.parse_file("tests/resources/file01.xml");
    assert!(doc_result.is_ok());
    let doc = doc_result.unwrap();
    let root = doc.get_root_element();
    assert!(!root.is_text_node());
    if let Some(child) = root.get_first_child() {
      assert!(root != child);
    } else {
      assert!(false);   //test failed - child doesn't exist
    }
  }
}

#[test]
/// Siblings basic unit tests
fn sibling_unit_tests() {
  let mut doc = Document::new().unwrap();
  let hello_element_result = Node::new("hello", None, &doc);
  assert!(hello_element_result.is_ok());
  let mut hello_element = hello_element_result.unwrap();
  doc.set_root_element(&mut hello_element);

  let new_sibling = Node::new("sibling", None, &doc).unwrap();
  assert!(hello_element.add_prev_sibling(new_sibling).is_some());
}

#[test]
/// Test the evaluation of an xpath expression yields the correct number of nodes
fn test_xpath_result_number_correct() {
  let parser = Parser::default();
  let doc_result = parser.parse_file("tests/resources/file01.xml");
  assert!(doc_result.is_ok());
  let doc = doc_result.unwrap();
  let context = Context::new(&doc).unwrap();

  let result1 = context.evaluate("//child").unwrap();
  assert_eq!(result1.get_number_of_nodes(), 2);
  assert_eq!(result1.get_nodes_as_vec().len(), 2);

  let result2 = context.evaluate("//nonexistent").unwrap();
  assert_eq!(result2.get_number_of_nodes(), 0);
  assert_eq!(result2.get_nodes_as_vec().len(), 0);
}

#[test]
/// Test xpath with namespaces
fn test_xpath_with_namespaces() {
  let parser = Parser::default();
  let doc_result = parser.parse_file("tests/resources/simple_namespaces.xml");
  assert!(doc_result.is_ok());

  let doc = doc_result.unwrap();
  let context = Context::new(&doc).unwrap();
  assert!(context.register_namespace("h", "http://example.com/ns/hello").is_ok());
  assert!(context.register_namespace("f", "http://example.com/ns/farewell").is_ok());
  assert!(context.register_namespace("r", "http://example.com/ns/root").is_ok());
  let result_h_td = context.evaluate("//h:td").unwrap();
  assert_eq!(result_h_td.get_number_of_nodes(), 3);
  assert_eq!(result_h_td.get_nodes_as_vec().len(), 3);

  let result_h_table = context.evaluate("//h:table").unwrap();
  assert_eq!(result_h_table.get_number_of_nodes(), 2);
  assert_eq!(result_h_table.get_nodes_as_vec().len(), 2);

  let result_f_footer = context.evaluate("//f:footer").unwrap();
  assert_eq!(result_f_footer.get_number_of_nodes(), 2);
  assert_eq!(result_f_footer.get_nodes_as_vec().len(), 2);

  let result_r = context.evaluate("//r:*").unwrap();
  assert_eq!(result_r.get_number_of_nodes(), 1);
  assert_eq!(result_r.get_nodes_as_vec().len(), 1);

  let result_h = context.evaluate("//h:*").unwrap();
  assert_eq!(result_h.get_number_of_nodes(), 7);
  assert_eq!(result_h.get_nodes_as_vec().len(), 7);

  let result_f = context.evaluate("//f:*").unwrap();
  assert_eq!(result_f.get_number_of_nodes(), 4);
  assert_eq!(result_f.get_nodes_as_vec().len(), 4);

  let result_all = context.evaluate("//*").unwrap();
  assert_eq!(result_all.get_number_of_nodes(), 12);
  assert_eq!(result_all.get_nodes_as_vec().len(), 12);

}

#[test]
/// Test that an xpath expression finds the correct node and
/// that the class names are interpreted correctly.
fn test_class_names() {
  let parser = Parser::default_html();
  let doc_result = parser.parse_file("tests/resources/file02.xml");
  assert!(doc_result.is_ok());
  let doc = doc_result.unwrap();
  let context = Context::new(&doc).unwrap();

  let p_result = context.evaluate("/html/body/p");
  assert!(p_result.is_ok());
  let p = p_result.unwrap();
  assert_eq!(p.get_number_of_nodes(), 1);

  let node = &p.get_nodes_as_vec()[0];
  let names = node.get_class_names();
  assert_eq!(names.len(), 2);
  assert!(names.contains("paragraph"));
  assert!(names.contains("important"));
  assert!(!names.contains("nonsense"));
}

#[test]
/// Test that an xpath string() function processed correctly
fn test_xpath_string_function() {
  let parser = Parser::default_html();
  let doc_result = parser.parse_file("tests/resources/file01.xml");
  assert!(doc_result.is_ok());
  let doc = doc_result.unwrap();
  let context = Context::new(&doc).unwrap();

  let p_result = context.evaluate("string(//root//child[1]/@attribute)");
  assert!(p_result.is_ok());
  let p = p_result.unwrap();
  // Not a node really
  assert_eq!(p.get_number_of_nodes(), 0);
  let content = p.to_string();
  assert_eq!(content, "value");
}

#[test]
/// Test well-formedness of a Rust string
/// IMPORTANT: Currenlty NOT THREAD-SAFE, use in single-threaded apps only!
fn test_well_formed_html() {
  let parser = Parser::default_html();

  let trivial_well_formed = parser.is_well_formed_html("<!DOCTYPE html>\n");
  assert!(trivial_well_formed);

  let trivial_ill_formed = parser.is_well_formed_html("garbage");
  assert!(!trivial_ill_formed);

  let should_ill_formed = parser.is_well_formed_html("<broken <markup>> </boom>");
  assert!(!should_ill_formed);

  let should_well_formed = parser.is_well_formed_html("<!DOCTYPE html>\n<title>Test</title>\n
Tiny
<math><mn>2</mn></math>");
  assert!(should_well_formed);
}

#[test]
/// Can mock a node object (useful for defaults that will be overridden)
fn test_can_mock_node() {
  let node_mock = Node::mock();
  assert!(!node_mock.is_text_node());
}

#[test]
/// Can make a mock node hashable
fn test_can_hash_mock_node() {
  let node_mock = Node::mock();
  assert!(node_mock.to_hashable() > 0);
}

#[test]
/// Can set and get attributes
fn test_can_manage_attributes() {
  let mut doc = Document::new().unwrap();
  let hello_element_result = Node::new("hello", None, &doc);
  assert!(hello_element_result.is_ok());
  let mut hello_element = hello_element_result.unwrap();
  doc.set_root_element(&mut hello_element);

  let key = "examplekey";
  let value = "examplevalue";
  let pre_value = hello_element.get_attribute(key);
  assert_eq!(pre_value, None);
  let pre_prop_value = hello_element.get_property(key);
  assert_eq!(pre_prop_value, None);

  hello_element.set_attribute(key, value);
  let new_value = hello_element.get_attribute(key);
  assert_eq!(new_value, Some(value.to_owned()));
}

#[test]
/// Basic namespace workflow
fn test_can_work_with_namespaces() {
  let mut doc = Document::new().unwrap();
  let root_node = Node::new("root", None, &doc).unwrap();
  doc.set_root_element(&root_node);

  let initial_namespace_list = root_node.get_namespaces(&doc);
  assert_eq!(initial_namespace_list.len(), 0);

  let mock_ns_result = Namespace::new("mock", "http://example.com/ns/mock", root_node.clone());
  assert!(mock_ns_result.is_ok());
  // try to attach this namespace to a node
  root_node.set_namespace(mock_ns_result.unwrap());

  // now get all namespaces for the node and check we have ours
  let namespace_list = root_node.get_namespaces(&doc);
  assert_eq!(namespace_list.len(), 1);

  // let namespace = namespace_list.pop();

}<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

trait MyTrait {
    const C: bool;
    //~^ associated constants are experimental
    //~| add #![feature(associated_consts)] to the crate attributes to enable
}

struct Foo;

impl Foo {
    const C: bool = true;
    //~^ associated constants are experimental
    //~| add #![feature(associated_consts)] to the crate attributes to enable
}
<commit_msg>Make a test compatible with the beta channel<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

trait MyTrait {
    const C: bool;
    //~^ associated constants are experimental
}

struct Foo;

impl Foo {
    const C: bool = true;
    //~^ associated constants are experimental
}
<|endoftext|>


<commit_before>#[feature(globs)];

extern mod wx;

use std::rt::start_on_main_thread;
use std::vec;

use wx::native::*;
use wx::wrapper::*;

static nullptr: *u8 = 0 as *u8;

#[start]
fn start(argc: int, argv: **u8, crate_map: *u8) -> int {
    start_on_main_thread(argc, argv, crate_map, on_main)
}

#[fixed_stack_segment]
#[inline(never)]
fn on_main() {
    let closure = wxClosure::new(wx_main as *u8, nullptr);
    let args: ~[*i32] = ~[];
    ELJApp::initializeC(closure, args.len() as i32, vec::raw::to_ptr(args) as *i32);
}

extern "C"
fn wx_main() {
    unsafe {
        // not mandatory
        // ELJApp::initAllImageHandlers();
        let idAny = -1;
        let defaultFrameStyle = 536878656 | 4194304;

        let frame = wxFrame::new(wxWindow(nullptr), idAny, "Hello, wxRust!", -1, -1, -1, -1, defaultFrameStyle);
        println("OK");

        let menubar = wxMenuBar::new(0);

        let fileMenu = wxMenu::new("", 0);
        let fileNew = wxMenuItem::newEx(idAny, "New", "Create a new file.", 0, wxMenu(nullptr));
        fileMenu.appendItem(fileNew);

        menubar.append(fileMenu, "File");

        frame.setMenuBar(menubar);

        let id = 30;
        let button = wxButton::new(frame, id, "Push me!", 10, 10, 50, 30, 0);
        fn button_clicked(fun: *u8, data: *u8, evt: *u8) {
            println("hello!");
            let frame = wxFrame(data);
            let msgDlg = wxMessageDialog::new(frame, "Pushed!!", "The Button", 0);
            msgDlg.showModal();
        }
        let closure = wxClosure::new(button_clicked as *u8, frame.handle());
        button.connect(id, id, expEVT_COMMAND_BUTTON_CLICKED(), closure.handle());

        frame.show();
        frame.raise();
    }
}

<commit_msg>Refactor test.rs.<commit_after>#[feature(globs)];

extern mod wx;

use std::libc::*;
use std::rt::start_on_main_thread;
use std::vec;

use wx::native::*;
use wx::wrapper::*;

static nullptr: *u8 = 0 as *u8;
static idAny: c_int = -1;

#[start]
fn start(argc: int, argv: **u8, crate_map: *u8) -> int {
    start_on_main_thread(argc, argv, crate_map, on_main)
}

#[fixed_stack_segment]
#[inline(never)]
fn on_main() {
    let closure = wxClosure::new(wx_main as *u8, nullptr);
    let args: ~[*i32] = ~[];
    ELJApp::initializeC(closure, args.len() as i32, vec::raw::to_ptr(args) as *i32);
}

extern "C"
fn wx_main() {
    let frame = MyFrame::new();
    frame.show();
    frame.raise();
}

struct MyFrame(wxFrame);
impl MyFrame {
    #[fixed_stack_segment]
    #[inline(never)]
    fn new() -> MyFrame {
        let defaultFrameStyle = 536878656 | 4194304;

        let frame = wxFrame::new(wxWindow(nullptr), idAny, "Hello, wxRust!", -1, -1, -1, -1, defaultFrameStyle);
        frame.setMenuBar(MyMenuBar::new().asMenuBar());

        MyButton::new(frame);

        MyFrame(frame)
    }
}

struct MyMenuBar(wxMenuBar);
impl MyMenuBar {
    #[fixed_stack_segment]
    #[inline(never)]
    fn new() -> MyMenuBar {
        let menubar = wxMenuBar::new(0);

        let fileMenu = wxMenu::new("", 0);
        let fileNew = wxMenuItem::newEx(idAny, "New", "Create a new file.", 0, wxMenu(nullptr));
        fileMenu.appendItem(fileNew);

        menubar.append(fileMenu, "File");
        MyMenuBar(menubar)
    }
    fn asMenuBar(&self) -> wxMenuBar {
        return **self;
    }
}

struct MyButton(wxButton);
impl MyButton {
    #[fixed_stack_segment]
    #[inline(never)]
    fn new<T: _wxWindow>(parent: T) -> MyButton {
        // XXX: I don't want passing parent ownership.
        let button = wxButton::new(wxFrame(parent.handle()), idAny, "Push me!", 10, 10, 50, 30, 0);
        let closure = wxClosure::new(MyButton::clicked as *u8, parent.handle());
        unsafe {
            button.connect(idAny, idAny, expEVT_COMMAND_BUTTON_CLICKED(), closure.handle());
        }

        MyButton(button)
    }
    #[fixed_stack_segment]
    #[inline(never)]
    fn clicked(fun: *u8, data: *u8, evt: *u8) {
        println("hello!");
        let parent = wxWindow(data);
        let msgDlg = wxMessageDialog::new(parent, "Pushed!!", "The Button", 0);
        msgDlg.showModal();
    }
}
<|endoftext|>