<commit_before>use std::path::{Path, PathBuf};
use std::fs::File;
use std::str::Lines;
use std::io::{ Read, BufRead, BufReader };
use std::iter::Peekable;

use nom::IResult;

use inner_tree::*;
use dts_parser::parse_include;

#[derive(Debug)]
pub enum LinemarkerFlag {
    Start,
    Return,
    System,
    Extern,
}

pub fn parse_cpp_outputs<'a>(cpp_stderr: &'a str,
                             cpp_output: &Path,
                             root_file_name: &str)
                             -> Result<(ParsedFile, Vec<u8>), String> {
    let mut include_tree = ParsedFile::new(PathBuf::from(&root_file_name), IncludeMethod::CPP);
    parse_cpp_stderr(&mut cpp_stderr.lines().peekable(), &mut include_tree, 0);
    // println!("{:#?}", include_tree);
    parse_cpp_file(cpp_output, &mut include_tree);
    // println!("{:#?}", include_tree);
    let result = include_dts_files(cpp_output, &mut include_tree, 0)?;
    Ok((include_tree, result))
}

// parse stderr to get the include tree
fn parse_cpp_stderr(lines: &mut Peekable<Lines>, parent_file: &mut ParsedFile, depth: usize) {
    while let Some(line) = lines.peek().cloned() {

        let mut count: usize = 0;
        for sc in line.chars() {
            if sc == '.' {
                count += 1;
            } else {
                break;
            }
        }
        let count = count;

        if count == 0 || count < depth || count == depth {
            return;
        } else if count > depth && (count - depth) == 1 {
            parent_file.add_include(ParsedFile::new(PathBuf::from(lines.next()
                                                        .unwrap()
                                                        .trim_left_matches('.')
                                                        .trim_left()),
                                                    IncludeMethod::CPP));
            parse_cpp_stderr(lines,
                             parent_file.included_files.last_mut().unwrap(),
                             depth + 1);
        } else {
            unreachable!();
        }
    }
}

// parse preprocessed file to find starts (and stops) of included files
fn parse_cpp_file(cpp_output: &Path, include_tree: &mut ParsedFile) {
    let global_buffer = BufReader::new(File::open(cpp_output).unwrap());
    let mut parsed_lines = global_buffer.lines()
        .enumerate()
        .filter(|&(_, ref line)| match *line {
            Ok(ref l) => l.starts_with('#'),
            Err(_) => false,
        })
        .map(|(line_num, line)| {
            let line = line.unwrap();
            let tokens: Vec<&str> = line.split('"').map(|s| s.trim_matches('#').trim()).collect();
            let child_num: usize = tokens[0].parse().unwrap();  //TODO: check length before assuming
            let flag = match tokens[2].parse().unwrap_or(0) { //TODO: make conversion function
                1 => Some(LinemarkerFlag::Start),
                2 => Some(LinemarkerFlag::Return),
                3 => Some(LinemarkerFlag::System),
                4 => Some(LinemarkerFlag::Extern),
                _ => None,
            };
            (line_num + 1, child_num, PathBuf::from(tokens[1]), flag)
        })
        .peekable();

    while let Some((path, mapping)) =
        parse_cpp_linemarkers(&parsed_lines.next(),
                              &parsed_lines.peek(),
                              Path::new(cpp_output))
            .unwrap() {
        if let Some(m) = mapping {
            include_tree.assign_mapping(path, m).expect("Failed to assign mapping");
        }
    }
}

fn parse_cpp_linemarkers<'a>(current: &'a Option<(usize, usize, PathBuf, Option<LinemarkerFlag>)>,
                             next: &Option<&(usize, usize, PathBuf, Option<LinemarkerFlag>)>,
                             cpp_output: &Path)
                             -> Result<Option<(&'a Path, Option<FileMapping>)>, String> {
    if let Some((c_line_num, c_child_num, ref c_path, _)) = *current {
        if let Some(&(n_line_num, _, _, _)) = *next {
            Ok(Some((c_path,
                     if n_line_num - c_line_num > 1 {
                         Some(FileMapping {
                             parent_start:
                                 line_to_byte_offset(File::open(cpp_output)
                                                         .expect(&format!("File not found: {}",
                                                                          cpp_output.to_str()
                                                                              .unwrap()))
                                                         .bytes()
                                                         .filter_map(|e| e.ok()),
                                                     c_line_num + 1)?,
                             child_start:
                                 line_to_byte_offset(File::open(c_path)
                                                         .expect(&format!("File not found: {}",
                                                                          c_path.to_str()
                                                                              .unwrap()))
                                                         .bytes()
                                                         .filter_map(|e| e.ok()),
                                                     c_child_num)?,
                             len: line_to_byte_offset(File::open(cpp_output)
                                                          .expect(&format!("File not found: {}",
                                                                           cpp_output.to_str()
                                                                               .unwrap()))
                                                          .bytes()
                                                          .filter_map(|e| e.ok()),
                                                      n_line_num)? -
                                  line_to_byte_offset(File::open(cpp_output)
                                                          .expect(&format!("File not found: {}",
                                                                           cpp_output.to_str()
                                                                               .unwrap()))
                                                          .bytes()
                                                          .filter_map(|e| e.ok()),
                                                      c_line_num + 1)?,
                         })
                     } else {
                         None
                     })))
        } else {
            let last_byte = File::open(cpp_output).unwrap().bytes().count();
            Ok(Some((c_path,
                     Some(FileMapping {
                         parent_start: line_to_byte_offset(File::open(cpp_output)
                                                               .unwrap()
                                                               .bytes()
                                                               .filter_map(|e| e.ok()),
                                                           c_line_num + 1)?,
                         child_start: line_to_byte_offset(File::open(c_path)
                                                              .unwrap()
                                                              .bytes()
                                                              .filter_map(|e| e.ok()),
                                                          c_child_num)?,
                         len: last_byte -
                              line_to_byte_offset(File::open(cpp_output)
                                                      .unwrap()
                                                      .bytes()
                                                      .filter_map(|e| e.ok()),
                                                  c_line_num + 1)?,
                     }))))
        }
    } else {
        Ok(None)
    }
}

fn include_dts_files(file: &Path,
                     include_tree: &mut ParsedFile,
                     main_offset: usize)
                     -> Result<Vec<u8>, String> {
    let mut file = File::open(file).unwrap();
    let mut buffer: Vec<u8> = Vec::new();

    let mut string_buffer = String::new();
    file.read_to_string(&mut string_buffer).map_err(|_| "IO Error".to_string())?;

    let mut buf = string_buffer.as_bytes();
    loop {
        // go until /include/
        buf = if let Some(offset) = buf.windows(9).position(|sub| sub == b"/include/") {
            buffer.extend_from_slice(&buf[..offset]);
            if let IResult::Done(rem, file) = parse_include(&buf[offset..]) {
                // println!("{}", file);
                // println!("Offset: {}", offset);
                // println!("{}", include_tree);

                let eaten_len = (buf.len() - offset) - rem.len();

                let included_path = Path::new(&file);
                let mut included_file = ParsedFile::new(included_path.to_path_buf(),
                                                        IncludeMethod::DTS);
                let total_len = buffer.len() + main_offset;
                included_file.mappings.push(FileMapping {
                    parent_start: total_len,
                    child_start: 0,
                    // TODO: check from parent directory of root file
                    len: File::open(&included_path).unwrap().bytes().count(),
                });
                buffer.extend(include_dts_files(included_path, &mut included_file, total_len)?);

                let (inc_start, inc_end) = included_file.bounds_of_tree()?;
                include_tree.offset_after_location(inc_start, inc_end as isize - inc_start as isize);
                // println!("After offset");
                // println!("{}", include_tree);

                {
                    let inc_file = include_tree.file_from_offset_mut(inc_start)?;
                    inc_file.split_mappings(inc_start, inc_end, eaten_len);
                    inc_file.add_include(included_file);
                }
                // println!("After split");
                // println!("{}", include_tree);

                rem
            } else {
                buffer.extend_from_slice(&buf[offset..offset + 9]);
                &buf[offset + 9..]
            }
        } else {
            // no more includes, just add the rest and return
            buffer.extend(buf);
            return Ok(buffer);
        };
    }
}
<commit_msg>Fix file mapping starts and lengths when inserting DTS includes<commit_after>use std::path::{Path, PathBuf};
use std::fs::File;
use std::str::Lines;
use std::io::{ Read, BufRead, BufReader };
use std::iter::Peekable;

use nom::IResult;

use inner_tree::*;
use dts_parser::parse_include;

#[derive(Debug)]
pub enum LinemarkerFlag {
    Start,
    Return,
    System,
    Extern,
}

pub fn parse_cpp_outputs<'a>(cpp_stderr: &'a str,
                             cpp_output: &Path,
                             root_file_name: &str)
                             -> Result<(ParsedFile, Vec<u8>), String> {
    let mut include_tree = ParsedFile::new(PathBuf::from(&root_file_name), IncludeMethod::CPP);
    parse_cpp_stderr(&mut cpp_stderr.lines().peekable(), &mut include_tree, 0);
    // println!("{:#?}", include_tree);
    parse_cpp_file(cpp_output, &mut include_tree);
    // println!("{:#?}", include_tree);
    let result = include_dts_files(cpp_output, &mut include_tree, 0)?;
    Ok((include_tree, result))
}

// parse stderr to get the include tree
fn parse_cpp_stderr(lines: &mut Peekable<Lines>, parent_file: &mut ParsedFile, depth: usize) {
    while let Some(line) = lines.peek().cloned() {

        let mut count: usize = 0;
        for sc in line.chars() {
            if sc == '.' {
                count += 1;
            } else {
                break;
            }
        }
        let count = count;

        if count == 0 || count < depth || count == depth {
            return;
        } else if count > depth && (count - depth) == 1 {
            parent_file.add_include(ParsedFile::new(PathBuf::from(lines.next()
                                                        .unwrap()
                                                        .trim_left_matches('.')
                                                        .trim_left()),
                                                    IncludeMethod::CPP));
            parse_cpp_stderr(lines,
                             parent_file.included_files.last_mut().unwrap(),
                             depth + 1);
        } else {
            unreachable!();
        }
    }
}

// parse preprocessed file to find starts (and stops) of included files
fn parse_cpp_file(cpp_output: &Path, include_tree: &mut ParsedFile) {
    let global_buffer = BufReader::new(File::open(cpp_output).unwrap());
    let mut parsed_lines = global_buffer.lines()
        .enumerate()
        .filter(|&(_, ref line)| match *line {
            Ok(ref l) => l.starts_with('#'),
            Err(_) => false,
        })
        .map(|(line_num, line)| {
            let line = line.unwrap();
            let tokens: Vec<&str> = line.split('"').map(|s| s.trim_matches('#').trim()).collect();
            let child_num: usize = tokens[0].parse().unwrap();  //TODO: check length before assuming
            let flag = match tokens[2].parse().unwrap_or(0) { //TODO: make conversion function
                1 => Some(LinemarkerFlag::Start),
                2 => Some(LinemarkerFlag::Return),
                3 => Some(LinemarkerFlag::System),
                4 => Some(LinemarkerFlag::Extern),
                _ => None,
            };
            (line_num + 1, child_num, PathBuf::from(tokens[1]), flag)
        })
        .peekable();

    while let Some((path, mapping)) =
        parse_cpp_linemarkers(&parsed_lines.next(),
                              &parsed_lines.peek(),
                              Path::new(cpp_output))
            .unwrap() {
        if let Some(m) = mapping {
            include_tree.assign_mapping(path, m).expect("Failed to assign mapping");
        }
    }
}

