<commit_before>pub trait XmlStr {
    fn end_of_attribute(&self, quote: &str) -> Option<uint>;
    fn end_of_literal(&self, expected: &str) -> Option<uint>;
    fn end_of_version_num(&self) -> Option<uint>;
    fn end_of_char_data(&self) -> Option<uint>;
    fn end_of_cdata(&self) -> Option<uint>;
    fn end_of_decimal_chars(&self) -> Option<uint>;
    fn end_of_hex_chars(&self) -> Option<uint>;
    fn end_of_comment(&self) -> Option<uint>;
    fn end_of_pi_value(&self) -> Option<uint>;
    fn end_of_start_rest(&self, is_first: |char| -> bool, is_rest: |char| -> bool) -> Option<uint>;
    fn end_of_name(&self) -> Option<uint>;
    fn end_of_space(&self) -> Option<uint>;
    fn end_of_start_tag(&self) -> Option<uint>;
}

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

        let (quote_char, _) = quote.slice_shift_char();
        let quote_char = quote_char.expect("Cant have null quote");

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

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

    fn end_of_literal(&self, expected: &str) -> Option<uint> {
        if self.starts_with(expected) {
            Some(expected.len())
        } else {
            None
        }
    }

    fn end_of_version_num(&self) -> Option<uint> {
        if self.starts_with("1.") {
            let mut positions = self.char_indices().peekable();
            positions.next();
            positions.next();

            // Need at least one character
            match positions.peek() {
                Some(&(_, c)) if c.is_decimal_char() => {},
                _ => return None,
            };

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

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

        // Using a hex literal because emacs' rust-mode doesn't
        // understand ] in a char literal. :-(
        let mut positions = self.char_indices().skip_while(|&(_, c)| c != '<' && c != '&' && c != '\x5d');

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

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

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

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

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

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

    fn end_of_start_rest(&self,
                         is_first: |char| -> bool,
                         is_rest: |char| -> bool)
                         -> Option<uint>
    {
        let mut positions = self.char_indices();

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

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

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

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

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

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

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

trait XmlChar {
    fn is_name_start_char(&self) -> bool;
    fn is_name_char(&self) -> bool;
    fn is_space_char(&self) -> bool;
    fn is_decimal_char(&self) -> bool;
    fn is_hex_char(&self) -> bool;
}

impl XmlChar for char {
    fn is_name_start_char(&self) -> bool {
        match *self {
            ':'                        |
            'A'..'Z'                   |
            '_'                        |
            'a'..'z'                   |
            '\U000000C0'..'\U000000D6' |
            '\U000000D8'..'\U000000F6' |
            '\U000000F8'..'\U000002FF' |
            '\U00000370'..'\U0000037D' |
            '\U0000037F'..'\U00001FFF' |
            '\U0000200C'..'\U0000200D' |
            '\U00002070'..'\U0000218F' |
            '\U00002C00'..'\U00002FEF' |
            '\U00003001'..'\U0000D7FF' |
            '\U0000F900'..'\U0000FDCF' |
            '\U0000FDF0'..'\U0000FFFD' |
            '\U00010000'..'\U000EFFFF' => true,
            _ => false,
        }
    }

    fn is_name_char(&self) -> bool {
        if self.is_name_start_char() { return true; }
        match *self {
            '-'                |
            '.'                |
            '0'..'9'           |
            '\u00B7'           |
            '\u0300'..'\u036F' |
            '\u203F'..'\u2040' => true,
            _ => false
        }
    }

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

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

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

#[cfg(test)]
mod test {

use super::XmlStr;

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

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

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

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

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

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

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

}
<commit_msg>Update range syntax from Rust update<commit_after>pub trait XmlStr {
    fn end_of_attribute(&self, quote: &str) -> Option<uint>;
    fn end_of_literal(&self, expected: &str) -> Option<uint>;
    fn end_of_version_num(&self) -> Option<uint>;
    fn end_of_char_data(&self) -> Option<uint>;
    fn end_of_cdata(&self) -> Option<uint>;
    fn end_of_decimal_chars(&self) -> Option<uint>;
    fn end_of_hex_chars(&self) -> Option<uint>;
    fn end_of_comment(&self) -> Option<uint>;
    fn end_of_pi_value(&self) -> Option<uint>;
    fn end_of_start_rest(&self, is_first: |char| -> bool, is_rest: |char| -> bool) -> Option<uint>;
    fn end_of_name(&self) -> Option<uint>;
    fn end_of_space(&self) -> Option<uint>;
    fn end_of_start_tag(&self) -> Option<uint>;
}

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

        let (quote_char, _) = quote.slice_shift_char();
        let quote_char = quote_char.expect("Cant have null quote");

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

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

    fn end_of_literal(&self, expected: &str) -> Option<uint> {
        if self.starts_with(expected) {
            Some(expected.len())
        } else {
            None
        }
    }

    fn end_of_version_num(&self) -> Option<uint> {
        if self.starts_with("1.") {
            let mut positions = self.char_indices().peekable();
            positions.next();
            positions.next();

            // Need at least one character
            match positions.peek() {
                Some(&(_, c)) if c.is_decimal_char() => {},
                _ => return None,
            };

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

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

        // Using a hex literal because emacs' rust-mode doesn't
        // understand ] in a char literal. :-(
        let mut positions = self.char_indices().skip_while(|&(_, c)| c != '<' && c != '&' && c != '\x5d');

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

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

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

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

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

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

    fn end_of_start_rest(&self,
                         is_first: |char| -> bool,
                         is_rest: |char| -> bool)
                         -> Option<uint>
    {
        let mut positions = self.char_indices();

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

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

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

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

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

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

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

trait XmlChar {
    fn is_name_start_char(&self) -> bool;
    fn is_name_char(&self) -> bool;
    fn is_space_char(&self) -> bool;
    fn is_decimal_char(&self) -> bool;
    fn is_hex_char(&self) -> bool;
}

impl XmlChar for char {
    fn is_name_start_char(&self) -> bool {
        match *self {
            ':'                         |
            'A'...'Z'                   |
            '_'                         |
            'a'...'z'                   |
            '\U000000C0'...'\U000000D6' |
            '\U000000D8'...'\U000000F6' |
            '\U000000F8'...'\U000002FF' |
            '\U00000370'...'\U0000037D' |
            '\U0000037F'...'\U00001FFF' |
            '\U0000200C'...'\U0000200D' |
            '\U00002070'...'\U0000218F' |
            '\U00002C00'...'\U00002FEF' |
            '\U00003001'...'\U0000D7FF' |
            '\U0000F900'...'\U0000FDCF' |
            '\U0000FDF0'...'\U0000FFFD' |
            '\U00010000'...'\U000EFFFF' => true,
            _ => false,
        }
    }

    fn is_name_char(&self) -> bool {
        if self.is_name_start_char() { return true; }
        match *self {
            '-'                 |
            '.'                 |
            '0'...'9'           |
            '\u00B7'            |
            '\u0300'...'\u036F' |
            '\u203F'...'\u2040' => true,
            _ => false
        }
    }

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

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

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

#[cfg(test)]
mod test {

use super::XmlStr;

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

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

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

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

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

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

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

}
<|endoftext|>


<commit_before>// Copyright 2013 The rust-pcre authors.
//
// 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.

// This is a port of the pcre project's `pcredemo` sample using rust-pcre bindings.

extern mod extra;
extern mod pcre;

use extra::enum_set::{EnumSet};
use extra::getopts::{Opt, getopts, optflag};
use extra::treemap::{TreeMap};
use pcre::{CompileOption, Match, Pcre, pcre_version};
use std::os;

fn print_usage(program: &str, opts: &[Opt]) {
    drop(opts);
    println!("Usage: {} [options] pattern subject", program);
    println!("{}", "Options:");
    println!("{}", "    -g                  Find all matches");
    println!("{}", "    -h, --help          Print usage and exit");
    println!("{}", "    --version           Print version information and exit");
}

fn print_version_info() {
    println!("rust-pcre 0.1 compiled against libpcre {}", pcre_version());
}

fn print_match(m: &Match, name_table: &TreeMap<~str, ~[uint]>) {
    println!("Match succeeded at offset {:u}", m.group_start(0u));

    // Show captured substrings by number.
    let mut i = 0u;
    while i < m.string_count() {
        println!("{:2u}: {:s}", i, m.group(i));
        i += 1;
    }

    let name_count = name_table.len();
    if name_count <= 0 {
        println!("{}", "No named substrings");
    } else {
        println!("{}", "Named substrings:");
        for (name, n_vec) in name_table.iter() {
            for n in n_vec.iter() {
                println!("({:u}) {:s}: {:s}", *n, *name, m.group(*n));
            }
        }
    }
}

fn main() {
    let args = os::args();
    let program = args[0].clone();

    let opts = ~[
        optflag("g"),
        optflag("h"),
        optflag("help"),
        optflag("version")
    ];

    let opt_matches = match getopts(args.tail(), opts) {
        Ok(m)  => m,
        Err(f) => {
            println!("Error: {}", f.to_err_msg());
            os::set_exit_status(1);
            return;
        }
    };

    if opt_matches.opt_present("h") || opt_matches.opt_present("help") {
        print_usage(program, opts);
        return;
    }

    if opt_matches.opt_present("version") {
        print_version_info();
        return;
    }

    let find_all = opt_matches.opt_present("g");
    if opt_matches.free.len() == 0 {
        println!("{}", "Error: No pattern");
        os::set_exit_status(1);
        return;
    } else if opt_matches.free.len() == 1 {
        println!("{}", "Error: No subject");
        os::set_exit_status(1);
        return;
    } else if opt_matches.free.len() > 2 {
        println!("{}", "Error: Too many command line arguments");
        os::set_exit_status(1);
        return;
    }

    let pattern = opt_matches.free[0].clone();
    let subject = opt_matches.free[1].clone();

    let mut compile_options: EnumSet<CompileOption> = EnumSet::empty();
    compile_options.add(pcre::DupNames);
    let re = match Pcre::compile_with_options(pattern, &compile_options) {
        Err(err) => {
            println!("Error: The pattern could not be compiled: {:s}", err.to_str());
            os::set_exit_status(1);
            return;
        },
        Ok(re) => re
    };
    let name_table = re.name_table();

    let opt_m = re.exec(subject);
    let m = match opt_m {
        None => {
            println!("{}", "No match");
            os::set_exit_status(1);
            return;
        }
        Some(m) => m
    };
    print_match(&m, &name_table);

    if find_all {
        let mut start_offset = m.group_end(0);
        loop {
            let opt_m = re.exec_from(subject, start_offset);
            let m = match opt_m {
                None => {
                    println!("{}", "\nNo more matches");
                    return;
                }
                Some(m) => m
            };

            println!("{}", "");
            print_match(&m, &name_table);

            start_offset = m.group_end(0);
        }
    }
}
<commit_msg>Clean up println!()s<commit_after>// Copyright 2013 The rust-pcre authors.
//
// 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.

// This is a port of the pcre project's `pcredemo` sample using rust-pcre bindings.

extern mod extra;
extern mod pcre;

use extra::enum_set::{EnumSet};
use extra::getopts::{Opt, getopts, optflag};
use extra::treemap::{TreeMap};
use pcre::{CompileOption, Match, Pcre, pcre_version};
use std::os;

fn print_usage(program: &str, opts: &[Opt]) {
    drop(opts);
    println!("Usage: {} [options] pattern subject", program);
    println!("Options:");
    println!("    -g                  Find all matches");
    println!("    -h, --help          Print usage and exit");
    println!("    --version           Print version information and exit");
}

fn print_version_info() {
    println!("rust-pcre 0.1 compiled against libpcre {}", pcre_version());
}

fn print_match(m: &Match, name_table: &TreeMap<~str, ~[uint]>) {
    println!("Match succeeded at offset {:u}", m.group_start(0u));

    // Show captured substrings by number.
    let mut i = 0u;
    while i < m.string_count() {
        println!("{:2u}: {:s}", i, m.group(i));
        i += 1;
    }

    let name_count = name_table.len();
    if name_count <= 0 {
        println!("No named substrings");
    } else {
        println!("Named substrings:");
        for (name, n_vec) in name_table.iter() {
            for n in n_vec.iter() {
                println!("({:u}) {:s}: {:s}", *n, *name, m.group(*n));
            }
        }
    }
}

fn main() {
    let args = os::args();
    let program = args[0].clone();

    let opts = ~[
        optflag("g"),
        optflag("h"),
        optflag("help"),
        optflag("version")
    ];

    let opt_matches = match getopts(args.tail(), opts) {
        Ok(m)  => m,
        Err(f) => {
            println!("Error: {}", f.to_err_msg());
            os::set_exit_status(1);
            return;
        }
    };

    if opt_matches.opt_present("h") || opt_matches.opt_present("help") {
        print_usage(program, opts);
        return;
    }

    if opt_matches.opt_present("version") {
        print_version_info();
        return;
    }

    let find_all = opt_matches.opt_present("g");
    if opt_matches.free.len() == 0 {
        println!("Error: No pattern");
        os::set_exit_status(1);
        return;
    } else if opt_matches.free.len() == 1 {
        println!("Error: No subject");
        os::set_exit_status(1);
        return;
    } else if opt_matches.free.len() > 2 {
        println!("Error: Too many command line arguments");
        os::set_exit_status(1);
        return;
    }

    let pattern = opt_matches.free[0].clone();
    let subject = opt_matches.free[1].clone();

    let mut compile_options: EnumSet<CompileOption> = EnumSet::empty();
    compile_options.add(pcre::DupNames);
    let re = match Pcre::compile_with_options(pattern, &compile_options) {
        Err(err) => {
            println!("Error: The pattern could not be compiled: {:s}", err.to_str());
            os::set_exit_status(1);
            return;
        },
        Ok(re) => re
    };
    let name_table = re.name_table();

    let opt_m = re.exec(subject);
    let m = match opt_m {
        None => {
            println!("No match");
            os::set_exit_status(1);
            return;
        }
        Some(m) => m
    };
    print_match(&m, &name_table);

    if find_all {
        let mut start_offset = m.group_end(0);
        loop {
            let opt_m = re.exec_from(subject, start_offset);
            let m = match opt_m {
                None => {
                    println!("\nNo more matches");
                    return;
                }
                Some(m) => m
            };

            println!("");
            print_match(&m, &name_table);

            start_offset = m.group_end(0);
        }
    }
}
<|endoftext|>


<commit_before>use prolog::and_stack::*;
use prolog::instructions::*;

use std::ops::IndexMut;

type Trail = Vec<(Ref, HeapCellValue)>;

pub(crate) struct RedirectInfo {
    trail: Trail
}

pub(crate) trait CopierTarget: IndexMut<usize, Output=HeapCellValue>
{
    fn source(&self) -> usize;
    fn threshold(&self) -> usize;
    fn push(&mut self, HeapCellValue);
    fn store(&self, Addr) -> Addr;
    fn deref(&self, Addr) -> Addr;
    fn stack(&mut self) -> &mut AndStack;

    fn unwind_trail(&mut self, redirect: RedirectInfo)
    {
        for (r, hcv) in redirect.trail {
            match r {
                Ref::AttrVar(hc) | Ref::HeapCell(hc) => self[hc] = hcv.clone(),
                Ref::StackCell(fr, sc) => self.stack()[fr][sc] = hcv.as_addr(0)
            }
        }
    }

    fn reinstantiate_var(&mut self, ra: Addr, scan: usize, trail: &mut Trail)
    {
        match ra {
            Addr::HeapCell(hc) => {
                self[scan] = HeapCellValue::Addr(Addr::HeapCell(scan));
                self[hc] = HeapCellValue::Addr(Addr::HeapCell(scan));
                trail.push((Ref::HeapCell(hc),
                            HeapCellValue::Addr(Addr::HeapCell(hc))));
            },
            Addr::StackCell(fr, sc) => {
                self[scan] = HeapCellValue::Addr(Addr::HeapCell(scan));
                self.stack()[fr][sc] = Addr::HeapCell(scan);
                trail.push((Ref::StackCell(fr, sc),
                            HeapCellValue::Addr(Addr::StackCell(fr, sc))));
            },
            Addr::AttrVar(hc) => {
                self[scan] = HeapCellValue::Addr(Addr::AttrVar(scan));
                self[hc] = HeapCellValue::Addr(Addr::AttrVar(scan));
                trail.push((Ref::AttrVar(hc),
                            HeapCellValue::Addr(Addr::AttrVar(hc))));
            },
            _ => {}
        }
    }

    // duplicate_term_impl(L1, L2) uses Cheney's algorithm to copy the term
    // at L1 to L2. trail is kept to restore the innards of L1 after
    // it's been copied to L2.
    fn duplicate_term_impl(&mut self, addr: Addr) -> RedirectInfo
    {
        let mut trail = Trail::new();
        let mut scan = self.source();
        let old_h = self.threshold();

        self.push(HeapCellValue::Addr(addr));

        while scan < self.threshold() {
            match self[scan].clone() {
                HeapCellValue::NamedStr(..) =>
                    scan += 1,
                HeapCellValue::Addr(a) =>
                    match a.clone() {
                        Addr::Lis(a) => {
                            if let HeapCellValue::Addr(Addr::Lis(b)) = self[a].clone() {
                                if b >= old_h {
                                    self[scan] = HeapCellValue::Addr(Addr::Lis(b));
                                    scan += 1;
                                    continue;
                                }
                            }

                            let threshold = self.threshold();
                            self[scan] = HeapCellValue::Addr(Addr::Lis(threshold));

                            let hcv = self[a].clone();
                            self.push(hcv.clone());

                            let ra = hcv.as_addr(threshold);
                            let rd = self.store(self.deref(ra));

                            match rd.clone() {
                                Addr::HeapCell(hc) if hc >= old_h =>
                                    self[threshold] = HeapCellValue::Addr(rd),
                                ra @ Addr::AttrVar(_) | ra @ Addr::HeapCell(..) | ra @ Addr::StackCell(..) =>
                                    if ra == rd {
                                        self.reinstantiate_var(ra, threshold, &mut trail);
                                    } else {
                                        self[threshold] = HeapCellValue::Addr(ra);
                                    },
                                _ => {
                                    trail.push((Ref::HeapCell(a), self[a].clone()));
                                    self[a] = HeapCellValue::Addr(Addr::Lis(threshold))
                                }
                            };

                            let hcv = self[a+1].clone();
                            self.push(hcv);

                            scan += 1;
                        },
                        Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(_, _) => {
                            let ra = a;
                            let rd = self.store(self.deref(ra.clone()));

                            match rd.clone() {
                                Addr::AttrVar(h) | Addr::HeapCell(h) if h >= old_h => {
                                    self[scan] = HeapCellValue::Addr(rd);
                                    scan += 1;
                                },
                                Addr::AttrVar(h) if ra == rd => {
                                    let threshold = self.threshold();
                                    self.push(HeapCellValue::Addr(Addr::AttrVar(threshold)));

                                    let list_val = self[h+1].clone();
                                    self.push(list_val);

                                    self.reinstantiate_var(ra, threshold, &mut trail);
                                    self[scan] = HeapCellValue::Addr(Addr::AttrVar(threshold));
                                },
                                _ if ra == rd => {
                                    self.reinstantiate_var(ra, scan, &mut trail);
                                    scan += 1;
                                },
                                _ => self[scan] = HeapCellValue::Addr(rd)
                            };
                        },
                        Addr::Str(s) => {
                            match self[s].clone() {
                                HeapCellValue::NamedStr(arity, name, fixity) => {
                                    let threshold = self.threshold();

                                    self[scan] = HeapCellValue::Addr(Addr::Str(threshold));
                                    self[s] = HeapCellValue::Addr(Addr::Str(threshold));

                                    trail.push((Ref::HeapCell(s),
                                                HeapCellValue::NamedStr(arity, name.clone(), fixity)));

                                    self.push(HeapCellValue::NamedStr(arity, name, fixity));

                                    for i in 0 .. arity {
                                        let hcv = self[s + 1 + i].clone();
                                        self.push(hcv);
                                    }
                                },
                                HeapCellValue::Addr(Addr::Str(o)) =>
                                    self[scan] = HeapCellValue::Addr(Addr::Str(o)),
                                _ => {}
                            };

                            scan += 1;
                        },
                        Addr::Con(_) => scan += 1
                    }
            }
        }

        RedirectInfo { trail }
    }

    fn duplicate_term(&mut self, addr: Addr)
    {
        let redirect = self.duplicate_term_impl(addr);
        self.unwind_trail(redirect);
    }
}
<commit_msg>properly copy already-copied attributed variables as shallow members of lists<commit_after>use prolog::and_stack::*;
use prolog::instructions::*;

use std::ops::IndexMut;

type Trail = Vec<(Ref, HeapCellValue)>;

pub(crate) struct RedirectInfo {
    trail: Trail
}

pub(crate) trait CopierTarget: IndexMut<usize, Output=HeapCellValue>
{
    fn source(&self) -> usize;
    fn threshold(&self) -> usize;
    fn push(&mut self, HeapCellValue);
    fn store(&self, Addr) -> Addr;
    fn deref(&self, Addr) -> Addr;
    fn stack(&mut self) -> &mut AndStack;

    fn unwind_trail(&mut self, redirect: RedirectInfo)
    {
        for (r, hcv) in redirect.trail {
            match r {
                Ref::AttrVar(hc) | Ref::HeapCell(hc) => self[hc] = hcv.clone(),
                Ref::StackCell(fr, sc) => self.stack()[fr][sc] = hcv.as_addr(0)
            }
        }
    }

    fn reinstantiate_var(&mut self, ra: Addr, scan: usize, trail: &mut Trail)
    {
        match ra {
            Addr::HeapCell(hc) => {
                self[scan] = HeapCellValue::Addr(Addr::HeapCell(scan));
                self[hc] = HeapCellValue::Addr(Addr::HeapCell(scan));
                trail.push((Ref::HeapCell(hc),
                            HeapCellValue::Addr(Addr::HeapCell(hc))));
            },
            Addr::StackCell(fr, sc) => {
                self[scan] = HeapCellValue::Addr(Addr::HeapCell(scan));
                self.stack()[fr][sc] = Addr::HeapCell(scan);
                trail.push((Ref::StackCell(fr, sc),
                            HeapCellValue::Addr(Addr::StackCell(fr, sc))));
            },
            Addr::AttrVar(hc) => {
                self[scan] = HeapCellValue::Addr(Addr::AttrVar(scan));
                self[hc] = HeapCellValue::Addr(Addr::AttrVar(scan));
                trail.push((Ref::AttrVar(hc),
                            HeapCellValue::Addr(Addr::AttrVar(hc))));
            },
            _ => {}
        }
    }

    // duplicate_term_impl(L1, L2) uses Cheney's algorithm to copy the term
    // at L1 to L2. trail is kept to restore the innards of L1 after
    // it's been copied to L2.
    fn duplicate_term_impl(&mut self, addr: Addr) -> RedirectInfo
    {
        let mut trail = Trail::new();
        let mut scan = self.source();
        let old_h = self.threshold();

        self.push(HeapCellValue::Addr(addr));

        while scan < self.threshold() {
            match self[scan].clone() {
                HeapCellValue::NamedStr(..) =>
                    scan += 1,
                HeapCellValue::Addr(a) =>
                    match a.clone() {
                        Addr::Lis(a) => {
                            if let HeapCellValue::Addr(Addr::Lis(b)) = self[a].clone() {
                                if b >= old_h {
                                    self[scan] = HeapCellValue::Addr(Addr::Lis(b));
                                    scan += 1;
                                    continue;
                                }
                            }

                            let threshold = self.threshold();
                            self[scan] = HeapCellValue::Addr(Addr::Lis(threshold));

                            let hcv = self[a].clone();
                            self.push(hcv.clone());

                            let ra = hcv.as_addr(threshold);
                            let rd = self.store(self.deref(ra));

                            match rd.clone() {
                                Addr::AttrVal(h) | Addr::HeapCell(h) if h >= old_h =>
                                    self[threshold] = HeapCellValue::Addr(rd),
                                ra @ Addr::AttrVar(_) | ra @ Addr::HeapCell(..) | ra @ Addr::StackCell(..) =>
                                    if ra == rd {
                                        self.reinstantiate_var(ra, threshold, &mut trail);
                                    } else {
                                        self[threshold] = HeapCellValue::Addr(ra);
                                    },
                                _ => {
                                    trail.push((Ref::HeapCell(a), self[a].clone()));
                                    self[a] = HeapCellValue::Addr(Addr::Lis(threshold))
                                }
                            };

                            let hcv = self[a+1].clone();
                            self.push(hcv);

                            scan += 1;
                        },
                        Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(_, _) => {
                            let ra = a;
                            let rd = self.store(self.deref(ra.clone()));

                            match rd.clone() {
                                Addr::AttrVar(h) | Addr::HeapCell(h) if h >= old_h => {
                                    self[scan] = HeapCellValue::Addr(rd);
                                    scan += 1;
                                },
                                Addr::AttrVar(h) if ra == rd => {
                                    let threshold = self.threshold();
                                    self.push(HeapCellValue::Addr(Addr::AttrVar(threshold)));

                                    let list_val = self[h+1].clone();
                                    self.push(list_val);

                                    self.reinstantiate_var(ra, threshold, &mut trail);
                                    self[scan] = HeapCellValue::Addr(Addr::AttrVar(threshold));
                                },
                                _ if ra == rd => {
                                    self.reinstantiate_var(ra, scan, &mut trail);
                                    scan += 1;
                                },
                                _ => self[scan] = HeapCellValue::Addr(rd)
                            };
                        },
                        Addr::Str(s) => {
                            match self[s].clone() {
                                HeapCellValue::NamedStr(arity, name, fixity) => {
                                    let threshold = self.threshold();

                                    self[scan] = HeapCellValue::Addr(Addr::Str(threshold));
                                    self[s] = HeapCellValue::Addr(Addr::Str(threshold));

                                    trail.push((Ref::HeapCell(s),
                                                HeapCellValue::NamedStr(arity, name.clone(), fixity)));

                                    self.push(HeapCellValue::NamedStr(arity, name, fixity));

                                    for i in 0 .. arity {
                                        let hcv = self[s + 1 + i].clone();
                                        self.push(hcv);
                                    }
                                },
                                HeapCellValue::Addr(Addr::Str(o)) =>
                                    self[scan] = HeapCellValue::Addr(Addr::Str(o)),
                                _ => {}
                            };

                            scan += 1;
                        },
                        Addr::Con(_) => scan += 1
                    }
            }
        }

        RedirectInfo { trail }
    }

    fn duplicate_term(&mut self, addr: Addr)
    {
        let redirect = self.duplicate_term_impl(addr);
        self.unwind_trail(redirect);
    }
}
<|endoftext|>


<commit_before>use std::fmt;

/// `Rank` is a type the represents the rank of a playing card.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Rank {
    Ace,
    Two,
    Three,
    Four,
    Five,
    Six,
    Seven,
    Eight,
    Nine,
    Ten,
    Jack,
    Queen,
    King,
}

/// `Suit` is a type the represents the suit of a playing card.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Suit {
    Hearts,
    Clubs,
    Diamonds,
    Spades,
}

/// `Card` is a struct that holds the [`Rank`] and [`Suit`] type of a playing card.
///
/// [`Rank`]: enum.Rank.html
/// [`Suit`]: enum.Suit.html
#[derive(Debug, PartialEq)]
pub struct Card {
    rank: Rank,
    suit: Suit,
}

impl Card {
    /// Constructs a new `Card`.
    ///
    /// # Examples
    ///
    /// ```
    /// use libterminal_cribbage::cards::{Card, Rank, Suit};
    ///
    /// let playing_card = Card::new(Rank::Ace, Suit::Spades);
    ///
    /// println!("Played card: {}", playing_card);
    /// ```
    pub fn new(rank: Rank, suit: Suit) -> Card {
        Card {
            rank: rank,
            suit: suit,
        }
    }
}

impl fmt::Display for Card {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        let rank_str = match self.rank {
            Rank::Two => "2",
            Rank::Three => "3",
            Rank::Four => "4",
            Rank::Five => "5",
            Rank::Six => "6",
            Rank::Seven => "7",
            Rank::Eight => "8",
            Rank::Nine => "9",
            Rank::Ten => "10",
            Rank::Jack => "J",
            Rank::Queen => "Q",
            Rank::King => "K",
            Rank::Ace => "A",
        };

        let suit_char = match self.suit {
            Suit::Hearts => '\u{2665}',
            Suit::Clubs => '\u{2663}',
            Suit::Diamonds => '\u{2666}',
            Suit::Spades => '\u{2660}',
        };

        write!(formatter, "[{}{}]", rank_str, suit_char)
    }
}

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

    #[test]
    fn test_new() {
        let test_card = Card::new(Rank::Ace, Suit::Clubs);

        assert_eq!(test_card.rank, Rank::Ace);
        assert_eq!(test_card.suit, Suit::Clubs);
    }
}<commit_msg>Add better ordering for Card struct.<commit_after>use std::fmt;

/// `Rank` is a type the represents the rank of a playing card.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Rank {
    Ace,
    Two,
    Three,
    Four,
    Five,
    Six,
    Seven,
    Eight,
    Nine,
    Ten,
    Jack,
    Queen,
    King,
}

/// `Suit` is a type the represents the suit of a playing card.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Suit {
    Hearts,
    Clubs,
    Diamonds,
    Spades,
}

/// `Card` is a struct that holds the [`Rank`] and [`Suit`] type of a playing card.
///
/// [`Rank`]: enum.Rank.html
/// [`Suit`]: enum.Suit.html
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Card {
    rank: Rank,
    suit: Suit,
}

impl Card {
    /// Constructs a new `Card`.
    ///
    /// # Examples
    ///
    /// ```
    /// use libterminal_cribbage::cards::{Card, Rank, Suit};
    ///
    /// let playing_card = Card::new(Rank::Ace, Suit::Spades);
    ///
    /// println!("Played card: {}", playing_card);
    /// ```
    pub fn new(rank: Rank, suit: Suit) -> Card {
        Card {
            rank: rank,
            suit: suit,
        }
    }
}

impl fmt::Display for Card {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        let rank_str = match self.rank {
            Rank::Two => "2",
            Rank::Three => "3",
            Rank::Four => "4",
            Rank::Five => "5",
            Rank::Six => "6",
            Rank::Seven => "7",
            Rank::Eight => "8",
            Rank::Nine => "9",
            Rank::Ten => "10",
            Rank::Jack => "J",
            Rank::Queen => "Q",
            Rank::King => "K",
            Rank::Ace => "A",
        };

        let suit_char = match self.suit {
            Suit::Hearts => '\u{2665}',
            Suit::Clubs => '\u{2663}',
            Suit::Diamonds => '\u{2666}',
            Suit::Spades => '\u{2660}',
        };

        write!(formatter, "[{}{}]", rank_str, suit_char)
    }
}

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

    #[test]
    fn test_new() {
        let test_card = Card::new(Rank::Ace, Suit::Clubs);

        assert_eq!(test_card.rank, Rank::Ace);
        assert_eq!(test_card.suit, Suit::Clubs);
    }
}<|endoftext|>


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