fn parse_cpp_linemarkers<'a>(current: &'a Option<(usize, usize, PathBuf, Option<LinemarkerFlag>)>,
                             next: &Option<&(usize, usize, PathBuf, Option<LinemarkerFlag>)>,
                             cpp_output: &Path)
                             -> Result<Option<(&'a Path, Option<FileMapping>)>, String> {
    if let Some((c_line_num, c_child_num, ref c_path, _)) = *current {
        if let Some(&(n_line_num, _, _, _)) = *next {
            Ok(Some((c_path,
                     if n_line_num - c_line_num > 1 {
                         Some(FileMapping {
                             parent_start:
                                 line_to_byte_offset(File::open(cpp_output)
                                                         .expect(&format!("File not found: {}",
                                                                          cpp_output.to_str()
                                                                              .unwrap()))
                                                         .bytes()
                                                         .filter_map(|e| e.ok()),
                                                     c_line_num + 1)?,
                             child_start:
                                 line_to_byte_offset(File::open(c_path)
                                                         .expect(&format!("File not found: {}",
                                                                          c_path.to_str()
                                                                              .unwrap()))
                                                         .bytes()
                                                         .filter_map(|e| e.ok()),
                                                     c_child_num)?,
                             len: line_to_byte_offset(File::open(cpp_output)
                                                          .expect(&format!("File not found: {}",
                                                                           cpp_output.to_str()
                                                                               .unwrap()))
                                                          .bytes()
                                                          .filter_map(|e| e.ok()),
                                                      n_line_num)? -
                                  line_to_byte_offset(File::open(cpp_output)
                                                          .expect(&format!("File not found: {}",
                                                                           cpp_output.to_str()
                                                                               .unwrap()))
                                                          .bytes()
                                                          .filter_map(|e| e.ok()),
                                                      c_line_num + 1)?,
                         })
                     } else {
                         None
                     })))
        } else {
            let last_byte = File::open(cpp_output).unwrap().bytes().count();
            Ok(Some((c_path,
                     Some(FileMapping {
                         parent_start: line_to_byte_offset(File::open(cpp_output)
                                                               .unwrap()
                                                               .bytes()
                                                               .filter_map(|e| e.ok()),
                                                           c_line_num + 1)?,
                         child_start: line_to_byte_offset(File::open(c_path)
                                                              .unwrap()
                                                              .bytes()
                                                              .filter_map(|e| e.ok()),
                                                          c_child_num)?,
                         len: last_byte -
                              line_to_byte_offset(File::open(cpp_output)
                                                      .unwrap()
                                                      .bytes()
                                                      .filter_map(|e| e.ok()),
                                                  c_line_num + 1)?,
                     }))))
        }
    } else {
        Ok(None)
    }
}

fn include_dts_files(file: &Path,
                     include_tree: &mut ParsedFile,
                     main_offset: usize)
                     -> Result<Vec<u8>, String> {
    let mut file = File::open(file).unwrap();
    let mut buffer: Vec<u8> = Vec::new();

    let mut string_buffer = String::new();
    file.read_to_string(&mut string_buffer).map_err(|_| "IO Error".to_string())?;

    let mut buf = string_buffer.as_bytes();
    loop {
        // go until /include/
        buf = if let Some(offset) = buf.windows(9).position(|sub| sub == b"/include/") {
            buffer.extend_from_slice(&buf[..offset]);
            if let IResult::Done(rem, file) = parse_include(&buf[offset..]) {
                // println!("{}", file);
                // println!("Offset: {}", offset);
                // println!("{}", include_tree);

                let eaten_len = (buf.len() - offset) - rem.len();

                let included_path = Path::new(&file);
                let mut included_file = ParsedFile::new(included_path.to_path_buf(),
                                                        IncludeMethod::DTS);
                let total_len = buffer.len() + main_offset - 1;
                included_file.mappings.push(FileMapping {
                    parent_start: total_len,
                    child_start: 0,
                    // TODO: check from parent directory of root file
                    len: File::open(&included_path).unwrap().bytes().count(),
                });
                buffer.extend(include_dts_files(included_path, &mut included_file, total_len)?);

                let (inc_start, inc_end) = included_file.bounds_of_tree()?;
                include_tree.offset_after_location(inc_start,
                                                   inc_end as isize
                                                   - inc_start as isize
                                                   - eaten_len as isize);
                // println!("After offset");
                // println!("{}", include_tree);

                {
                    let inc_file = include_tree.file_from_offset_mut(inc_start)?;
                    inc_file.split_mappings(inc_start, inc_end, eaten_len);
                    inc_file.add_include(included_file);
                }
                // println!("After split");
                // println!("{}", include_tree);

                rem
            } else {
                buffer.extend_from_slice(&buf[offset..offset + 9]);
                &buf[offset + 9..]
            }
        } else {
            // no more includes, just add the rest and return
            buffer.extend(buf);
            return Ok(buffer);
        };
    }
}
<|endoftext|>


<commit_before>use std::io::Write;

#[macro_use]
mod common;

use common::util::*;

static UTIL_NAME: &'static str = "tail";

static INPUT: &'static str = "foobar.txt";

#[test]
fn test_stdin_default() {
    let (at, mut ucmd) = testing(UTIL_NAME);
    let result = ucmd.run_piped_stdin(at.read(INPUT));
    assert_eq!(result.stdout, at.read("foobar_stdin_default.expected"));
}

#[test]
fn test_single_default() {
    let (at, mut ucmd) = testing(UTIL_NAME);
    let result = ucmd.arg(INPUT).run();
    assert_eq!(result.stdout, at.read("foobar_single_default.expected"));
}

#[test]
fn test_single_big_args() {
    const FILE: &'static str = "single_big_args.txt";
    const EXPECTED_FILE: &'static str = "single_big_args_expected.txt";
    const LINES: usize = 1_000_000;
    const N_ARG: usize = 100_000;

    let (at, mut ucmd) = testing(UTIL_NAME);

    let mut big_input = at.make_scoped_file(FILE);
    for i in 0..LINES {
        write!(&mut big_input, "Line {}\n", i).expect("Could not write to FILE");
    }
    big_input.flush().expect("Could not flush FILE");

    let mut big_expected = at.make_scoped_file(EXPECTED_FILE);
    for i in (LINES - N_ARG)..LINES {
        write!(&mut big_expected, "Line {}\n", i).expect("Could not write to EXPECTED_FILE");
    }
    big_expected.flush().expect("Could not flush EXPECTED_FILE");

    let result = ucmd.arg(FILE).arg("-n").arg(format!("{}", N_ARG)).run();
    assert_eq!(result.stdout, at.read(EXPECTED_FILE));
}

#[test]
fn test_bytes_single() {
    let (at, mut ucmd) = testing(UTIL_NAME);
    let result = ucmd.arg("-c").arg("10").arg(INPUT).run();
    assert_eq!(result.stdout, at.read("foobar_bytes_single.expected"));
}

#[test]
fn test_bytes_stdin() {
    let (at, mut ucmd) = testing(UTIL_NAME);
    let result = ucmd.arg("-c").arg("13").run_piped_stdin(at.read(INPUT));
    assert_eq!(result.stdout, at.read("foobar_bytes_stdin.expected"));
}
<commit_msg>tests/tail: Add a test for tail'ing large files in bytes mode<commit_after>use std::io::Write;

#[macro_use]
mod common;

use common::util::*;

static UTIL_NAME: &'static str = "tail";

static INPUT: &'static str = "foobar.txt";

#[test]
fn test_stdin_default() {
    let (at, mut ucmd) = testing(UTIL_NAME);
    let result = ucmd.run_piped_stdin(at.read(INPUT));
    assert_eq!(result.stdout, at.read("foobar_stdin_default.expected"));
}

#[test]
fn test_single_default() {
    let (at, mut ucmd) = testing(UTIL_NAME);
    let result = ucmd.arg(INPUT).run();
    assert_eq!(result.stdout, at.read("foobar_single_default.expected"));
}

#[test]
fn test_single_big_args() {
    const FILE: &'static str = "single_big_args.txt";
    const EXPECTED_FILE: &'static str = "single_big_args_expected.txt";
    const LINES: usize = 1_000_000;
    const N_ARG: usize = 100_000;

    let (at, mut ucmd) = testing(UTIL_NAME);

    let mut big_input = at.make_scoped_file(FILE);
    for i in 0..LINES {
        write!(&mut big_input, "Line {}\n", i).expect("Could not write to FILE");
    }
    big_input.flush().expect("Could not flush FILE");

    let mut big_expected = at.make_scoped_file(EXPECTED_FILE);
    for i in (LINES - N_ARG)..LINES {
        write!(&mut big_expected, "Line {}\n", i).expect("Could not write to EXPECTED_FILE");
    }
    big_expected.flush().expect("Could not flush EXPECTED_FILE");

    let result = ucmd.arg(FILE).arg("-n").arg(format!("{}", N_ARG)).run();
    assert_eq!(result.stdout, at.read(EXPECTED_FILE));
}

#[test]
fn test_bytes_single() {
    let (at, mut ucmd) = testing(UTIL_NAME);
    let result = ucmd.arg("-c").arg("10").arg(INPUT).run();
    assert_eq!(result.stdout, at.read("foobar_bytes_single.expected"));
}

#[test]
fn test_bytes_stdin() {
    let (at, mut ucmd) = testing(UTIL_NAME);
    let result = ucmd.arg("-c").arg("13").run_piped_stdin(at.read(INPUT));
    assert_eq!(result.stdout, at.read("foobar_bytes_stdin.expected"));
}

#[test]
fn test_bytes_big() {
    const FILE: &'static str = "test_bytes_big.txt";
    const EXPECTED_FILE: &'static str = "test_bytes_big_expected.txt";
    const BYTES: usize = 1_000_000;
    const N_ARG: usize = 100_000;

    let (at, mut ucmd) = testing(UTIL_NAME);

    let mut big_input = at.make_scoped_file(FILE);
    for i in 0..BYTES {
        let digit = std::char::from_digit((i % 10) as u32, 10).unwrap();
        write!(&mut big_input, "{}", digit).expect("Could not write to FILE");
    }
    big_input.flush().expect("Could not flush FILE");

    let mut big_expected = at.make_scoped_file(EXPECTED_FILE);
    for i in (BYTES - N_ARG)..BYTES {
        let digit = std::char::from_digit((i % 10) as u32, 10).unwrap();
        write!(&mut big_expected, "{}", digit).expect("Could not write to EXPECTED_FILE");
    }
    big_expected.flush().expect("Could not flush EXPECTED_FILE");

    let result = ucmd.arg(FILE).arg("-c").arg(format!("{}", N_ARG)).run().stdout;
    let expected = at.read(EXPECTED_FILE);

    assert_eq!(result.len(), expected.len());
    for (actual_char, expected_char) in result.chars().zip(expected.chars()) {
        assert_eq!(actual_char, expected_char);
    }
}
<|endoftext|>


<commit_before>use crate::{
    command_line::{CommandLine, Entry},
    schema::{default_renderer, FieldValidator, KindValue, ValidatorContext, SCHEMA},
    types::{Hash, Model, Msg, Node, Selector},
};
use std::{collections::BTreeMap, rc::Rc};
use web_sys::HtmlInputElement;
use yew::prelude::*;

pub struct NodeComponent {
    input_node_ref: NodeRef,
}

#[derive(Properties, PartialEq)]
pub struct NodeProperties {
    pub path: Vec<Selector>,
    pub model: Rc<Model>,
    #[prop_or_default]
    pub hash: Option<Hash>,
    #[prop_or_default]
    pub placeholder: String,
    // When a new value is typed in the text box.
    #[prop_or_default]
    pub oninput: Callback<String>,
    // When the node is selected (e.g. clicked).
    #[prop_or_default]
    pub onselect: Callback<Vec<Selector>>,
    #[prop_or_default]
    pub updatemodel: Callback<Msg>,
    #[prop_or_default]
    pub validators: &'static [FieldValidator],
}

pub enum NodeMsg {
    Click,
}

pub const FIELD_CLASSES: &[&str] = &[
    "bg-blue-300",
    "inline-block",
    "p-0.5",
    "px-1",
    "border-blue-800",
    "border",
    "rounded",
];
pub const KIND_CLASSES: &[&str] = &[
    "kind",
    "bg-yellow-400",
    "border-yellow-800",
    "border-4",
    "inline-block",
    "p-0.5",
    "px-1",
];

impl Component for NodeComponent {
    type Message = NodeMsg;
    type Properties = NodeProperties;

    fn create(ctx: &Context<Self>) -> Self {
        log::debug!("creating node");
        Self {
            input_node_ref: NodeRef::default(),
        }
    }

    fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
        let props = ctx.props();
        let path = props.path.clone();
        let cursor = props.model.cursor.clone();
        let selected = path == cursor;
        if selected {
            self.focus_input();
        }
    }

    fn view(&self, ctx: &Context<Self>) -> Html {
        let default_node = Node::default();
        let props = ctx.props();
        let hash = props.hash.clone().unwrap_or_default();
        let node = props.model.file.lookup_hash(&hash).unwrap_or(&default_node);
        let path = props.path.clone();
        let cursor = props.model.cursor.clone();
        let oninput = props.oninput.clone();
        let selected = path == cursor;
        let kind = SCHEMA.get_kind(&node.kind);
        let inner = if node.kind.is_empty() {
            let onupdatemodel = ctx.props().updatemodel.clone();
            let path = path.clone();
            let entries: Vec<Entry> = props
                .validators
                .iter()
                .flat_map(|v| match v {
                    FieldValidator::Kind(k) => Some(Entry {
                        label: k.to_string(),
                        description: "".to_string(),
                        action: Msg::ReplaceNode(
                            path.clone(),
                            Node {
                                kind: k.to_string(),
                                value: "".to_string(),
                                children: BTreeMap::new(),
                            },
                            false,
                        ),
                        valid_classes: KIND_CLASSES.iter().map(|v| v.to_string()).collect(),
                    }),
                    FieldValidator::Literal(v) => None,
                })
                .collect();
            let onenter = {
                let path = path.clone();
                let onupdatemodel = onupdatemodel.clone();
                Callback::from(move |()| {
                    onupdatemodel.emit(Msg::Parent);
                })
            };
            let onupdatemodel0 = onupdatemodel.clone();
            html! {
              <CommandLine
                input_node_ref={ self.input_node_ref.clone() }
                entries={ entries }
                value={ node.value.clone() }
                oninput={ Callback::from(move |v| {
                    onupdatemodel.emit(Msg::SetNodeValue(path.clone(), v));
                 }) }
                onselect={ ctx.props().updatemodel.clone() }
                ondelete={ Callback::from(move |()| {
                    onupdatemodel0.emit(Msg::DeleteItem);
                 }) }
                onenter={ onenter }
                enabled={ selected }
              />
            }
        } else {
            let renderer = if props.model.rich_render {
                kind.map(|r| {
                    let KindValue::Struct { renderer, .. } = r.value;
                    renderer
                })
                .unwrap_or(default_renderer)
            } else {
                default_renderer
            };
            let validator_context = ValidatorContext {
                model: props.model.clone(),
                path: path.clone(),
                node: node.clone(),
                onselect: props.onselect.clone(),
                updatemodel: props.updatemodel.clone(),
            };
            let content = renderer(&validator_context);
            let footer = if selected {
                let entries: Vec<Entry> = kind
                    .map(|k| {
                        let KindValue::Struct { fields, .. } = k.value;
                        fields
                            .iter()
                            .map(|f| Entry {
                                label: f.name.to_string(),
                                description: "".to_string(),
                                action: Msg::AddField(path.to_vec(), f.name.to_string()),
                                valid_classes: FIELD_CLASSES
                                    .iter()
                                    .map(|v| v.to_string())
                                    .collect(),
                            })
                            .collect()
                    })
                    .unwrap_or_default();
                // Make it look like an actual field.
                let onupdatemodel0 = ctx.props().updatemodel.clone();
                html! {

                        <CommandLine
                            input_node_ref={ self.input_node_ref.clone() }
                            entries={ entries }
                            value={ node.value.clone() }
                            onselect={ ctx.props().updatemodel.clone() }
                            ondelete={ Callback::from(move |()| {
                                onupdatemodel0.emit(Msg::Parent);
                            }) }
                            enabled=true
                        />

                }
            } else {
                html! {}
            };
            html! {
              <>
                { content }
                { footer }
              </>
            }
        };
        let onselect = ctx.props().onselect.clone();
        let onclick = {
            let path = path.clone();
            ctx.link().callback(move |e: MouseEvent| {
                e.stop_propagation();
                onselect.emit(path.clone());
                NodeMsg::Click
            })
        };
        let mut classes = vec![
            "node".to_string(),
            "align-top".to_string(),
            "flex-auto".to_string(),
        ];
        if selected {
            classes.push("selected".to_string());
        }
        html! {

              { inner }

        }
    }

    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
        match msg {
            NodeMsg::Click => {
                self.focus_input();
                true
            }
        }
    }

    fn changed(&mut self, ctx: &Context<Self>) -> bool {
        true
    }
}

impl NodeComponent {
    fn focus_input(&self) {
        log::info!("focusing input");
        let input_node = self.input_node_ref.clone();
        input_node
            .cast::<HtmlInputElement>()
            .unwrap()
            .focus()
            .unwrap();
    }
}
<commit_msg>Props equality<commit_after>use crate::{
    command_line::{CommandLine, Entry},
    schema::{default_renderer, FieldValidator, KindValue, ValidatorContext, SCHEMA},
    types::{Hash, Model, Msg, Node, Selector},
};
use std::{collections::BTreeMap, rc::Rc};
use web_sys::HtmlInputElement;
use yew::prelude::*;

pub struct NodeComponent {
    input_node_ref: NodeRef,
    old_props: Option<NodeProperties>,
}

#[derive(Properties, PartialEq, Clone)]
pub struct NodeProperties {
    pub path: Vec<Selector>,
    pub model: Rc<Model>,
    #[prop_or_default]
    pub hash: Option<Hash>,
    #[prop_or_default]
    pub placeholder: String,
    // When a new value is typed in the text box.
    #[prop_or_default]
    pub oninput: Callback<String>,
    // When the node is selected (e.g. clicked).
    #[prop_or_default]
    pub onselect: Callback<Vec<Selector>>,
    #[prop_or_default]
    pub updatemodel: Callback<Msg>,
    #[prop_or_default]
    pub validators: &'static [FieldValidator],
}

pub enum NodeMsg {
    Click,
}

pub const FIELD_CLASSES: &[&str] = &[
    "bg-blue-300",
    "inline-block",
    "p-0.5",
    "px-1",
    "border-blue-800",
    "border",
    "rounded",
];
pub const KIND_CLASSES: &[&str] = &[
    "kind",
    "bg-yellow-400",
    "border-yellow-800",
    "border-4",
    "inline-block",
    "p-0.5",
    "px-1",
];

impl Component for NodeComponent {
    type Message = NodeMsg;
    type Properties = NodeProperties;

    fn create(ctx: &Context<Self>) -> Self {
        log::debug!("creating node");
        Self {
            input_node_ref: NodeRef::default(),
            old_props: None,
        }
    }

    fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
        let props = ctx.props();
        let path = props.path.clone();
        let cursor = props.model.cursor.clone();
        let selected = path == cursor;
        if selected {
            self.focus_input();
        }
    }

    fn view(&self, ctx: &Context<Self>) -> Html {
        let default_node = Node::default();
        let props = ctx.props();
        let hash = props.hash.clone().unwrap_or_default();
        let node = props.model.file.lookup_hash(&hash).unwrap_or(&default_node);
        let path = props.path.clone();
        let cursor = props.model.cursor.clone();
        let oninput = props.oninput.clone();
        let selected = path == cursor;
        let kind = SCHEMA.get_kind(&node.kind);
        let inner = if node.kind.is_empty() {
            let onupdatemodel = ctx.props().updatemodel.clone();
            let path = path.clone();
            let entries: Vec<Entry> = props
                .validators
                .iter()
                .flat_map(|v| match v {
                    FieldValidator::Kind(k) => Some(Entry {
                        label: k.to_string(),
                        description: "".to_string(),
                        action: Msg::ReplaceNode(
                            path.clone(),
                            Node {
                                kind: k.to_string(),
                                value: "".to_string(),
                                children: BTreeMap::new(),
                            },
                            false,
                        ),
                        valid_classes: KIND_CLASSES.iter().map(|v| v.to_string()).collect(),
                    }),
                    FieldValidator::Literal(v) => None,
                })
                .collect();
            let onenter = {
                let path = path.clone();
                let onupdatemodel = onupdatemodel.clone();
                Callback::from(move |()| {
                    onupdatemodel.emit(Msg::Parent);
                })
            };
            let onupdatemodel0 = onupdatemodel.clone();
            html! {
              <CommandLine
                input_node_ref={ self.input_node_ref.clone() }
                entries={ entries }
                value={ node.value.clone() }
                oninput={ Callback::from(move |v| {
                    onupdatemodel.emit(Msg::SetNodeValue(path.clone(), v));
                 }) }
                onselect={ ctx.props().updatemodel.clone() }
                ondelete={ Callback::from(move |()| {
                    onupdatemodel0.emit(Msg::DeleteItem);
                 }) }
                onenter={ onenter }
                enabled={ selected }
              />
            }
        } else {
            let renderer = if props.model.rich_render {
                kind.map(|r| {
                    let KindValue::Struct { renderer, .. } = r.value;
                    renderer
                })
                .unwrap_or(default_renderer)
            } else {
                default_renderer
            };
            let validator_context = ValidatorContext {
                model: props.model.clone(),
                path: path.clone(),
                node: node.clone(),
                onselect: props.onselect.clone(),
                updatemodel: props.updatemodel.clone(),
            };
            let content = renderer(&validator_context);
            let footer = if selected {
                let entries: Vec<Entry> = kind
                    .map(|k| {
                        let KindValue::Struct { fields, .. } = k.value;
                        fields
                            .iter()
                            .map(|f| Entry {
                                label: f.name.to_string(),
                                description: "".to_string(),
                                action: Msg::AddField(path.to_vec(), f.name.to_string()),
                                valid_classes: FIELD_CLASSES
                                    .iter()
                                    .map(|v| v.to_string())
                                    .collect(),
                            })
                            .collect()
                    })
                    .unwrap_or_default();
                // Make it look like an actual field.
                let onupdatemodel0 = ctx.props().updatemodel.clone();
                html! {

                        <CommandLine
                            input_node_ref={ self.input_node_ref.clone() }
                            entries={ entries }
                            value={ node.value.clone() }
                            onselect={ ctx.props().updatemodel.clone() }
                            ondelete={ Callback::from(move |()| {
                                onupdatemodel0.emit(Msg::Parent);
                            }) }
                            enabled=true
                        />

                }
            } else {
                html! {}
            };
            html! {
              <>
                { content }
                { footer }
              </>
            }
        };
        let onselect = ctx.props().onselect.clone();
        let onclick = {
            let path = path.clone();
            ctx.link().callback(move |e: MouseEvent| {
                e.stop_propagation();
                onselect.emit(path.clone());
                NodeMsg::Click
            })
        };
        let mut classes = vec![
            "node".to_string(),
            "align-top".to_string(),
            "flex-auto".to_string(),
        ];
        if selected {
            classes.push("selected".to_string());
        }
        html! {

              { inner }

        }
    }

    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
        match msg {
            NodeMsg::Click => {
                self.focus_input();
                true
            }
        }
    }

    fn changed(&mut self, ctx: &Context<Self>) -> bool {
        let same = self.old_props == Some(ctx.props().clone());
        log::debug!("Node changed");
        log::debug!("same props: {:?}", same);
        if let Some(old_props) = &self.old_props {
            log::debug!("same path: {:?}", old_props.path == ctx.props().path);
            log::debug!("same model: {:?}", old_props.model == ctx.props().model);
            log::debug!("same hash: {:?}", old_props.hash == ctx.props().hash);
            log::debug!(
                "same updatemodel: {:?}",
                old_props.updatemodel == ctx.props().updatemodel
            );
            return old_props.path != ctx.props().path
                || old_props.model != ctx.props().model
                || old_props.hash != ctx.props().hash;
        }
        self.old_props = Some(ctx.props().clone());
        !same
    }
}