use executor::FieldResult;
use value::Value;
use schema::model::RootNode;

enum Sample {
    One,
    Two,
}

struct Scalar(i64);

struct Interface {}

struct Root {}

graphql_scalar!(Scalar as "SampleScalar" {
    resolve(&self) -> Value {
        Value::int(self.0)
    }

    from_input_value(v: &InputValue) -> Option<Scalar> {
        v.as_int_value().map(|i| Scalar(i))
    }
});

graphql_enum!(Sample as "SampleEnum" {
    Sample::One => "ONE",
    Sample::Two => "TWO",
});

graphql_interface!(Interface: () as "SampleInterface" |&self| {
    description: "A sample interface"

    field sample_enum() -> FieldResult<Sample> as "A sample field in the interface" {
        Ok(Sample::One)
    }

    instance_resolvers: |&_| [
        Some(Root {}),
    ]
});

graphql_object!(Root: () as "Root" |&self| {
    interfaces: [Interface]

    field sample_enum() -> FieldResult<Sample> {
        Ok(Sample::One)
    }

    field sample_scalar() -> FieldResult<Scalar> {
        Ok(Scalar(123))
    }
});

#[test]
fn test_execution() {
    let doc = r#"
    {
        sampleEnum
        sampleScalar
    }
    "#;
    let schema = RootNode::new(Root {}, ());

    let (result, errs) = ::execute(doc, None, &schema, &HashMap::new(), &())
        .expect("Execution failed");

    assert_eq!(errs, []);

    println!("Result: {:?}", result);

    assert_eq!(result, Value::object(vec![
        ("sampleEnum", Value::string("ONE")),
        ("sampleScalar", Value::int(123)),
    ].into_iter().collect()));
}