impl NodeComponent {
    fn focus_input(&self) {
        log::info!("focusing input");
        let input_node = self.input_node_ref.clone();
        input_node
            .cast::<HtmlInputElement>()
            .unwrap()
            .focus()
            .unwrap();
    }
}
<|endoftext|>


<commit_before>use std::sync::Arc;
use error::Error;

use super::{Request, Response, IronResult};

pub trait Handler: Send + Sync {
    fn call(&self, &mut Request) -> IronResult<Response>;

    fn catch(&self, &mut Request, Box<Error>) -> (Response, IronResult<()>);
}

pub trait BeforeMiddleware: Send + Sync {
    fn before(&self, &mut Request) -> IronResult<()>;

    fn catch(&self, _: &mut Request, err: Box<Error>) -> IronResult<()> {
        Err(err)
    }
}

pub trait AfterMiddleware: Send + Sync {
    fn after(&self, &mut Request, &mut Response) -> IronResult<()>;

    // This response was generated by the `catch` function of Handlers and is abnormal in some way.
    fn catch(&self, _: &mut Request, _: &mut Response, err: Box<Error>) -> IronResult<()> {
        Err(err)
    }
}

pub trait AroundMiddleware: Handler {
    fn with_handler(&mut self, handler: Box<Handler + Send + Sync>);
}

pub trait Chain: Handler {
    fn new<H: Handler>(H) -> Self;

    fn link<B, A>(&mut self, (B, A)) where A: AfterMiddleware, B: BeforeMiddleware;

    fn link_before<B>(&mut self, B) where B: BeforeMiddleware;

    fn link_after<A>(&mut self, A) where A: AfterMiddleware;
}

pub struct ChainBuilder {
    befores: Vec<Box<BeforeMiddleware + Send + Sync>>,
    afters: Vec<Box<AfterMiddleware + Send + Sync>>,
    handler: Box<Handler + Send + Sync>
}

impl Chain for ChainBuilder {
    fn new<H: Handler>(handler: H) -> ChainBuilder {
        ChainBuilder {
            befores: vec![],
            afters: vec![],
            handler: box handler as Box<Handler + Send + Sync>
        }
    }

    fn link<B, A>(&mut self, link: (B, A))
    where A: AfterMiddleware, B: BeforeMiddleware {
        let (before, after) = link;
        self.befores.push(box before as Box<BeforeMiddleware + Send + Sync>);
        self.afters.push(box after as Box<AfterMiddleware + Send + Sync>);
    }

    fn link_before<B>(&mut self, before: B)
    where B: BeforeMiddleware {
        self.befores.push(box before as Box<BeforeMiddleware + Send + Sync>);
    }

    fn link_after<A>(&mut self, after: A)
    where A: AfterMiddleware {
        self.afters.push(box after as Box<AfterMiddleware + Send + Sync>);
    }
}

impl Handler for ChainBuilder {
    fn call(&self, req: &mut Request) -> IronResult<Response> {
        let before_result = helpers::run_befores(req, self.befores.as_slice(), None);

        let (res, err) = match before_result {
            Ok(()) => match self.handler.call(req) {
                Ok(res) => (res, None),
                Err(e) => helpers::run_handler_catch(req, e, &self.handler)
            },
            Err(e) => helpers::run_handler_catch(req, e, &self.handler)
        };

        helpers::run_afters(req, res, err, self.afters.as_slice())
    }

    fn catch(&self, req: &mut Request, err: Box<Error>) -> (Response, IronResult<()>) {
        let before_result = helpers::run_befores(req, self.befores.as_slice(), Some(err));

        let (res, err) = match before_result {
            Ok(()) => match self.handler.call(req) {
                Ok(res) => (res, None),
                Err(e) => helpers::run_handler_catch(req, e, &self.handler)
            },
            Err(e) => helpers::run_handler_catch(req, e, &self.handler)
        };

        match helpers::run_afters(req, res, err, self.afters.as_slice()) {
            Ok(res) => (res, Ok(())),
            // FIXME: Make 500
            Err(err) => (Response::new(), Err(err))
        }
    }
}

impl Handler for fn(&mut Request) -> IronResult<Response> {
    fn call(&self, req: &mut Request) -> IronResult<Response> {
        self.call(req)
    }

    fn catch(&self, _: &mut Request, err: Box<Error>) -> (Response, IronResult<()>) {
        // FIXME: Make Response a 500
        (Response::new(), Err(err))
    }
}

impl Handler for Box<Handler + Send + Sync> {
    fn call(&self, req: &mut Request) -> IronResult<Response> {
        self.call(req)
    }

    fn catch(&self, req: &mut Request, err: Box<Error>) -> (Response, IronResult<()>) {
        self.catch(req, err)
    }
}

impl Handler for Arc<Box<Handler + Send + Sync>> {
    fn call(&self, req: &mut Request) -> IronResult<Response> {
        self.call(req)
    }

    fn catch(&self, req: &mut Request, err: Box<Error>) -> (Response, IronResult<()>) {
        self.catch(req, err)
    }
}

mod helpers {
    use super::super::{Request, Response, IronResult};
    use super::{AfterMiddleware, BeforeMiddleware, Handler};
    use error::Error;

    pub fn run_befores(req: &mut Request, befores: &[Box<BeforeMiddleware>], err: Option<Box<Error>>) -> IronResult<()> {
        match err {
            Some(mut e) => {
                for (i, before) in befores.iter().enumerate() {
                    match before.catch(req, e) {
                        Ok(_) => return run_befores(req, befores, None),
                        Err(new) => e = new
                    }
                }
                Err(e)
            },

            None => {
                for (i, before) in befores.iter().enumerate() {
                    match before.before(req) {
                        Ok(_) => (),
                        Err(err) => return run_befores(req, befores.slice_from(i), Some(err))
                    }
                }
                Ok(())
            }
        }
    }

    pub fn run_afters(req: &mut Request, mut res: Response, err: Option<Box<Error>>,
                  afters: &[Box<AfterMiddleware>]) -> IronResult<Response> {
        match err {
            Some(mut e) => {
                for (i, after) in afters.iter().enumerate() {
                    match after.catch(req, &mut res, e) {
                        Ok(_) => return run_afters(req, res, None, afters),
                        Err(new) => e = new
                    }
                }
                Err(e)
            },

            None => {
                for (i, after) in afters.iter().enumerate() {
                    match after.after(req, &mut res) {
                        Ok(_) => (),
                        Err(err) => return run_afters(req, res, Some(err), afters.slice_from(i))
                    }
                }
                Ok(res)
            }
        }
    }

    pub fn run_handler_catch(req: &mut Request, err: Box<Error>,
                         handler: &Box<Handler>) -> (Response, Option<Box<Error>>) {
        match handler.catch(req, err) {
            (res, Ok(())) => (res, None),
            (res, Err(e)) => (res, Some(e))
        }
    }
}

<commit_msg>(feat) Added Chain::around for adding AroundMiddleware.<commit_after>use std::sync::Arc;
use error::Error;

use super::{Request, Response, IronResult};

pub trait Handler: Send + Sync {
    fn call(&self, &mut Request) -> IronResult<Response>;

    fn catch(&self, &mut Request, Box<Error>) -> (Response, IronResult<()>);
}

pub trait BeforeMiddleware: Send + Sync {
    fn before(&self, &mut Request) -> IronResult<()>;

    fn catch(&self, _: &mut Request, err: Box<Error>) -> IronResult<()> {
        Err(err)
    }
}

pub trait AfterMiddleware: Send + Sync {
    fn after(&self, &mut Request, &mut Response) -> IronResult<()>;

    // This response was generated by the `catch` function of Handlers and is abnormal in some way.
    fn catch(&self, _: &mut Request, _: &mut Response, err: Box<Error>) -> IronResult<()> {
        Err(err)
    }
}

pub trait AroundMiddleware: Handler {
    fn around(&mut self, handler: Box<Handler + Send + Sync>);
}

pub trait Chain: Handler {
    fn new<H: Handler>(H) -> Self;

    fn link<B, A>(&mut self, (B, A)) where A: AfterMiddleware, B: BeforeMiddleware;

    fn link_before<B>(&mut self, B) where B: BeforeMiddleware;

    fn link_after<A>(&mut self, A) where A: AfterMiddleware;

    fn around<A>(&mut self, A) where A: AroundMiddleware;
}

pub struct ChainBuilder {
    befores: Vec<Box<BeforeMiddleware + Send + Sync>>,
    afters: Vec<Box<AfterMiddleware + Send + Sync>>,
    handler: Box<Handler + Send + Sync>
}

impl Chain for ChainBuilder {
    fn new<H: Handler>(handler: H) -> ChainBuilder {
        ChainBuilder {
            befores: vec![],
            afters: vec![],
            handler: box handler as Box<Handler + Send + Sync>
        }
    }

    fn link<B, A>(&mut self, link: (B, A))
    where A: AfterMiddleware, B: BeforeMiddleware {
        let (before, after) = link;
        self.befores.push(box before as Box<BeforeMiddleware + Send + Sync>);
        self.afters.push(box after as Box<AfterMiddleware + Send + Sync>);
    }

    fn link_before<B>(&mut self, before: B) where B: BeforeMiddleware {
        self.befores.push(box before as Box<BeforeMiddleware + Send + Sync>);
    }

    fn link_after<A>(&mut self, after: A) where A: AfterMiddleware {
        self.afters.push(box after as Box<AfterMiddleware + Send + Sync>);
    }

    fn around<A>(&mut self, mut around: A) where A: AroundMiddleware {
        use std::mem;

        let old = mem::replace(&mut self.handler, box Nop as Box<Handler + Send + Sync>);
        around.around(old);
        self.handler = box around as Box<Handler + Send + Sync>;
    }
}

impl Handler for ChainBuilder {
    fn call(&self, req: &mut Request) -> IronResult<Response> {
        let before_result = helpers::run_befores(req, self.befores.as_slice(), None);

        let (res, err) = match before_result {
            Ok(()) => match self.handler.call(req) {
                Ok(res) => (res, None),
                Err(e) => helpers::run_handler_catch(req, e, &self.handler)
            },
            Err(e) => helpers::run_handler_catch(req, e, &self.handler)
        };

        helpers::run_afters(req, res, err, self.afters.as_slice())
    }

    fn catch(&self, req: &mut Request, err: Box<Error>) -> (Response, IronResult<()>) {
        let before_result = helpers::run_befores(req, self.befores.as_slice(), Some(err));

        let (res, err) = match before_result {
            Ok(()) => match self.handler.call(req) {
                Ok(res) => (res, None),
                Err(e) => helpers::run_handler_catch(req, e, &self.handler)
            },
            Err(e) => helpers::run_handler_catch(req, e, &self.handler)
        };

        match helpers::run_afters(req, res, err, self.afters.as_slice()) {
            Ok(res) => (res, Ok(())),
            // FIXME: Make 500
            Err(err) => (Response::new(), Err(err))
        }
    }
}

impl Handler for fn(&mut Request) -> IronResult<Response> {
    fn call(&self, req: &mut Request) -> IronResult<Response> {
        self.call(req)
    }

    fn catch(&self, _: &mut Request, err: Box<Error>) -> (Response, IronResult<()>) {
        // FIXME: Make Response a 500
        (Response::new(), Err(err))
    }
}

pub struct Nop;

impl Handler for Nop {
    fn call(&self, _: &mut Request) -> IronResult<Response> {
        Ok(Response::new())
    }

    fn catch(&self, _: &mut Request, err: Box<Error>) -> (Response, IronResult<()>) {
        // FIXME: Make Response a 500
        (Response::new(), Err(err))
    }
}