#[test]
fn enum_introspection() {
    let doc = r#"
    {
        __type(name: "SampleEnum") {
            name
            kind
            description
            enumValues {
                name
                description
                isDeprecated
                deprecationReason
            }
        }
    }
    "#;
    let schema = RootNode::new(Root {}, ());

    let (result, errs) = ::execute(doc, None, &schema, &HashMap::new(), &())
        .expect("Execution failed");

    assert_eq!(errs, []);

    println!("Result: {:?}", result);

    let type_info = result
        .as_object_value().expect("Result is not an object")
        .get("__type").expect("__type field missing")
        .as_object_value().expect("__type field not an object value");

    assert_eq!(type_info.get("name"), Some(&Value::string("SampleEnum")));
    assert_eq!(type_info.get("kind"), Some(&Value::string("ENUM")));
    assert_eq!(type_info.get("description"), Some(&Value::null()));

    let values = type_info
        .get("enumValues").expect("enumValues field missing")
        .as_list_value().expect("enumValues not a list");

    assert_eq!(values.len(), 2);

    assert!(values.contains(&Value::object(vec![
        ("name", Value::string("ONE")),
        ("description", Value::null()),
        ("isDeprecated", Value::boolean(false)),
        ("deprecationReason", Value::null()),
    ].into_iter().collect())));

    assert!(values.contains(&Value::object(vec![
        ("name", Value::string("TWO")),
        ("description", Value::null()),
        ("isDeprecated", Value::boolean(false)),
        ("deprecationReason", Value::null()),
    ].into_iter().collect())));
}