impl Handler for Box<Handler + Send + Sync> {
    fn call(&self, req: &mut Request) -> IronResult<Response> {
        self.call(req)
    }

    fn catch(&self, req: &mut Request, err: Box<Error>) -> (Response, IronResult<()>) {
        self.catch(req, err)
    }
}

impl Handler for Arc<Box<Handler + Send + Sync>> {
    fn call(&self, req: &mut Request) -> IronResult<Response> {
        self.call(req)
    }

    fn catch(&self, req: &mut Request, err: Box<Error>) -> (Response, IronResult<()>) {
        self.catch(req, err)
    }
}

mod helpers {
    use super::super::{Request, Response, IronResult};
    use super::{AfterMiddleware, BeforeMiddleware, Handler};
    use error::Error;

    pub fn run_befores(req: &mut Request, befores: &[Box<BeforeMiddleware>], err: Option<Box<Error>>) -> IronResult<()> {
        match err {
            Some(mut e) => {
                for (i, before) in befores.iter().enumerate() {
                    match before.catch(req, e) {
                        Ok(_) => return run_befores(req, befores, None),
                        Err(new) => e = new
                    }
                }
                Err(e)
            },

            None => {
                for (i, before) in befores.iter().enumerate() {
                    match before.before(req) {
                        Ok(_) => (),
                        Err(err) => return run_befores(req, befores.slice_from(i), Some(err))
                    }
                }
                Ok(())
            }
        }
    }

    pub fn run_afters(req: &mut Request, mut res: Response, err: Option<Box<Error>>,
                  afters: &[Box<AfterMiddleware>]) -> IronResult<Response> {
        match err {
            Some(mut e) => {
                for (i, after) in afters.iter().enumerate() {
                    match after.catch(req, &mut res, e) {
                        Ok(_) => return run_afters(req, res, None, afters),
                        Err(new) => e = new
                    }
                }
                Err(e)
            },

            None => {
                for (i, after) in afters.iter().enumerate() {
                    match after.after(req, &mut res) {
                        Ok(_) => (),
                        Err(err) => return run_afters(req, res, Some(err), afters.slice_from(i))
                    }
                }
                Ok(res)
            }
        }
    }

    pub fn run_handler_catch(req: &mut Request, err: Box<Error>,
                         handler: &Box<Handler>) -> (Response, Option<Box<Error>>) {
        match handler.catch(req, err) {
            (res, Ok(())) => (res, None),
            (res, Err(e)) => (res, Some(e))
        }
    }
}

<|endoftext|>


<commit_before>/* Copyright (C) 2015 Yutaka Kamei */

extern crate urlparse;
use urlparse::*;

#[test]
fn test_parse_qs() {
    let map1 = parse_qs("q=%E3%83%86%E3%82%B9%E3%83%88+%E3%83%86%E3%82%B9%E3%83%88&e=utf-8");
    let map2 = parse_qs("a=123&a=90&a=%E4%BA%80%E4%BA%95&b=0;n1;n2");
    let map3 = parse_qs("a=v1&a=v2&b=cv1&c=");
    let q = map1.get(&"q".to_string()).unwrap().get(0).unwrap();
    let e = map1.get(&"e".to_string()).unwrap().get(0).unwrap();
    let a = map2.get(&"a".to_string()).unwrap();
    let b = map2.get(&"b".to_string()).unwrap();
    let n1 = map2.get(&"n1".to_string());
    let n2 = map2.get(&"n2".to_string());
    let first = map3.get_first(&"a".to_string()).unwrap();
    let value = map3.get_from_str("a").unwrap();
    let value_first = map3.get_first_from_str("a").unwrap();
    let vn1 = map3.get_first(&"vn1".to_string());
    let vn2 = map3.get_from_str("vn2");
    let vn3 = map3.get_first_from_str("vn3");
    let c = map3.get_first_from_str("c");
    assert_eq!(*q, "テスト テスト".to_string());
    assert_eq!(*e, "utf-8".to_string());
    assert_eq!(a.len(), 3);
    assert_eq!(b.len(), 1);
    assert_eq!(*a.get(2).unwrap(), "亀井");
    assert_eq!(n1, None);
    assert_eq!(n2, None);
    assert_eq!(*first, "v1");
    assert_eq!(value, ["v1".to_string(), "v2".to_string()]);
    assert_eq!(value_first, "v1");
    assert_eq!(vn1, None);
    assert_eq!(vn2, None);
    assert_eq!(vn3, None);
    assert_eq!(c, None);
}
<commit_msg>Update test for parse_qs()<commit_after>/* Copyright (C) 2015 Yutaka Kamei */

extern crate urlparse;
use urlparse::*;
use std::collections::HashMap;
use std::iter::FromIterator;

#[test]
fn test_qsl() {
    let query = parse_qs("");
    assert_eq!(query, HashMap::new());
    let query = parse_qs("&");
    assert_eq!(query, HashMap::new());
    let query = parse_qs("&&");
    assert_eq!(query, HashMap::new());
    let query = parse_qs("=");
    assert_eq!(query, HashMap::new());
    let query = parse_qs("=a");
    assert_eq!(query, HashMap::from_iter(vec![("".to_string(), vec!["a".to_string()])]));
    let query = parse_qs("a");
    assert_eq!(query, HashMap::new());
    let query = parse_qs("a=");
    assert_eq!(query, HashMap::new());
    let query = parse_qs("&a=b");
    assert_eq!(query, HashMap::from_iter(vec![("a".to_string(), vec!["b".to_string()])]));
    let query = parse_qs("a=a+b&b=b+c");
    assert_eq!(query, HashMap::from_iter(vec![("a".to_string(), vec!["a b".to_string()]),
                                              ("b".to_string(), vec!["b c".to_string()])]));
    let query = parse_qs("a=1&a=2");
    assert_eq!(query, HashMap::from_iter(vec![("a".to_string(), vec!["1".to_string(), "2".to_string()])]));
}

#[test]
fn test_parse_qs() {
    let map1 = parse_qs("q=%E3%83%86%E3%82%B9%E3%83%88+%E3%83%86%E3%82%B9%E3%83%88&e=utf-8");
    let map2 = parse_qs("a=123&a=90&a=%E4%BA%80%E4%BA%95&b=0;n1;n2");
    let map3 = parse_qs("a=v1&a=v2&b=cv1&c=");
    let q = map1.get(&"q".to_string()).unwrap().get(0).unwrap();
    let e = map1.get(&"e".to_string()).unwrap().get(0).unwrap();
    let a = map2.get(&"a".to_string()).unwrap();
    let b = map2.get(&"b".to_string()).unwrap();
    let n1 = map2.get(&"n1".to_string());
    let n2 = map2.get(&"n2".to_string());
    let first = map3.get_first(&"a".to_string()).unwrap();
    let value = map3.get_from_str("a").unwrap();
    let value_first = map3.get_first_from_str("a").unwrap();
    let vn1 = map3.get_first(&"vn1".to_string());
    let vn2 = map3.get_from_str("vn2");
    let vn3 = map3.get_first_from_str("vn3");
    let c = map3.get_first_from_str("c");
    assert_eq!(*q, "テスト テスト".to_string());
    assert_eq!(*e, "utf-8".to_string());
    assert_eq!(a.len(), 3);
    assert_eq!(b.len(), 1);
    assert_eq!(*a.get(2).unwrap(), "亀井");
    assert_eq!(n1, None);
    assert_eq!(n2, None);
    assert_eq!(*first, "v1");
    assert_eq!(value, ["v1".to_string(), "v2".to_string()]);
    assert_eq!(value_first, "v1");
    assert_eq!(vn1, None);
    assert_eq!(vn2, None);
    assert_eq!(vn3, None);
    assert_eq!(c, None);
}
<|endoftext|>


<commit_before>use std::string::String as StdString;
use std::io::{Read, stdin};
use std::fmt;
use std::fs::File;
use std::sync::Mutex;

use vm::{self, Result, Variants};
use vm::gc::{Gc, Traverseable};
use vm::types::*;
use vm::thread::ThreadInternal;
use vm::thread::{Thread, Status};
use vm::api::{Array, Hole, VmType, Getable, OpaqueValue, Pushable, IO, WithVM, Userdata, primitive};
use vm::api::generic::{A, B};
use vm::stack::StackFrame;

use vm::internal::Value;

use super::{Compiler, Error};

fn print(s: &str) -> IO<()> {
    print!("{}", s);
    IO::Value(())
}

fn println(s: &str) -> IO<()> {
    println!("{}", s);
    IO::Value(())
}

struct GluonFile(Mutex<File>);

impl Userdata for GluonFile {}

impl fmt::Debug for GluonFile {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "File")
    }
}

impl VmType for GluonFile {
    type Type = GluonFile;
}

impl Traverseable for GluonFile {
    fn traverse(&self, _: &mut Gc) {}
}

fn open_file(s: &str) -> IO<GluonFile> {
    match File::open(s) {
        Ok(f) => IO::Value(GluonFile(Mutex::new(f))),
        Err(err) => IO::Exception(format!("{}", err)),
    }
}

fn read_file<'vm>(file: WithVM<'vm, &GluonFile>, count: usize) -> IO<Array<'vm, u8>> {
    let WithVM { vm, value: file } = file;
    let mut file = file.0.lock().unwrap();
    let mut buffer = Vec::with_capacity(count);
    unsafe {
        buffer.set_len(count);
        match file.read(&mut *buffer) {
            Ok(bytes_read) => {
                let value = {
                    let mut context = vm.context();
                    match context.alloc(&buffer[..bytes_read]) {
                        Ok(value) => value,
                        Err(err) => return IO::Exception(format!("{}", err)),
                    }
                };
                IO::Value(Getable::from_value(vm, Variants::new(&Value::Array(value)))
                    .expect("Array"))
            }
            Err(err) => IO::Exception(format!("{}", err)),
        }
    }
}

fn read_file_to_string(s: &str) -> IO<String> {
    let mut buffer = String::new();
    match File::open(s).and_then(|mut file| file.read_to_string(&mut buffer)) {
        Ok(_) => IO::Value(buffer),
        Err(err) => {
            use std::fmt::Write;
            buffer.clear();
            let _ = write!(&mut buffer, "{}", err);
            IO::Exception(buffer)
        }
    }
}

fn read_char() -> IO<char> {
    match stdin().bytes().next() {
        Some(result) => {
            match result {
                Ok(b) => {
                    ::std::char::from_u32(b as u32)
                        .map(IO::Value)
                        .unwrap_or_else(|| IO::Exception("Not a valid char".into()))
                }
                Err(err) => IO::Exception(format!("{}", err)),
            }
        }
        None => IO::Exception("No read".into()),
    }
}

fn read_line() -> IO<String> {
    let mut buffer = String::new();
    match stdin().read_line(&mut buffer) {
        Ok(_) => IO::Value(buffer),
        Err(err) => {
            use std::fmt::Write;
            buffer.clear();
            let _ = write!(&mut buffer, "{}", err);
            IO::Exception(buffer)
        }
    }
}

/// IO a -> (String -> IO a) -> IO a
extern "C" fn catch_io(vm: &Thread) -> Status {
    let mut context = vm.context();
    let frame_level = context.stack.get_frames().len();
    let action = StackFrame::current(&mut context.stack)[0];
    context.stack.push(action);
    0.push(vm, &mut context).unwrap();
    match vm.call_function(context, 1) {
        Ok(_) => Status::Ok,
        Err(err) => {
            let mut context = vm.context();
            {
                let mut stack = StackFrame::current(&mut context.stack);
                while stack.stack.get_frames().len() > frame_level {
                    if stack.exit_scope().is_err() {
                        return Status::Error;
                    }
                }
                let callback = stack[1];
                stack.push(callback);
            }
            let fmt = format!("{}", err);
            let _ = fmt.push(vm, &mut context);
            0.push(vm, &mut context).unwrap();
            match vm.call_function(context, 2) {
                Ok(_) => Status::Ok,
                Err(err) => {
                    context = vm.context();
                    let fmt = format!("{}", err);
                    let _ = fmt.push(vm, &mut context);
                    Status::Error
                }
            }
        }
    }
}

fn clear_frames(err: Error, frame_level: usize, mut stack: StackFrame) -> IO<String> {
    let fmt = match err {
        Error::VM(vm::Error::Panic(_)) => {
            let trace = stack.stack.stacktrace(frame_level);
            format!("{}\n{}", err, trace)
        }
        _ => format!("{}", err),
    };
    while stack.stack.get_frames().len() > frame_level {
        if stack.exit_scope().is_err() {
            return IO::Exception(fmt);
        }
    }
    IO::Exception(fmt)
}

fn run_expr(WithVM { vm, value: expr }: WithVM<&str>) -> IO<String> {
    let frame_level = vm.context().stack.get_frames().len();
    let run_result = Compiler::new().run_expr::<OpaqueValue<&Thread, Hole>>(vm, "<top>", expr);
    let mut context = vm.context();
    let stack = StackFrame::current(&mut context.stack);
    match run_result {
        Ok((value, typ)) => IO::Value(format!("{:?} : {}", value, typ)),
        Err(err) => clear_frames(err, frame_level, stack),
    }
}

fn load_script(WithVM { vm, value: name }: WithVM<&str>, expr: &str) -> IO<String> {
    let frame_level = vm.context().stack.get_frames().len();
    let run_result = Compiler::new().load_script_async(vm, name, expr).sync_or_error();
    let mut context = vm.context();
    let stack = StackFrame::current(&mut context.stack);
    match run_result {
        Ok(()) => IO::Value(format!("Loaded {}", name)),
        Err(err) => clear_frames(err, frame_level, stack),
    }
}

pub fn load(vm: &Thread) -> Result<()> {

    vm.register_type::<GluonFile>("File", &[])?;

    // io_flat_map f m : (a -> IO b) -> IO a -> IO b
    //     = f (m ())
    let io_flat_map = vec![// [f, m, ()]       Initial stack
                           Call(1), // [f, m_ret]       Call m ()
                           PushInt(0), // [f, m_ret, ()]   Add a dummy argument ()
                           TailCall(2) /* [f_ret]          Call f m_ret () */];
    let io_flat_map_type = <fn (fn (A) -> IO<B>, IO<A>) -> IO<B> as VmType>::make_type(vm);
    vm.add_bytecode("io_flat_map", io_flat_map_type, 3, io_flat_map)?;

    vm.add_bytecode("io_pure",
                      <fn(A) -> IO<A> as VmType>::make_type(vm),
                      2,
                      vec![Pop(1)])?;
    // IO functions
    vm.define_global("io",
                       record!(
        open_file => primitive!(1 open_file),
        read_file => primitive!(2 read_file),
        read_file_to_string => primitive!(1 read_file_to_string),
        read_char => primitive!(0 read_char),
        read_line => primitive!(0 read_line),
        print => primitive!(1 print),
        println => primitive!(1 println),
        catch =>
            primitive::<fn (IO<A>, fn (StdString) -> IO<A>) -> IO<A>>("io.catch", catch_io),
        run_expr => primitive!(1 run_expr),
        load_script => primitive!(2 load_script)
    ))?;
    Ok(())
}
<commit_msg>feat(repl): Print values with the pretty printer<commit_after>use std::string::String as StdString;
use std::io::{Read, stdin};
use std::fmt;
use std::fs::File;
use std::sync::Mutex;

use vm::{self, Result, Variants};
use vm::gc::{Gc, Traverseable};
use vm::types::*;
use vm::thread::ThreadInternal;
use vm::thread::{Thread, Status};
use vm::api::{Array, Hole, VmType, Getable, OpaqueValue, Pushable, IO, WithVM, Userdata, primitive};
use vm::api::generic::{A, B};
use vm::stack::StackFrame;
use vm::internal::ValuePrinter;

use vm::internal::Value;

use super::{Compiler, Error};

fn print(s: &str) -> IO<()> {
    print!("{}", s);
    IO::Value(())
}

fn println(s: &str) -> IO<()> {
    println!("{}", s);
    IO::Value(())
}

struct GluonFile(Mutex<File>);

impl Userdata for GluonFile {}

impl fmt::Debug for GluonFile {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "File")
    }
}

impl VmType for GluonFile {
    type Type = GluonFile;
}

impl Traverseable for GluonFile {
    fn traverse(&self, _: &mut Gc) {}
}

fn open_file(s: &str) -> IO<GluonFile> {
    match File::open(s) {
        Ok(f) => IO::Value(GluonFile(Mutex::new(f))),
        Err(err) => IO::Exception(format!("{}", err)),
    }
}

fn read_file<'vm>(file: WithVM<'vm, &GluonFile>, count: usize) -> IO<Array<'vm, u8>> {
    let WithVM { vm, value: file } = file;
    let mut file = file.0.lock().unwrap();
    let mut buffer = Vec::with_capacity(count);
    unsafe {
        buffer.set_len(count);
        match file.read(&mut *buffer) {
            Ok(bytes_read) => {
                let value = {
                    let mut context = vm.context();
                    match context.alloc(&buffer[..bytes_read]) {
                        Ok(value) => value,
                        Err(err) => return IO::Exception(format!("{}", err)),
                    }
                };
                IO::Value(Getable::from_value(vm, Variants::new(&Value::Array(value)))
                    .expect("Array"))
            }
            Err(err) => IO::Exception(format!("{}", err)),
        }
    }
}

fn read_file_to_string(s: &str) -> IO<String> {
    let mut buffer = String::new();
    match File::open(s).and_then(|mut file| file.read_to_string(&mut buffer)) {
        Ok(_) => IO::Value(buffer),
        Err(err) => {
            use std::fmt::Write;
            buffer.clear();
            let _ = write!(&mut buffer, "{}", err);
            IO::Exception(buffer)
        }
    }
}

fn read_char() -> IO<char> {
    match stdin().bytes().next() {
        Some(result) => {
            match result {
                Ok(b) => {
                    ::std::char::from_u32(b as u32)
                        .map(IO::Value)
                        .unwrap_or_else(|| IO::Exception("Not a valid char".into()))
                }
                Err(err) => IO::Exception(format!("{}", err)),
            }
        }
        None => IO::Exception("No read".into()),
    }
}

fn read_line() -> IO<String> {
    let mut buffer = String::new();
    match stdin().read_line(&mut buffer) {
        Ok(_) => IO::Value(buffer),
        Err(err) => {
            use std::fmt::Write;
            buffer.clear();
            let _ = write!(&mut buffer, "{}", err);
            IO::Exception(buffer)
        }
    }
}

/// IO a -> (String -> IO a) -> IO a
extern "C" fn catch_io(vm: &Thread) -> Status {
    let mut context = vm.context();
    let frame_level = context.stack.get_frames().len();
    let action = StackFrame::current(&mut context.stack)[0];
    context.stack.push(action);
    0.push(vm, &mut context).unwrap();
    match vm.call_function(context, 1) {
        Ok(_) => Status::Ok,
        Err(err) => {
            let mut context = vm.context();
            {
                let mut stack = StackFrame::current(&mut context.stack);
                while stack.stack.get_frames().len() > frame_level {
                    if stack.exit_scope().is_err() {
                        return Status::Error;
                    }
                }
                let callback = stack[1];
                stack.push(callback);
            }
            let fmt = format!("{}", err);
            let _ = fmt.push(vm, &mut context);
            0.push(vm, &mut context).unwrap();
            match vm.call_function(context, 2) {
                Ok(_) => Status::Ok,
                Err(err) => {
                    context = vm.context();
                    let fmt = format!("{}", err);
                    let _ = fmt.push(vm, &mut context);
                    Status::Error
                }
            }
        }
    }
}

fn clear_frames(err: Error, frame_level: usize, mut stack: StackFrame) -> IO<String> {
    let fmt = match err {
        Error::VM(vm::Error::Panic(_)) => {
            let trace = stack.stack.stacktrace(frame_level);
            format!("{}\n{}", err, trace)
        }
        _ => format!("{}", err),
    };
    while stack.stack.get_frames().len() > frame_level {
        if stack.exit_scope().is_err() {
            return IO::Exception(fmt);
        }
    }
    IO::Exception(fmt)
}

fn run_expr(WithVM { vm, value: expr }: WithVM<&str>) -> IO<String> {
    let frame_level = vm.context().stack.get_frames().len();
    let run_result = Compiler::new().run_expr::<OpaqueValue<&Thread, Hole>>(vm, "<top>", expr);
    let mut context = vm.context();
    let stack = StackFrame::current(&mut context.stack);
    match run_result {
        Ok((value, typ)) => {
            let env = vm.global_env().get_env();
            unsafe {
                IO::Value(format!("{} : {}",
                                  ValuePrinter::new(&*env, &typ, value.get_value()).width(80),
                                  typ))
            }
        }
        Err(err) => clear_frames(err, frame_level, stack),
    }
}

fn load_script(WithVM { vm, value: name }: WithVM<&str>, expr: &str) -> IO<String> {
    let frame_level = vm.context().stack.get_frames().len();
    let run_result = Compiler::new().load_script_async(vm, name, expr).sync_or_error();
    let mut context = vm.context();
    let stack = StackFrame::current(&mut context.stack);
    match run_result {
        Ok(()) => IO::Value(format!("Loaded {}", name)),
        Err(err) => clear_frames(err, frame_level, stack),
    }
}

pub fn load(vm: &Thread) -> Result<()> {

    vm.register_type::<GluonFile>("File", &[])?;

    // io_flat_map f m : (a -> IO b) -> IO a -> IO b
    //     = f (m ())
    let io_flat_map = vec![// [f, m, ()]       Initial stack
                           Call(1), // [f, m_ret]       Call m ()
                           PushInt(0), // [f, m_ret, ()]   Add a dummy argument ()
                           TailCall(2) /* [f_ret]          Call f m_ret () */];
    let io_flat_map_type = <fn (fn (A) -> IO<B>, IO<A>) -> IO<B> as VmType>::make_type(vm);
    vm.add_bytecode("io_flat_map", io_flat_map_type, 3, io_flat_map)?;

    vm.add_bytecode("io_pure",
                      <fn(A) -> IO<A> as VmType>::make_type(vm),
                      2,
                      vec![Pop(1)])?;
    // IO functions
    vm.define_global("io",
                       record!(
        open_file => primitive!(1 open_file),
        read_file => primitive!(2 read_file),
        read_file_to_string => primitive!(1 read_file_to_string),
        read_char => primitive!(0 read_char),
        read_line => primitive!(0 read_line),
        print => primitive!(1 print),
        println => primitive!(1 println),
        catch =>
            primitive::<fn (IO<A>, fn (StdString) -> IO<A>) -> IO<A>>("io.catch", catch_io),
        run_expr => primitive!(1 run_expr),
        load_script => primitive!(2 load_script)
    ))?;
    Ok(())
}
<|endoftext|>


<commit_before>use std::ops::Add;
use std::num::{Float, FromPrimitive, ToPrimitive, FromStrRadix};
use std::cmp::min;
use std::fmt;
use std::fmt::Writer;
use std::f64;
use std::cmp::Ordering;

use num::bigint::{BigInt, ToBigInt};
use num::rational::{Ratio, BigRational};
use num::{Signed, Zero, Integer, CheckedAdd};

#[derive(Debug)]
pub enum Real {
    Fixnum(isize),
    Integer(BigInt),
    Rational(BigRational),
    Flonum(f64)
}

impl fmt::Display for Real {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Real::Fixnum(n) => write!(f, "{}", n),
            &Real::Integer(ref n) => write!(f, "{}", n),
            &Real::Rational(ref n) => write!(f, "{}", n),
            &Real::Flonum(n) => write!(f, "{}", n)
        }
    }
}

#[inline]
pub fn fix2int(n: isize) -> BigInt {
    n.to_bigint().unwrap()
}