#[test]
fn interface_introspection() {
    let doc = r#"
    {
        __type(name: "SampleInterface") {
            name
            kind
            description
            possibleTypes {
                name
            }
            fields {
                name
                description
                args {
                    name
                }
                type {
                    name
                    kind
                    ofType {
                        name
                        kind
                    }
                }
                isDeprecated
                deprecationReason
            }
        }
    }
    "#;
    let schema = RootNode::new(Root {}, ());

    let (result, errs) = ::execute(doc, None, &schema, &HashMap::new(), &())
        .expect("Execution failed");

    assert_eq!(errs, []);

    println!("Result: {:?}", result);

    let type_info = result
        .as_object_value().expect("Result is not an object")
        .get("__type").expect("__type field missing")
        .as_object_value().expect("__type field not an object value");

    assert_eq!(type_info.get("name"), Some(&Value::string("SampleInterface")));
    assert_eq!(type_info.get("kind"), Some(&Value::string("INTERFACE")));
    assert_eq!(type_info.get("description"), Some(&Value::string("A sample interface")));

    let possible_types = type_info
        .get("possibleTypes").expect("possibleTypes field missing")
        .as_list_value().expect("possibleTypes not a list");

    assert_eq!(possible_types.len(), 1);

    assert!(possible_types.contains(&Value::object(vec![
        ("name", Value::string("Root")),
    ].into_iter().collect())));

    let fields = type_info
        .get("fields").expect("fields field missing")
        .as_list_value().expect("fields field not an object value");

    assert_eq!(fields.len(), 2);

    assert!(fields.contains(&Value::object(vec![
        ("name", Value::string("sampleEnum")),
        ("description", Value::string("A sample field in the interface")),
        ("args", Value::list(vec![])),
        ("type", Value::object(vec![
            ("name", Value::null()),
            ("kind", Value::string("NON_NULL")),
            ("ofType", Value::object(vec![
                ("name", Value::string("SampleEnum")),
                ("kind", Value::string("ENUM")),
            ].into_iter().collect())),
        ].into_iter().collect())),
        ("isDeprecated", Value::boolean(false)),
        ("deprecationReason", Value::null()),
    ].into_iter().collect())));
}

#[test]
fn scalar_introspection() {
    let doc = r#"
    {
        __type(name: "SampleScalar") {
            name
            kind
            description
        }
    }
    "#;
    let schema = RootNode::new(Root {}, ());

    let (result, errs) = ::execute(doc, None, &schema, &HashMap::new(), &())
        .expect("Execution failed");

    assert_eq!(errs, []);

    println!("Result: {:?}", result);

    let type_info = result
        .as_object_value().expect("Result is not an object")
        .get("__type").expect("__type field missing");

    assert_eq!(type_info, &Value::object(vec![
        ("name", Value::string("SampleScalar")),
        ("kind", Value::string("SCALAR")),
        ("description", Value::null()),
    ].into_iter().collect()));
}
<commit_msg>Test querying meta fields for mismatching types<commit_after>use std::collections::HashMap;

use executor::FieldResult;
use value::Value;
use schema::model::RootNode;

enum Sample {
    One,
    Two,
}

struct Scalar(i64);

struct Interface {}

struct Root {}

graphql_scalar!(Scalar as "SampleScalar" {
    resolve(&self) -> Value {
        Value::int(self.0)
    }

    from_input_value(v: &InputValue) -> Option<Scalar> {
        v.as_int_value().map(|i| Scalar(i))
    }
});

graphql_enum!(Sample as "SampleEnum" {
    Sample::One => "ONE",
    Sample::Two => "TWO",
});