#[inline]
pub fn int2rat(n: &BigInt) -> BigRational {
    Ratio::from_integer(n.clone())
}

#[inline]
pub fn fix2rat(n: isize) -> BigRational {
    int2rat(&fix2int(n))
}

#[inline]
pub fn fix2flo(n: isize) -> f64 {
    FromPrimitive::from_int(n).unwrap()
}

pub fn int2flo(n: &BigInt) -> f64 {
    if n.is_zero() {
        return 0f64;
    }

    let neg = *n < Zero::zero();
    let repr = {
        let mut digits = Vec::new();
        let mut m = n.abs();
        let base: BigInt = FromPrimitive::from_u64(1 << 32).unwrap();
        while m >= base {
            let (q, r) = m.div_mod_floor(&base);
            digits.push(r.to_uint().unwrap());
            m = q;
        }
        if !m.is_zero() {
            digits.push(m.to_uint().unwrap());
        }
        let mut s = String::new();
        for &d in digits.iter().rev() {
            write!(&mut s, "{}", fmt::radix(d, 2)).unwrap();
        }
        s
    };
    let mantissa_repr = &repr[0 .. min(f64::MANTISSA_DIGITS, repr.len())];
    let u_mantissa:i64 = FromStrRadix::from_str_radix(mantissa_repr,
                                                        2).unwrap();
    let i_mantissa = if neg {
        -u_mantissa
    } else {
        u_mantissa
    };
    let exp = (repr.len() - mantissa_repr.len()) as isize;
    let m:f64 = i_mantissa.to_f64().unwrap();
    return Float::ldexp(m, exp);
}

pub fn rat2flo(r: &BigRational) -> f64 {
    int2flo(r.numer()) / int2flo(r.denom())
}

fn coerce_arith<Fix, Big, Rat, Flo>(lhs: &Real, rhs: &Real,
                                    fix_op: Fix, big_op: Big,
                                    rat_op: Rat, flo_op: Flo)
        -> Real
    where Fix: Fn(isize, isize) -> Option<isize>,
          Big: Fn(&BigInt, &BigInt) -> BigInt,
          Rat: Fn(&BigRational, &BigRational) -> BigRational,
          Flo: Fn(f64, f64) -> f64
{
    coerce(lhs, rhs,
           |x, y| match fix_op(x, y) {
               Some(n) => Real::Fixnum(n),
               None => Real::Integer(big_op(&fix2int(x), &fix2int(y)))
           },
           |x, y| Real::Integer(big_op(x, y)),
           |x, y| Real::Rational(rat_op(x, y)),
           |x, y| Real::Flonum(flo_op(x, y))
    )
}

fn coerce<Fix, Big, Rat, Flo, T>(lhs: &Real, rhs: &Real,
                                 fix_op: Fix, big_op: Big,
                                 rat_op: Rat, flo_op: Flo)
        -> T
    where Fix: Fn(isize, isize) -> T,
          Big: Fn(&BigInt, &BigInt) -> T,
          Rat: Fn(&BigRational, &BigRational) -> T,
          Flo: Fn(f64, f64) -> T
{
    match (lhs, rhs) {
        (&Real::Fixnum(l), &Real::Fixnum(r)) =>
            fix_op(l, r),
        (&Real::Fixnum(l), &Real::Integer(ref r)) =>
            big_op(&fix2int(l), r),
        (&Real::Fixnum(l), &Real::Rational(ref r)) =>
            rat_op(&fix2rat(l), r),
        (&Real::Fixnum(l), &Real::Flonum(r)) =>
            flo_op(fix2flo(l), r),
        (&Real::Integer(ref l), &Real::Fixnum(r)) =>
            big_op(l, &fix2int(r)),
        (&Real::Integer(ref l), &Real::Integer(ref r)) =>
            big_op(l, r),
        (&Real::Integer(ref l), &Real::Rational(ref r)) =>
            rat_op(&int2rat(l), r),
        (&Real::Integer(ref l), &Real::Flonum(r)) =>
            flo_op(int2flo(l), r),
        (&Real::Rational(ref l), &Real::Fixnum(r)) =>
            rat_op(l, &fix2rat(r)),
        (&Real::Rational(ref l), &Real::Integer(ref r)) =>
            rat_op(l, &int2rat(r)),
        (&Real::Rational(ref l), &Real::Rational(ref r)) =>
            rat_op(l, r),
        (&Real::Rational(ref l), &Real::Flonum(r)) =>
            flo_op(rat2flo(l), r),
        (&Real::Flonum(l), &Real::Fixnum(r)) =>
            flo_op(l, fix2flo(r)),
        (&Real::Flonum(l), &Real::Integer(ref r)) =>
            flo_op(l, int2flo(r)),
        (&Real::Flonum(l), &Real::Rational(ref r)) =>
            flo_op(l, rat2flo(r)),
        (&Real::Flonum(l), &Real::Flonum(r)) =>
            flo_op(l, r)
    }
}

impl Add<Real> for Real {
    type Output = Real;

    fn add(self, other: Real) -> Real {
        coerce_arith(&self, &other,
                     |x, y| x.checked_add(&y),
                     |x, y| x + y,
                     |x, y| x + y,
                     |x, y| x + y)
    }
}

impl<'a, 'b> Add<&'a Real> for &'b Real {
    type Output = Real;

    fn add(self, other: &Real) -> Real {
        coerce_arith(self, other,
                     |x, y| x.checked_add(&y),
                     |x, y| x + y,
                     |x, y| x + y,
                     |x, y| x + y)
    }
}

impl PartialEq for Real {
    fn eq(&self, other: &Real) -> bool {
        coerce(self, other,
               |x, y| x == y,
               |x, y| x == y,
               |x, y| x == y,
               |x, y| x == y
        )
    }
}

impl PartialOrd for Real {
    fn partial_cmp(&self, other: &Real) -> Option<Ordering> {
        coerce(self, other,
                    |x, y| x.partial_cmp(&y),
                    |x, y| x.partial_cmp(y),
                    |x, y| x.partial_cmp(y),
                    |x, y| x.partial_cmp(&y)
        )
    }
}

#[cfg(test)]
mod test {
    use super::{Real, int2flo};
    use std::num::FromPrimitive;

    #[test]
    fn test_add() {
        assert_eq!(Real::Fixnum(3), Real::Fixnum(1) + Real::Fixnum(2));
    }

    #[test]
    fn test_int2flo() {
        let n = FromPrimitive::from_int(3).unwrap();
        assert_eq!(3.0, int2flo(&n));
    }
}
<commit_msg>Reduce exact numbers: Rational -> Integer -> Fixnum<commit_after>use std::ops::Add;
use std::num::{Float, FromPrimitive, ToPrimitive, FromStrRadix};
use std::cmp::min;
use std::fmt;
use std::fmt::Writer;
use std::{f64, isize};
use std::cmp::Ordering;

use num::bigint::{BigInt, ToBigInt};
use num::rational::{Ratio, BigRational};
use num::{Signed, Zero, Integer, CheckedAdd};

#[derive(Debug)]
pub enum Real {
    Fixnum(isize),
    Integer(BigInt),
    Rational(BigRational),
    Flonum(f64)
}

impl fmt::Display for Real {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Real::Fixnum(n) => write!(f, "{}", n),
            &Real::Integer(ref n) => write!(f, "{}", n),
            &Real::Rational(ref n) => write!(f, "{}", n),
            &Real::Flonum(n) => write!(f, "{}", n)
        }
    }
}

#[inline]
pub fn fix2int(n: isize) -> BigInt {
    n.to_bigint().unwrap()
}

#[inline]
pub fn int2rat(n: &BigInt) -> BigRational {
    Ratio::from_integer(n.clone())
}

#[inline]
pub fn fix2rat(n: isize) -> BigRational {
    int2rat(&fix2int(n))
}

#[inline]
pub fn fix2flo(n: isize) -> f64 {
    FromPrimitive::from_int(n).unwrap()
}

pub fn int2flo(n: &BigInt) -> f64 {
    if n.is_zero() {
        return 0f64;
    }

    let neg = *n < Zero::zero();
    let repr = {
        let mut digits = Vec::new();
        let mut m = n.abs();
        let base: BigInt = FromPrimitive::from_u64(1 << 32).unwrap();
        while m >= base {
            let (q, r) = m.div_mod_floor(&base);
            digits.push(r.to_uint().unwrap());
            m = q;
        }
        if !m.is_zero() {
            digits.push(m.to_uint().unwrap());
        }
        let mut s = String::new();
        for &d in digits.iter().rev() {
            write!(&mut s, "{}", fmt::radix(d, 2)).unwrap();
        }
        s
    };
    let mantissa_repr = &repr[0 .. min(f64::MANTISSA_DIGITS, repr.len())];
    let u_mantissa:i64 = FromStrRadix::from_str_radix(mantissa_repr,
                                                        2).unwrap();
    let i_mantissa = if neg {
        -u_mantissa
    } else {
        u_mantissa
    };
    let exp = (repr.len() - mantissa_repr.len()) as isize;
    let m:f64 = i_mantissa.to_f64().unwrap();
    return Float::ldexp(m, exp);
}

pub fn rat2flo(r: &BigRational) -> f64 {
    int2flo(r.numer()) / int2flo(r.denom())
}

pub fn reduce(r: Real) -> Real {
    match r {
        Real::Fixnum(_) =>
            r,
        Real::Integer(n) => {
            let max_fixnum: BigInt = FromPrimitive::from_int(isize::MAX).unwrap();
            let min_fixnum: BigInt = FromPrimitive::from_int(isize::MIN).unwrap();

            if min_fixnum <= n && n <= max_fixnum {
                Real::Fixnum(n.to_int().unwrap())
            } else {
                Real::Integer(n)
            }
        },
        Real::Rational(n) =>
            if n.is_integer() {
                reduce(Real::Integer(n.to_integer()))
            } else {
                Real::Rational(n)
            },
        Real::Flonum(_) =>
            r
    }
}

fn coerce_arith<Fix, Big, Rat, Flo>(lhs: &Real, rhs: &Real,
                                    fix_op: Fix, big_op: Big,
                                    rat_op: Rat, flo_op: Flo)
        -> Real
    where Fix: Fn(isize, isize) -> Option<isize>,
          Big: Fn(&BigInt, &BigInt) -> BigInt,
          Rat: Fn(&BigRational, &BigRational) -> BigRational,
          Flo: Fn(f64, f64) -> f64
{
    coerce(lhs, rhs,
           |x, y| match fix_op(x, y) {
               Some(n) => Real::Fixnum(n),
               None => reduce(Real::Integer(big_op(&fix2int(x), &fix2int(y))))
           },
           |x, y| reduce(Real::Integer(big_op(x, y))),
           |x, y| reduce(Real::Rational(rat_op(x, y))),
           |x, y| Real::Flonum(flo_op(x, y))
    )
}