graphql_interface!(Interface: () as "SampleInterface" |&self| {
    description: "A sample interface"

    field sample_enum() -> FieldResult<Sample> as "A sample field in the interface" {
        Ok(Sample::One)
    }

    instance_resolvers: |&_| [
        Some(Root {}),
    ]
});

graphql_object!(Root: () as "Root" |&self| {
    interfaces: [Interface]

    field sample_enum() -> FieldResult<Sample> {
        Ok(Sample::One)
    }

    field sample_scalar() -> FieldResult<Scalar> {
        Ok(Scalar(123))
    }
});

#[test]
fn test_execution() {
    let doc = r#"
    {
        sampleEnum
        sampleScalar
    }
    "#;
    let schema = RootNode::new(Root {}, ());

    let (result, errs) = ::execute(doc, None, &schema, &HashMap::new(), &())
        .expect("Execution failed");

    assert_eq!(errs, []);

    println!("Result: {:?}", result);

    assert_eq!(result, Value::object(vec![
        ("sampleEnum", Value::string("ONE")),
        ("sampleScalar", Value::int(123)),
    ].into_iter().collect()));
}

#[test]
fn enum_introspection() {
    let doc = r#"
    {
        __type(name: "SampleEnum") {
            name
            kind
            description
            enumValues {
                name
                description
                isDeprecated
                deprecationReason
            }
            interfaces { name }
            possibleTypes { name }
            inputFields { name }
            ofType { name }
        }
    }
    "#;
    let schema = RootNode::new(Root {}, ());

    let (result, errs) = ::execute(doc, None, &schema, &HashMap::new(), &())
        .expect("Execution failed");

    assert_eq!(errs, []);

    println!("Result: {:?}", result);

    let type_info = result
        .as_object_value().expect("Result is not an object")
        .get("__type").expect("__type field missing")
        .as_object_value().expect("__type field not an object value");

    assert_eq!(type_info.get("name"), Some(&Value::string("SampleEnum")));
    assert_eq!(type_info.get("kind"), Some(&Value::string("ENUM")));
    assert_eq!(type_info.get("description"), Some(&Value::null()));
    assert_eq!(type_info.get("interfaces"), Some(&Value::null()));
    assert_eq!(type_info.get("possibleTypes"), Some(&Value::null()));
    assert_eq!(type_info.get("inputFields"), Some(&Value::null()));
    assert_eq!(type_info.get("ofType"), Some(&Value::null()));

    let values = type_info
        .get("enumValues").expect("enumValues field missing")
        .as_list_value().expect("enumValues not a list");

    assert_eq!(values.len(), 2);

    assert!(values.contains(&Value::object(vec![
        ("name", Value::string("ONE")),
        ("description", Value::null()),
        ("isDeprecated", Value::boolean(false)),
        ("deprecationReason", Value::null()),
    ].into_iter().collect())));

    assert!(values.contains(&Value::object(vec![
        ("name", Value::string("TWO")),
        ("description", Value::null()),
        ("isDeprecated", Value::boolean(false)),
        ("deprecationReason", Value::null()),
    ].into_iter().collect())));
}

#[test]
fn interface_introspection() {
    let doc = r#"
    {
        __type(name: "SampleInterface") {
            name
            kind
            description
            possibleTypes {
                name
            }
            fields {
                name
                description
                args {
                    name
                }
                type {
                    name
                    kind
                    ofType {
                        name
                        kind
                    }
                }
                isDeprecated
                deprecationReason
            }
            interfaces { name }
            enumValues { name }
            inputFields { name }
            ofType { name }
        }
    }
    "#;
    let schema = RootNode::new(Root {}, ());

    let (result, errs) = ::execute(doc, None, &schema, &HashMap::new(), &())
        .expect("Execution failed");

    assert_eq!(errs, []);

    println!("Result: {:?}", result);

    let type_info = result
        .as_object_value().expect("Result is not an object")
        .get("__type").expect("__type field missing")
        .as_object_value().expect("__type field not an object value");

    assert_eq!(type_info.get("name"), Some(&Value::string("SampleInterface")));
    assert_eq!(type_info.get("kind"), Some(&Value::string("INTERFACE")));
    assert_eq!(type_info.get("description"), Some(&Value::string("A sample interface")));
    assert_eq!(type_info.get("interfaces"), Some(&Value::null()));
    assert_eq!(type_info.get("enumValues"), Some(&Value::null()));
    assert_eq!(type_info.get("inputFields"), Some(&Value::null()));
    assert_eq!(type_info.get("ofType"), Some(&Value::null()));

    let possible_types = type_info
        .get("possibleTypes").expect("possibleTypes field missing")
        .as_list_value().expect("possibleTypes not a list");

    assert_eq!(possible_types.len(), 1);

    assert!(possible_types.contains(&Value::object(vec![
        ("name", Value::string("Root")),
    ].into_iter().collect())));

    let fields = type_info
        .get("fields").expect("fields field missing")
        .as_list_value().expect("fields field not an object value");

    assert_eq!(fields.len(), 2);

    assert!(fields.contains(&Value::object(vec![
        ("name", Value::string("sampleEnum")),
        ("description", Value::string("A sample field in the interface")),
        ("args", Value::list(vec![])),
        ("type", Value::object(vec![
            ("name", Value::null()),
            ("kind", Value::string("NON_NULL")),
            ("ofType", Value::object(vec![
                ("name", Value::string("SampleEnum")),
                ("kind", Value::string("ENUM")),
            ].into_iter().collect())),
        ].into_iter().collect())),
        ("isDeprecated", Value::boolean(false)),
        ("deprecationReason", Value::null()),
    ].into_iter().collect())));
}

#[test]
fn scalar_introspection() {
    let doc = r#"
    {
        __type(name: "SampleScalar") {
            name
            kind
            description
            fields { name }
            interfaces { name }
            possibleTypes { name }
            enumValues { name }
            inputFields { name }
            ofType { name }
        }
    }
    "#;
    let schema = RootNode::new(Root {}, ());

    let (result, errs) = ::execute(doc, None, &schema, &HashMap::new(), &())
        .expect("Execution failed");

    assert_eq!(errs, []);

    println!("Result: {:?}", result);

    let type_info = result
        .as_object_value().expect("Result is not an object")
        .get("__type").expect("__type field missing");

    assert_eq!(type_info, &Value::object(vec![
        ("name", Value::string("SampleScalar")),
        ("kind", Value::string("SCALAR")),
        ("description", Value::null()),
        ("fields", Value::null()),
        ("interfaces", Value::null()),
        ("possibleTypes", Value::null()),
        ("enumValues", Value::null()),
        ("inputFields", Value::null()),
        ("ofType", Value::null()),
    ].into_iter().collect()));
}
<|endoftext|>


<commit_before>use std::cmp::Reverse;
use std::collections::{BinaryHeap, HashMap};

use crate::{
    query::{BooleanQuery, BoostQuery, Occur, Query, TermQuery, bm25::idf},
    schema::{Field, FieldType, FieldValue, IndexRecordOption, Term, Value},
    tokenizer::{BoxTokenStream, FacetTokenizer, PreTokenizedStream, Tokenizer},
    DocAddress, Result, Searcher, TantivyError,
};

#[derive(Debug, PartialEq)]
struct ScoreTerm {
    pub term: Term,
    pub score: f32,
}

impl ScoreTerm {
    fn new(term: Term, score: f32) -> Self {
        Self { term, score }
    }
}

impl Eq for ScoreTerm {}

impl PartialOrd for ScoreTerm {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        self.score.partial_cmp(&other.score)
    }
}

impl Ord for ScoreTerm {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.partial_cmp(other).unwrap_or(std::cmp::Ordering::Equal)
    }
}

/// A struct used as helper to build [`MoreLikeThisQuery`]
/// This more-like-this implementation is inspired by the Appache Lucene
/// amd closely follows the same implementation with adaptabtion to Tantivy vocabulary and API.
///
/// [MoreLikeThis](https://github.com/apache/lucene/blob/main/lucene/queries/src/java/org/apache/lucene/queries/mlt/MoreLikeThis.java#L147)
/// [MoreLikeThisQuery](https://github.com/apache/lucene/blob/main/lucene/queries/src/java/org/apache/lucene/queries/mlt/MoreLikeThisQuery.java#L36)
#[derive(Debug, Clone)]
pub struct MoreLikeThis {
    /// Ignore words which do not occur in at least this many docs.
    pub min_doc_frequency: Option<u64>,
    /// Ignore words which occur in more than this many docs.
    pub max_doc_frequency: Option<u64>,
    /// Ignore words less frequent than this.
    pub min_term_frequency: Option<usize>,
    /// Don't return a query longer than this.
    pub max_query_terms: Option<usize>,
    /// Ignore words if less than this length.
    pub min_word_length: Option<usize>,
    /// Ignore words if greater than this length.
    pub max_word_length: Option<usize>,
    /// Boost factor to use when boosting the terms
    pub boost_factor: Option<f32>,
    /// Current set of stop words.
    pub stop_words: Vec<String>,
}

impl Default for MoreLikeThis {
    fn default() -> Self {
        Self {
            min_doc_frequency: Some(5),
            max_doc_frequency: None,
            min_term_frequency: Some(2),
            max_query_terms: Some(25),
            min_word_length: None,
            max_word_length: None,
            boost_factor: Some(1.0),
            stop_words: vec![],
        }
    }
}

impl MoreLikeThis {
    /// Creates a [`BooleanQuery`] using a document address to collect
    /// the top stored field values.
    pub fn query_with_document(
        &self,
        searcher: &Searcher,
        doc_address: DocAddress,
    ) -> Result<BooleanQuery> {
        let score_terms = self.retrieve_terms_from_doc_address(searcher, doc_address)?;
        let query = self.create_query(score_terms);
        Ok(query)
    }

    /// Creates a [`BooleanQuery`] using a set of field values.
    pub fn query_with_document_fields(
        &self,
        searcher: &Searcher,
        doc_fields: &[(Field, Vec<FieldValue>)],
    ) -> Result<BooleanQuery> {
        let score_terms = self.retrieve_terms_from_doc_fields(searcher, doc_fields)?;
        let query = self.create_query(score_terms);
        Ok(query)
    }

    /// Creates a [`BooleanQuery`] from an ascendingly sorted list of ScoreTerm
    /// This will map the list of ScoreTerm to a list of [`TermQuery`]  and compose a
    /// BooleanQuery using that list as sub queries.
    fn create_query(&self, mut score_terms: Vec<ScoreTerm>) -> BooleanQuery {
        score_terms.sort_by(|left_ts, right_ts| right_ts.cmp(left_ts));
        let best_score = score_terms.first().map_or(1f32, |x| x.score);
        let mut queries = Vec::new();

        for ScoreTerm { term, score } in score_terms {
            let mut query: Box<dyn Query> =
                Box::new(TermQuery::new(term, IndexRecordOption::Basic));
            if let Some(factor) = self.boost_factor {
                query = Box::new(BoostQuery::new(query, score * factor / best_score));
            }
            queries.push((Occur::Should, query));
        }
        BooleanQuery::from(queries)
    }

    /// Finds terms for a more-like-this query.
    /// doc_address is the address of document from which to find terms.
    fn retrieve_terms_from_doc_address(
        &self,
        searcher: &Searcher,
        doc_address: DocAddress,
    ) -> Result<Vec<ScoreTerm>> {
        let doc = searcher.doc(doc_address)?;
        let field_to_field_values = doc
            .get_sorted_field_values()
            .iter()
            .map(|(field, values)| {
                (
                    *field,
                    values
                        .iter()
                        .map(|v| (**v).clone())
                        .collect::<Vec<FieldValue>>(),
                )
            })
            .collect::<Vec<_>>();
        self.retrieve_terms_from_doc_fields(searcher, &field_to_field_values)
    }

    /// Finds terms for a more-like-this query.
    /// field_to_field_values is a mapping from field to possible values of taht field.
    fn retrieve_terms_from_doc_fields(
        &self,
        searcher: &Searcher,
        field_to_field_values: &[(Field, Vec<FieldValue>)],
    ) -> Result<Vec<ScoreTerm>> {
        if field_to_field_values.is_empty() {
            return Err(TantivyError::InvalidArgument("Cannot create more like this query on empty field values. The document may not have stored fields".to_string()));
        }

        let mut field_to_term_freq_map = HashMap::new();
        for (field, field_values) in field_to_field_values {
            self.add_term_frequencies(searcher, *field, field_values, &mut field_to_term_freq_map)?;
        }
        self.create_score_term(searcher, field_to_term_freq_map)
    }

    /// Computes the frequency of values for a field while updating the term frequencies
    /// Note: A FieldValue can be made up of multiple terms.
    /// We are interested in extracting terms within FieldValue
    fn add_term_frequencies(
        &self,
        searcher: &Searcher,
        field: Field,
        field_values: &[FieldValue],
        term_frequencies: &mut HashMap<Term, usize>,
    ) -> Result<()> {
        let schema = searcher.schema();
        let tokenizer_manager = searcher.index().tokenizers();

        let field_entry = schema.get_field_entry(field);
        if !field_entry.is_indexed() {
            return Ok(());
        }

        // extract the raw value, possibly tokenizing & filtering to update the term frequency map
        match field_entry.field_type() {
            FieldType::HierarchicalFacet(_) => {
                let facets: Vec<&str> = field_values
                    .iter()
                    .map(|field_value| match *field_value.value() {
                        Value::Facet(ref facet) => Ok(facet.encoded_str()),
                        _ => Err(TantivyError::InvalidArgument(
                            "invalid field value".to_string(),
                        )),
                    })
                    .collect::<Result<Vec<_>>>()?;
                for fake_str in facets {
                    FacetTokenizer.token_stream(fake_str).process(&mut |token| {
                        if self.is_noise_word(token.text.clone()) {
                            let term = Term::from_field_text(field, &token.text);
                            *term_frequencies.entry(term).or_insert(0) += 1;
                        }
                    });
                }
            }
            FieldType::Str(text_options) => {
                let mut token_streams: Vec<BoxTokenStream> = vec![];

                for field_value in field_values {
                    match field_value.value() {
                        Value::PreTokStr(tok_str) => {
                            token_streams.push(PreTokenizedStream::from(tok_str.clone()).into());
                        }
                        Value::Str(ref text) => {
                            if let Some(tokenizer) = text_options
                                .get_indexing_options()
                                .map(|text_indexing_options| {
                                    text_indexing_options.tokenizer().to_string()
                                })
                                .and_then(|tokenizer_name| tokenizer_manager.get(&tokenizer_name))
                            {
                                token_streams.push(tokenizer.token_stream(text));
                            }
                        }
                        _ => (),
                    }
                }

                for mut token_stream in token_streams {
                    token_stream.process(&mut |token| {
                        if !self.is_noise_word(token.text.clone()) {
                            let term = Term::from_field_text(field, &token.text);
                            *term_frequencies.entry(term).or_insert(0) += 1;
                        }
                    });
                }
            }
            FieldType::U64(_) => {
                for field_value in field_values {
                    let val = field_value
                        .value()
                        .u64_value()
                        .ok_or(TantivyError::InvalidArgument("invalid value".to_string()))?;
                    if !self.is_noise_word(val.to_string()) {
                        let term = Term::from_field_u64(field, val);
                        *term_frequencies.entry(term).or_insert(0) += 1;
                    }
                }
            }
            FieldType::Date(_) => {
                for field_value in field_values {
                    // TODO: Ask if this is the semantic (timestamp) we want
                    let val = field_value
                        .value()
                        .date_value()
                        .ok_or(TantivyError::InvalidArgument("invalid value".to_string()))?
                        .timestamp();
                    if !self.is_noise_word(val.to_string()) {
                        let term = Term::from_field_i64(field, val);
                        *term_frequencies.entry(term).or_insert(0) += 1;
                    }
                }
            }
            FieldType::I64(_) => {
                for field_value in field_values {
                    let val = field_value
                        .value()
                        .i64_value()
                        .ok_or(TantivyError::InvalidArgument("invalid value".to_string()))?;
                    if !self.is_noise_word(val.to_string()) {
                        let term = Term::from_field_i64(field, val);
                        *term_frequencies.entry(term).or_insert(0) += 1;
                    }
                }
            }
            FieldType::F64(_) => {
                for field_value in field_values {
                    let val = field_value
                        .value()
                        .f64_value()
                        .ok_or(TantivyError::InvalidArgument("invalid value".to_string()))?;
                    if !self.is_noise_word(val.to_string()) {
                        let term = Term::from_field_f64(field, val);
                        *term_frequencies.entry(term).or_insert(0) += 1;
                    }
                }
            }
            _ => {}
        }
        Ok(())
    }

    /// Determines if the term is likely to be of interest based on "more-like-this" settings
    fn is_noise_word(&self, word: String) -> bool {
        let word_length = word.len();
        if word_length == 0 {
            return true;
        }
        if self
            .min_word_length
            .map(|min| word_length < min)
            .unwrap_or(false)
        {
            return true;
        }
        if self
            .max_word_length
            .map(|max| word_length > max)
            .unwrap_or(false)
        {
            return true;
        }
        return self.stop_words.contains(&word);
    }

    /// Couputes the score for each term while ignoring not useful terms
    fn create_score_term(
        &self,
        searcher: &Searcher,
        per_field_term_frequencies: HashMap<Term, usize>,
    ) -> Result<Vec<ScoreTerm>> {
        let mut score_terms: BinaryHeap<Reverse<ScoreTerm>> = BinaryHeap::new();
        let num_docs = searcher
            .segment_readers()
            .iter()
            .map(|segment_reader| segment_reader.num_docs() as u64)
            .sum::<u64>();

        for (term, term_frequency) in per_field_term_frequencies.iter() {
            // ignore terms with less than min_term_frequency
            if self
                .min_term_frequency
                .map(|min_term_frequency| *term_frequency < min_term_frequency)
                .unwrap_or(false)
            {
                continue;
            }

            let doc_freq = searcher.doc_freq(&term)?;

            // ignore terms with less than min_doc_frequency
            if self
                .min_doc_frequency
                .map(|min_doc_frequency| doc_freq < min_doc_frequency)
                .unwrap_or(false)
            {
                continue;
            }

            // ignore terms with more than max_doc_frequency
            if self
                .max_doc_frequency
                .map(|max_doc_frequency| doc_freq > max_doc_frequency)
                .unwrap_or(false)
            {
                continue;
            }

            // ignore terms with zero frequency
            if doc_freq == 0 {
                continue;
            }

            // compute similarity & score
            let idf = idf(doc_freq, num_docs);
            let score = (*term_frequency as f32) * idf;
            if let Some(limit) = self.max_query_terms {
                if score_terms.len() > limit {
                    // update the least significant term
                    let least_significant_term_score = score_terms.peek().unwrap().0.score;
                    if least_significant_term_score < score {
                        score_terms.peek_mut().unwrap().0 = ScoreTerm::new(term.clone(), score);
                    }
                } else {
                    score_terms.push(Reverse(ScoreTerm::new(term.clone(), score)));
                }
            } else {
                score_terms.push(Reverse(ScoreTerm::new(term.clone(), score)));
            }
        }

        let score_terms_vec: Vec<ScoreTerm> = score_terms
            .into_iter()
            .map(|reverse_score_term| reverse_score_term.0)
            .collect();

        Ok(score_terms_vec)
    }

}
<commit_msg>fix cargo fmt<commit_after>use std::cmp::Reverse;
use std::collections::{BinaryHeap, HashMap};