fn coerce<Fix, Big, Rat, Flo, T>(lhs: &Real, rhs: &Real,
                                 fix_op: Fix, big_op: Big,
                                 rat_op: Rat, flo_op: Flo)
        -> T
    where Fix: Fn(isize, isize) -> T,
          Big: Fn(&BigInt, &BigInt) -> T,
          Rat: Fn(&BigRational, &BigRational) -> T,
          Flo: Fn(f64, f64) -> T
{
    match (lhs, rhs) {
        (&Real::Fixnum(l), &Real::Fixnum(r)) =>
            fix_op(l, r),
        (&Real::Fixnum(l), &Real::Integer(ref r)) =>
            big_op(&fix2int(l), r),
        (&Real::Fixnum(l), &Real::Rational(ref r)) =>
            rat_op(&fix2rat(l), r),
        (&Real::Fixnum(l), &Real::Flonum(r)) =>
            flo_op(fix2flo(l), r),
        (&Real::Integer(ref l), &Real::Fixnum(r)) =>
            big_op(l, &fix2int(r)),
        (&Real::Integer(ref l), &Real::Integer(ref r)) =>
            big_op(l, r),
        (&Real::Integer(ref l), &Real::Rational(ref r)) =>
            rat_op(&int2rat(l), r),
        (&Real::Integer(ref l), &Real::Flonum(r)) =>
            flo_op(int2flo(l), r),
        (&Real::Rational(ref l), &Real::Fixnum(r)) =>
            rat_op(l, &fix2rat(r)),
        (&Real::Rational(ref l), &Real::Integer(ref r)) =>
            rat_op(l, &int2rat(r)),
        (&Real::Rational(ref l), &Real::Rational(ref r)) =>
            rat_op(l, r),
        (&Real::Rational(ref l), &Real::Flonum(r)) =>
            flo_op(rat2flo(l), r),
        (&Real::Flonum(l), &Real::Fixnum(r)) =>
            flo_op(l, fix2flo(r)),
        (&Real::Flonum(l), &Real::Integer(ref r)) =>
            flo_op(l, int2flo(r)),
        (&Real::Flonum(l), &Real::Rational(ref r)) =>
            flo_op(l, rat2flo(r)),
        (&Real::Flonum(l), &Real::Flonum(r)) =>
            flo_op(l, r)
    }
}

impl Add<Real> for Real {
    type Output = Real;

    fn add(self, other: Real) -> Real {
        coerce_arith(&self, &other,
                     |x, y| x.checked_add(&y),
                     |x, y| x + y,
                     |x, y| x + y,
                     |x, y| x + y)
    }
}

impl<'a, 'b> Add<&'a Real> for &'b Real {
    type Output = Real;

    fn add(self, other: &Real) -> Real {
        coerce_arith(self, other,
                     |x, y| x.checked_add(&y),
                     |x, y| x + y,
                     |x, y| x + y,
                     |x, y| x + y)
    }
}

impl PartialEq for Real {
    fn eq(&self, other: &Real) -> bool {
        coerce(self, other,
               |x, y| x == y,
               |x, y| x == y,
               |x, y| x == y,
               |x, y| x == y
        )
    }
}

impl PartialOrd for Real {
    fn partial_cmp(&self, other: &Real) -> Option<Ordering> {
        coerce(self, other,
                    |x, y| x.partial_cmp(&y),
                    |x, y| x.partial_cmp(y),
                    |x, y| x.partial_cmp(y),
                    |x, y| x.partial_cmp(&y)
        )
    }
}

#[cfg(test)]
mod test {
    use super::{Real, int2flo};
    use std::num::FromPrimitive;

    #[test]
    fn test_add() {
        assert_eq!(Real::Fixnum(3), Real::Fixnum(1) + Real::Fixnum(2));
    }

    #[test]
    fn test_reduce() {
        let x = Real::Integer(FromPrimitive::from_int(1).unwrap());
        let y = Real::Integer(FromPrimitive::from_int(2).unwrap());
        assert_eq!(Real::Fixnum(3), x+y);
    }

    #[test]
    fn test_int2flo() {
        let n = FromPrimitive::from_int(3).unwrap();
        assert_eq!(3.0, int2flo(&n));
    }
}
<|endoftext|>


<commit_before>//! Basic manipulation of rectangles.

use std::cmp;

/// A rectangular region of non-zero width and height.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Rect {
    left: i32,
    top: i32,
    width: u32,
    height: u32,
}

/// A geometrical representation of a set of 2D points with coordinate type T.
pub trait Region<T> {
    /// Whether this region contains the given point.
    fn contains(&self, x: T, y: T) -> bool;
}

impl Rect {
    /// Reduces possibility of confusing coordinates and dimensions
    /// when specifying rects.
    pub fn at(x: i32, y: i32) -> RectPosition {
        RectPosition { left: x, top: y }
    }

    /// Smallest y-coordinate reached by rect.
    pub fn top(&self) -> i32 {
        self.top
    }

    /// Smallest x-coordinate reached by rect.
    pub fn left(&self) -> i32 {
        self.left
    }

    /// Greatest y-coordinate reached by rect.
    pub fn bottom(&self) -> i32 {
        self.top + (self.height as i32) - 1
    }

    /// Greatest x-coordinate reached by rect.
    pub fn right(&self) -> i32 {
        self.left + (self.width as i32) - 1
    }

    /// Width of rect.
    pub fn width(&self) -> u32 {
        self.width
    }

    /// Height of rect.
    pub fn height(&self) -> u32 {
        self.height
    }

    /// Returns the intersection of self and other, or none if they are are disjoint.
    pub fn intersect(&self, other: Rect) -> Option<Rect> {
        let left = cmp::max(self.left, other.left);
        let top = cmp::max(self.top, other.top);
        let right = cmp::min(self.right(), other.right());
        let bottom = cmp::min(self.bottom(), other.bottom());

        if right < left || bottom < top {
            return None;
        }

        Some(Rect {
            left: left,
            top: top,
            width: (right - left) as u32 + 1,
            height: (bottom - top) as u32 + 1,
        })
    }
}

impl Region<i32> for Rect {
    fn contains(&self, x: i32, y: i32) -> bool {
        self.left <= x && x <= self.right() &&
        self.top <= y && y <= self.bottom()
    }
}

impl Region<f32> for Rect {
    fn contains(&self, x: f32, y: f32) -> bool {
        self.left as f32 <= x && x <= self.right() as f32 &&
        self.top as f32 <= y && y <= self.bottom() as f32
    }
}

/// Position of the top left of a rectangle.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct RectPosition {
    left: i32,
    top: i32,
}

impl RectPosition {
    /// Construct a rectangle from a position and size. Width and height
    /// are required to be strictly positive.
    pub fn of_size(self, width: u32, height: u32) -> Rect {
        assert!(width > 0, "width must be strictly positive");
        assert!(height > 0, "height must be strictly positive");
        Rect {
            left: self.left,
            top: self.top,
            width: width,
            height: height,
        }
    }
}

#[cfg(test)]
mod test {
    use super::{
        Rect, Region
    };

    #[test]
    #[should_panic]
    fn test_rejects_empty_rectangle() {
        Rect::at(1, 2).of_size(0, 1);
    }

    #[test]
    fn test_intersect_disjoint() {
        let r = Rect::at(0, 0).of_size(5, 5);
        let s = Rect::at(10, 10).of_size(100, 12);
        assert_eq!(r.intersect(s), None);
    }

    #[test]
    fn test_intersect_overlapping() {
        let r = Rect::at(0, 0).of_size(5, 5);
        let s = Rect::at(1, 4).of_size(10, 12);
        let i = Rect::at(1, 4).of_size(4, 1);
        assert_eq!(r.intersect(s), Some(i));
    }

    #[test]
    fn test_contains_i32() {
        let r = Rect::at(5, 5).of_size(6, 6);
        assert!(r.contains(5, 5));
        assert!(r.contains(10, 10));
        assert!(!r.contains(10, 11));
        assert!(!r.contains(11, 10));
    }

    #[test]
    fn test_contains_f32() {
        let r = Rect::at(5, 5).of_size(6, 6);
        assert!(r.contains(5f32, 5f32));
        assert!(!r.contains(10.1f32, 10f32));
    }
}
<commit_msg>Improve Rect docs<commit_after>//! Basic manipulation of rectangles.

use std::cmp;

/// A rectangular region of non-zero width and height.
/// # Examples
/// ```
/// use imageproc::rect::Rect;
/// use imageproc::rect::Region;
///
/// // Construct a rectangle with top-left corner at (4, 5), width 6 and height 7.
/// let rect = Rect::at(4, 5).of_size(6, 7);
///
/// // Contains top-left point:
/// assert_eq!(rect.left(), 4);
/// assert_eq!(rect.top(), 5);
/// assert!(rect.contains(rect.left(), rect.top()));
///
/// // Contains bottom-right point, at (left + width - 1, top + height - 1):
/// assert_eq!(rect.right(), 9);
/// assert_eq!(rect.bottom(), 11);
/// assert!(rect.contains(rect.right(), rect.bottom()));
/// ```
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Rect {
    left: i32,
    top: i32,
    width: u32,
    height: u32,
}

/// A geometrical representation of a set of 2D points with coordinate type T.
pub trait Region<T> {
    /// Whether this region contains the given point.
    fn contains(&self, x: T, y: T) -> bool;
}

impl Rect {
    /// Reduces possibility of confusing coordinates and dimensions
    /// when specifying rects.
    pub fn at(x: i32, y: i32) -> RectPosition {
        RectPosition { left: x, top: y }
    }

    /// Smallest y-coordinate reached by rect.
    pub fn top(&self) -> i32 {
        self.top
    }

    /// Smallest x-coordinate reached by rect.
    pub fn left(&self) -> i32 {
        self.left
    }

    /// Greatest y-coordinate reached by rect.
    pub fn bottom(&self) -> i32 {
        self.top + (self.height as i32) - 1
    }

    /// Greatest x-coordinate reached by rect.
    pub fn right(&self) -> i32 {
        self.left + (self.width as i32) - 1
    }

    /// Width of rect.
    pub fn width(&self) -> u32 {
        self.width
    }

    /// Height of rect.
    pub fn height(&self) -> u32 {
        self.height
    }

    /// Returns the intersection of self and other, or none if they are are disjoint.
    pub fn intersect(&self, other: Rect) -> Option<Rect> {
        let left = cmp::max(self.left, other.left);
        let top = cmp::max(self.top, other.top);
        let right = cmp::min(self.right(), other.right());
        let bottom = cmp::min(self.bottom(), other.bottom());

        if right < left || bottom < top {
            return None;
        }

        Some(Rect {
            left: left,
            top: top,
            width: (right - left) as u32 + 1,
            height: (bottom - top) as u32 + 1,
        })
    }
}

impl Region<i32> for Rect {
    fn contains(&self, x: i32, y: i32) -> bool {
        self.left <= x && x <= self.right() &&
        self.top <= y && y <= self.bottom()
    }
}

impl Region<f32> for Rect {
    fn contains(&self, x: f32, y: f32) -> bool {
        self.left as f32 <= x && x <= self.right() as f32 &&
        self.top as f32 <= y && y <= self.bottom() as f32
    }
}

/// Position of the top left of a rectangle.
/// Only used when building a `Rect`.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct RectPosition {
    left: i32,
    top: i32,
}

impl RectPosition {
    /// Construct a rectangle from a position and size. Width and height
    /// are required to be strictly positive.
    pub fn of_size(self, width: u32, height: u32) -> Rect {
        assert!(width > 0, "width must be strictly positive");
        assert!(height > 0, "height must be strictly positive");
        Rect {
            left: self.left,
            top: self.top,
            width: width,
            height: height,
        }
    }
}

#[cfg(test)]
mod test {
    use super::{
        Rect, Region
    };

    #[test]
    #[should_panic]
    fn test_rejects_empty_rectangle() {
        Rect::at(1, 2).of_size(0, 1);
    }

    #[test]
    fn test_intersect_disjoint() {
        let r = Rect::at(0, 0).of_size(5, 5);
        let s = Rect::at(10, 10).of_size(100, 12);
        assert_eq!(r.intersect(s), None);
    }

    #[test]
    fn test_intersect_overlapping() {
        let r = Rect::at(0, 0).of_size(5, 5);
        let s = Rect::at(1, 4).of_size(10, 12);
        let i = Rect::at(1, 4).of_size(4, 1);
        assert_eq!(r.intersect(s), Some(i));
    }

    #[test]
    fn test_contains_i32() {
        let r = Rect::at(5, 5).of_size(6, 6);
        assert!(r.contains(5, 5));
        assert!(r.contains(10, 10));
        assert!(!r.contains(10, 11));
        assert!(!r.contains(11, 10));
    }

    #[test]
    fn test_contains_f32() {
        let r = Rect::at(5, 5).of_size(6, 6);
        assert!(r.contains(5f32, 5f32));
        assert!(!r.contains(10.1f32, 10f32));
    }
}
<|endoftext|>