use crate::{
    query::{bm25::idf, BooleanQuery, BoostQuery, Occur, Query, TermQuery},
    schema::{Field, FieldType, FieldValue, IndexRecordOption, Term, Value},
    tokenizer::{BoxTokenStream, FacetTokenizer, PreTokenizedStream, Tokenizer},
    DocAddress, Result, Searcher, TantivyError,
};

#[derive(Debug, PartialEq)]
struct ScoreTerm {
    pub term: Term,
    pub score: f32,
}

impl ScoreTerm {
    fn new(term: Term, score: f32) -> Self {
        Self { term, score }
    }
}

impl Eq for ScoreTerm {}

impl PartialOrd for ScoreTerm {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        self.score.partial_cmp(&other.score)
    }
}

impl Ord for ScoreTerm {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.partial_cmp(other).unwrap_or(std::cmp::Ordering::Equal)
    }
}

/// A struct used as helper to build [`MoreLikeThisQuery`]
/// This more-like-this implementation is inspired by the Appache Lucene
/// amd closely follows the same implementation with adaptabtion to Tantivy vocabulary and API.
///
/// [MoreLikeThis](https://github.com/apache/lucene/blob/main/lucene/queries/src/java/org/apache/lucene/queries/mlt/MoreLikeThis.java#L147)
/// [MoreLikeThisQuery](https://github.com/apache/lucene/blob/main/lucene/queries/src/java/org/apache/lucene/queries/mlt/MoreLikeThisQuery.java#L36)
#[derive(Debug, Clone)]
pub struct MoreLikeThis {
    /// Ignore words which do not occur in at least this many docs.
    pub min_doc_frequency: Option<u64>,
    /// Ignore words which occur in more than this many docs.
    pub max_doc_frequency: Option<u64>,
    /// Ignore words less frequent than this.
    pub min_term_frequency: Option<usize>,
    /// Don't return a query longer than this.
    pub max_query_terms: Option<usize>,
    /// Ignore words if less than this length.
    pub min_word_length: Option<usize>,
    /// Ignore words if greater than this length.
    pub max_word_length: Option<usize>,
    /// Boost factor to use when boosting the terms
    pub boost_factor: Option<f32>,
    /// Current set of stop words.
    pub stop_words: Vec<String>,
}

impl Default for MoreLikeThis {
    fn default() -> Self {
        Self {
            min_doc_frequency: Some(5),
            max_doc_frequency: None,
            min_term_frequency: Some(2),
            max_query_terms: Some(25),
            min_word_length: None,
            max_word_length: None,
            boost_factor: Some(1.0),
            stop_words: vec![],
        }
    }
}

impl MoreLikeThis {
    /// Creates a [`BooleanQuery`] using a document address to collect
    /// the top stored field values.
    pub fn query_with_document(
        &self,
        searcher: &Searcher,
        doc_address: DocAddress,
    ) -> Result<BooleanQuery> {
        let score_terms = self.retrieve_terms_from_doc_address(searcher, doc_address)?;
        let query = self.create_query(score_terms);
        Ok(query)
    }

    /// Creates a [`BooleanQuery`] using a set of field values.
    pub fn query_with_document_fields(
        &self,
        searcher: &Searcher,
        doc_fields: &[(Field, Vec<FieldValue>)],
    ) -> Result<BooleanQuery> {
        let score_terms = self.retrieve_terms_from_doc_fields(searcher, doc_fields)?;
        let query = self.create_query(score_terms);
        Ok(query)
    }

    /// Creates a [`BooleanQuery`] from an ascendingly sorted list of ScoreTerm
    /// This will map the list of ScoreTerm to a list of [`TermQuery`]  and compose a
    /// BooleanQuery using that list as sub queries.
    fn create_query(&self, mut score_terms: Vec<ScoreTerm>) -> BooleanQuery {
        score_terms.sort_by(|left_ts, right_ts| right_ts.cmp(left_ts));
        let best_score = score_terms.first().map_or(1f32, |x| x.score);
        let mut queries = Vec::new();

        for ScoreTerm { term, score } in score_terms {
            let mut query: Box<dyn Query> =
                Box::new(TermQuery::new(term, IndexRecordOption::Basic));
            if let Some(factor) = self.boost_factor {
                query = Box::new(BoostQuery::new(query, score * factor / best_score));
            }
            queries.push((Occur::Should, query));
        }
        BooleanQuery::from(queries)
    }

    /// Finds terms for a more-like-this query.
    /// doc_address is the address of document from which to find terms.
    fn retrieve_terms_from_doc_address(
        &self,
        searcher: &Searcher,
        doc_address: DocAddress,
    ) -> Result<Vec<ScoreTerm>> {
        let doc = searcher.doc(doc_address)?;
        let field_to_field_values = doc
            .get_sorted_field_values()
            .iter()
            .map(|(field, values)| {
                (
                    *field,
                    values
                        .iter()
                        .map(|v| (**v).clone())
                        .collect::<Vec<FieldValue>>(),
                )
            })
            .collect::<Vec<_>>();
        self.retrieve_terms_from_doc_fields(searcher, &field_to_field_values)
    }

    /// Finds terms for a more-like-this query.
    /// field_to_field_values is a mapping from field to possible values of taht field.
    fn retrieve_terms_from_doc_fields(
        &self,
        searcher: &Searcher,
        field_to_field_values: &[(Field, Vec<FieldValue>)],
    ) -> Result<Vec<ScoreTerm>> {
        if field_to_field_values.is_empty() {
            return Err(TantivyError::InvalidArgument("Cannot create more like this query on empty field values. The document may not have stored fields".to_string()));
        }

        let mut field_to_term_freq_map = HashMap::new();
        for (field, field_values) in field_to_field_values {
            self.add_term_frequencies(searcher, *field, field_values, &mut field_to_term_freq_map)?;
        }
        self.create_score_term(searcher, field_to_term_freq_map)
    }

    /// Computes the frequency of values for a field while updating the term frequencies
    /// Note: A FieldValue can be made up of multiple terms.
    /// We are interested in extracting terms within FieldValue
    fn add_term_frequencies(
        &self,
        searcher: &Searcher,
        field: Field,
        field_values: &[FieldValue],
        term_frequencies: &mut HashMap<Term, usize>,
    ) -> Result<()> {
        let schema = searcher.schema();
        let tokenizer_manager = searcher.index().tokenizers();

        let field_entry = schema.get_field_entry(field);
        if !field_entry.is_indexed() {
            return Ok(());
        }

        // extract the raw value, possibly tokenizing & filtering to update the term frequency map
        match field_entry.field_type() {
            FieldType::HierarchicalFacet(_) => {
                let facets: Vec<&str> = field_values
                    .iter()
                    .map(|field_value| match *field_value.value() {
                        Value::Facet(ref facet) => Ok(facet.encoded_str()),
                        _ => Err(TantivyError::InvalidArgument(
                            "invalid field value".to_string(),
                        )),
                    })
                    .collect::<Result<Vec<_>>>()?;
                for fake_str in facets {
                    FacetTokenizer.token_stream(fake_str).process(&mut |token| {
                        if self.is_noise_word(token.text.clone()) {
                            let term = Term::from_field_text(field, &token.text);
                            *term_frequencies.entry(term).or_insert(0) += 1;
                        }
                    });
                }
            }
            FieldType::Str(text_options) => {
                let mut token_streams: Vec<BoxTokenStream> = vec![];

                for field_value in field_values {
                    match field_value.value() {
                        Value::PreTokStr(tok_str) => {
                            token_streams.push(PreTokenizedStream::from(tok_str.clone()).into());
                        }
                        Value::Str(ref text) => {
                            if let Some(tokenizer) = text_options
                                .get_indexing_options()
                                .map(|text_indexing_options| {
                                    text_indexing_options.tokenizer().to_string()
                                })
                                .and_then(|tokenizer_name| tokenizer_manager.get(&tokenizer_name))
                            {
                                token_streams.push(tokenizer.token_stream(text));
                            }
                        }
                        _ => (),
                    }
                }

                for mut token_stream in token_streams {
                    token_stream.process(&mut |token| {
                        if !self.is_noise_word(token.text.clone()) {
                            let term = Term::from_field_text(field, &token.text);
                            *term_frequencies.entry(term).or_insert(0) += 1;
                        }
                    });
                }
            }
            FieldType::U64(_) => {
                for field_value in field_values {
                    let val = field_value
                        .value()
                        .u64_value()
                        .ok_or(TantivyError::InvalidArgument("invalid value".to_string()))?;
                    if !self.is_noise_word(val.to_string()) {
                        let term = Term::from_field_u64(field, val);
                        *term_frequencies.entry(term).or_insert(0) += 1;
                    }
                }
            }
            FieldType::Date(_) => {
                for field_value in field_values {
                    // TODO: Ask if this is the semantic (timestamp) we want
                    let val = field_value
                        .value()
                        .date_value()
                        .ok_or(TantivyError::InvalidArgument("invalid value".to_string()))?
                        .timestamp();
                    if !self.is_noise_word(val.to_string()) {
                        let term = Term::from_field_i64(field, val);
                        *term_frequencies.entry(term).or_insert(0) += 1;
                    }
                }
            }
            FieldType::I64(_) => {
                for field_value in field_values {
                    let val = field_value
                        .value()
                        .i64_value()
                        .ok_or(TantivyError::InvalidArgument("invalid value".to_string()))?;
                    if !self.is_noise_word(val.to_string()) {
                        let term = Term::from_field_i64(field, val);
                        *term_frequencies.entry(term).or_insert(0) += 1;
                    }
                }
            }
            FieldType::F64(_) => {
                for field_value in field_values {
                    let val = field_value
                        .value()
                        .f64_value()
                        .ok_or(TantivyError::InvalidArgument("invalid value".to_string()))?;
                    if !self.is_noise_word(val.to_string()) {
                        let term = Term::from_field_f64(field, val);
                        *term_frequencies.entry(term).or_insert(0) += 1;
                    }
                }
            }
            _ => {}
        }
        Ok(())
    }

    /// Determines if the term is likely to be of interest based on "more-like-this" settings
    fn is_noise_word(&self, word: String) -> bool {
        let word_length = word.len();
        if word_length == 0 {
            return true;
        }
        if self
            .min_word_length
            .map(|min| word_length < min)
            .unwrap_or(false)
        {
            return true;
        }
        if self
            .max_word_length
            .map(|max| word_length > max)
            .unwrap_or(false)
        {
            return true;
        }
        return self.stop_words.contains(&word);
    }

    /// Couputes the score for each term while ignoring not useful terms
    fn create_score_term(
        &self,
        searcher: &Searcher,
        per_field_term_frequencies: HashMap<Term, usize>,
    ) -> Result<Vec<ScoreTerm>> {
        let mut score_terms: BinaryHeap<Reverse<ScoreTerm>> = BinaryHeap::new();
        let num_docs = searcher
            .segment_readers()
            .iter()
            .map(|segment_reader| segment_reader.num_docs() as u64)
            .sum::<u64>();

        for (term, term_frequency) in per_field_term_frequencies.iter() {
            // ignore terms with less than min_term_frequency
            if self
                .min_term_frequency
                .map(|min_term_frequency| *term_frequency < min_term_frequency)
                .unwrap_or(false)
            {
                continue;
            }

            let doc_freq = searcher.doc_freq(&term)?;

            // ignore terms with less than min_doc_frequency
            if self
                .min_doc_frequency
                .map(|min_doc_frequency| doc_freq < min_doc_frequency)
                .unwrap_or(false)
            {
                continue;
            }

            // ignore terms with more than max_doc_frequency
            if self
                .max_doc_frequency
                .map(|max_doc_frequency| doc_freq > max_doc_frequency)
                .unwrap_or(false)
            {
                continue;
            }

            // ignore terms with zero frequency
            if doc_freq == 0 {
                continue;
            }

            // compute similarity & score
            let idf = idf(doc_freq, num_docs);
            let score = (*term_frequency as f32) * idf;
            if let Some(limit) = self.max_query_terms {
                if score_terms.len() > limit {
                    // update the least significant term
                    let least_significant_term_score = score_terms.peek().unwrap().0.score;
                    if least_significant_term_score < score {
                        score_terms.peek_mut().unwrap().0 = ScoreTerm::new(term.clone(), score);
                    }
                } else {
                    score_terms.push(Reverse(ScoreTerm::new(term.clone(), score)));
                }
            } else {
                score_terms.push(Reverse(ScoreTerm::new(term.clone(), score)));
            }
        }

        let score_terms_vec: Vec<ScoreTerm> = score_terms
            .into_iter()
            .map(|reverse_score_term| reverse_score_term.0)
            .collect();

        Ok(score_terms_vec)
    }
}
<|endoftext|>


<commit_before>use std::net::SocketAddr;
use fibers::net::{self, TcpStream};
use futures::{Future, Poll};

pub use self::request::{Request, RequestBuilder};
pub use self::response::{Response, ReadResponse};

use {Error, Method, Version};
use defaults;
use connection::{self, TransportStream};

mod request;
mod response;

/// HTTP client.
#[derive(Debug, Clone)]
pub struct Client {
    max_response_header_count: usize,
    min_buffer_size: usize,
    max_buffer_size: usize,
    version: Version,
}
impl Client {
    pub fn new() -> Self {
        Client {
            max_response_header_count: defaults::MAX_HEADER_COUNT,
            min_buffer_size: defaults::MIN_BUFFER_SIZE,
            max_buffer_size: defaults::MAX_BUFFER_SIZE,
            version: Version::default(),
        }
    }
    pub fn max_response_header_count(&mut self, count: usize) -> &mut Self {
        self.max_response_header_count = count;
        self
    }
    pub fn min_buffer_size(&mut self, size: usize) -> &mut Self {
        assert!(size <= self.max_buffer_size);
        self.min_buffer_size = size;
        self
    }
    pub fn max_buffer_size(&mut self, size: usize) -> &mut Self {
        assert!(self.min_buffer_size <= size);
        self.max_buffer_size = size;
        self
    }
    pub fn version(&mut self, version: Version) -> &mut Self {
        self.version = version;
        self
    }
    pub fn connect(&self, server_addr: SocketAddr) -> Connect {
        Connect {
            client: self.clone(),
            future: TcpStream::connect(server_addr),
        }
    }
}

#[derive(Debug)]
pub struct Connection<T> {
    inner: connection::Connection<T>,
    version: Version,
}
impl<T: TransportStream> Connection<T> {
    fn new(stream: T, client: &Client) -> Self {
        let max_header_count = client.max_response_header_count;
        let inner = connection::Connection::new(
            stream,
            client.min_buffer_size,
            client.max_buffer_size,
            max_header_count,
        );
        Connection {
            inner: inner,
            version: client.version,
        }
    }
    pub fn build_request(self, method: Method, path: &str) -> RequestBuilder<T> {
        request::builder(self, method, path)
    }

    pub fn read_response(self) -> ReadResponse<T> {
        ReadResponse::new(self)
    }
}
impl<T> AsMut<connection::Connection<T>> for Connection<T> {
    fn as_mut(&mut self) -> &mut connection::Connection<T> {
        &mut self.inner
    }
}

#[derive(Debug)]
pub struct Connect {
    client: Client,
    future: net::futures::Connect,
}
impl Future for Connect {
    type Item = Connection<TcpStream>;
    type Error = Error;
    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        Ok(track!(self.future.poll().map_err(Error::from))?.map(
            |socket| {
                Connection::new(socket, &self.client)
            },
        ))
    }
}
<commit_msg>Add nodelay option<commit_after>use std::net::SocketAddr;
use fibers::net::{self, TcpStream};
use futures::{Future, Poll};

pub use self::request::{Request, RequestBuilder};
pub use self::response::{Response, ReadResponse};

use {Error, Method, Version};
use defaults;
use connection::{self, TransportStream};

mod request;
mod response;

/// HTTP client.
#[derive(Debug, Clone)]
pub struct Client {
    max_response_header_count: usize,
    min_buffer_size: usize,
    max_buffer_size: usize,
    version: Version,

    nodelay: bool,
}
impl Client {
    pub fn new() -> Self {
        Client {
            max_response_header_count: defaults::MAX_HEADER_COUNT,
            min_buffer_size: defaults::MIN_BUFFER_SIZE,
            max_buffer_size: defaults::MAX_BUFFER_SIZE,
            version: Version::default(),
            nodelay: true,
        }
    }
    pub fn max_response_header_count(&mut self, count: usize) -> &mut Self {
        self.max_response_header_count = count;
        self
    }
    pub fn min_buffer_size(&mut self, size: usize) -> &mut Self {
        assert!(size <= self.max_buffer_size);
        self.min_buffer_size = size;
        self
    }
    pub fn max_buffer_size(&mut self, size: usize) -> &mut Self {
        assert!(self.min_buffer_size <= size);
        self.max_buffer_size = size;
        self
    }
    pub fn version(&mut self, version: Version) -> &mut Self {
        self.version = version;
        self
    }
    pub fn nodelay(&mut self, b: bool) -> &mut Self {
        self.nodelay = b;
        self
    }
    pub fn connect(&self, server_addr: SocketAddr) -> Connect {
        Connect {
            client: self.clone(),
            future: TcpStream::connect(server_addr),
            nodelay: self.nodelay,
        }
    }
}

#[derive(Debug)]
pub struct Connection<T> {
    inner: connection::Connection<T>,
    version: Version,
}
impl<T: TransportStream> Connection<T> {
    fn new(stream: T, client: &Client) -> Self {
        let max_header_count = client.max_response_header_count;
        let inner = connection::Connection::new(
            stream,
            client.min_buffer_size,
            client.max_buffer_size,
            max_header_count,
        );
        Connection {
            inner: inner,
            version: client.version,
        }
    }
    pub fn build_request(self, method: Method, path: &str) -> RequestBuilder<T> {
        request::builder(self, method, path)
    }

    pub fn read_response(self) -> ReadResponse<T> {
        ReadResponse::new(self)
    }
}
impl<T> AsMut<connection::Connection<T>> for Connection<T> {
    fn as_mut(&mut self) -> &mut connection::Connection<T> {
        &mut self.inner
    }
}

#[derive(Debug)]
pub struct Connect {
    client: Client,
    future: net::futures::Connect,
    nodelay: bool,
}
impl Future for Connect {
    type Item = Connection<TcpStream>;
    type Error = Error;
    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        Ok(
            track!(self.future.poll().map_err(Error::from))?.map(|socket| {
                unsafe {
                    let _ = socket.with_inner(|socket| socket.set_nodelay(self.nodelay));
                }
                Connection::new(socket, &self.client)
            }),
        )
    }
}
<|endoftext|>