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

//! Implementation of the install aspects of the compiler.
//!
//! This module is responsible for installing the standard library,
//! compiler, and documentation.

use std::env;
use std::fs;
use std::path::{Path, PathBuf, Component};
use std::process::Command;

use Build;
use dist::{sanitize_sh, tmpdir};

/// Installs everything.
pub fn install(build: &Build, stage: u32, host: &str) {
    let prefix_default = PathBuf::from("/usr/local");
    let docdir_default = PathBuf::from("share/doc/rust");
    let mandir_default = PathBuf::from("share/man");
    let libdir_default = PathBuf::from("lib");
    let prefix = build.config.prefix.as_ref().unwrap_or(&prefix_default);
    let docdir = build.config.docdir.as_ref().unwrap_or(&docdir_default);
    let libdir = build.config.libdir.as_ref().unwrap_or(&libdir_default);
    let mandir = build.config.mandir.as_ref().unwrap_or(&mandir_default);

    let docdir = prefix.join(docdir);
    let libdir = prefix.join(libdir);
    let mandir = prefix.join(mandir);

    let destdir = env::var_os("DESTDIR").map(PathBuf::from);

    let prefix = add_destdir(&prefix, &destdir);
    let docdir = add_destdir(&docdir, &destdir);
    let libdir = add_destdir(&libdir, &destdir);
    let mandir = add_destdir(&mandir, &destdir);

    let empty_dir = build.out.join("tmp/empty_dir");
    t!(fs::create_dir_all(&empty_dir));
    if build.config.docs {
        install_sh(&build, "docs", "rust-docs", stage, host, &prefix,
                   &docdir, &libdir, &mandir, &empty_dir);
    }
    if build.config.rust_save_analysis {
        install_sh(&build, "analysis", "rust-analysis", stage, host, &prefix,
                   &docdir, &libdir, &mandir, &empty_dir);
    }
    install_sh(&build, "std", "rust-std", stage, host, &prefix,
               &docdir, &libdir, &mandir, &empty_dir);
    install_sh(&build, "rustc", "rustc", stage, host, &prefix,
               &docdir, &libdir, &mandir, &empty_dir);
    t!(fs::remove_dir_all(&empty_dir));
}

fn install_sh(build: &Build, package: &str, name: &str, stage: u32, host: &str,
              prefix: &Path, docdir: &Path, libdir: &Path, mandir: &Path, empty_dir: &Path) {
    println!("Install {} stage{} ({})", package, stage, host);
    let package_name = format!("{}-{}-{}", name, build.rust_package_vers(), host);

    let mut cmd = Command::new("sh");
    cmd.current_dir(empty_dir)
       .arg(sanitize_sh(&tmpdir(build).join(&package_name).join("install.sh")))
       .arg(format!("--prefix={}", sanitize_sh(prefix)))
       .arg(format!("--docdir={}", sanitize_sh(docdir)))
       .arg(format!("--libdir={}", sanitize_sh(libdir)))
       .arg(format!("--mandir={}", sanitize_sh(mandir)))
       .arg("--disable-ldconfig");
    build.run(&mut cmd);
}

fn add_destdir(path: &Path, destdir: &Option<PathBuf>) -> PathBuf {
    let mut ret = match *destdir {
        Some(ref dest) => dest.clone(),
        None => return path.to_path_buf(),
    };
    for part in path.components() {
        match part {
            Component::Normal(s) => ret.push(s),
            _ => {}
        }
    }
    return ret
}
<commit_msg>Fixes other targets rustlibs installation<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation of the install aspects of the compiler.
//!
//! This module is responsible for installing the standard library,
//! compiler, and documentation.

use std::env;
use std::fs;
use std::path::{Path, PathBuf, Component};
use std::process::Command;

use Build;
use dist::{sanitize_sh, tmpdir};

/// Installs everything.
pub fn install(build: &Build, stage: u32, host: &str) {
    let prefix_default = PathBuf::from("/usr/local");
    let docdir_default = PathBuf::from("share/doc/rust");
    let mandir_default = PathBuf::from("share/man");
    let libdir_default = PathBuf::from("lib");
    let prefix = build.config.prefix.as_ref().unwrap_or(&prefix_default);
    let docdir = build.config.docdir.as_ref().unwrap_or(&docdir_default);
    let libdir = build.config.libdir.as_ref().unwrap_or(&libdir_default);
    let mandir = build.config.mandir.as_ref().unwrap_or(&mandir_default);

    let docdir = prefix.join(docdir);
    let libdir = prefix.join(libdir);
    let mandir = prefix.join(mandir);

    let destdir = env::var_os("DESTDIR").map(PathBuf::from);

    let prefix = add_destdir(&prefix, &destdir);
    let docdir = add_destdir(&docdir, &destdir);
    let libdir = add_destdir(&libdir, &destdir);
    let mandir = add_destdir(&mandir, &destdir);

    let empty_dir = build.out.join("tmp/empty_dir");
    t!(fs::create_dir_all(&empty_dir));
    if build.config.docs {
        install_sh(&build, "docs", "rust-docs", stage, host, &prefix,
                   &docdir, &libdir, &mandir, &empty_dir);
    }

    for target in build.config.target.iter() {
        install_sh(&build, "std", "rust-std", stage, target, &prefix,
                   &docdir, &libdir, &mandir, &empty_dir);
    }

    if build.config.rust_save_analysis {
        install_sh(&build, "analysis", "rust-analysis", stage, host, &prefix,
                   &docdir, &libdir, &mandir, &empty_dir);
    }

    install_sh(&build, "rustc", "rustc", stage, host, &prefix,
               &docdir, &libdir, &mandir, &empty_dir);
    t!(fs::remove_dir_all(&empty_dir));
}

fn install_sh(build: &Build, package: &str, name: &str, stage: u32, host: &str,
              prefix: &Path, docdir: &Path, libdir: &Path, mandir: &Path, empty_dir: &Path) {
    println!("Install {} stage{} ({})", package, stage, host);
    let package_name = format!("{}-{}-{}", name, build.rust_package_vers(), host);

    let mut cmd = Command::new("sh");
    cmd.current_dir(empty_dir)
       .arg(sanitize_sh(&tmpdir(build).join(&package_name).join("install.sh")))
       .arg(format!("--prefix={}", sanitize_sh(prefix)))
       .arg(format!("--docdir={}", sanitize_sh(docdir)))
       .arg(format!("--libdir={}", sanitize_sh(libdir)))
       .arg(format!("--mandir={}", sanitize_sh(mandir)))
       .arg("--disable-ldconfig");
    build.run(&mut cmd);
}

fn add_destdir(path: &Path, destdir: &Option<PathBuf>) -> PathBuf {
    let mut ret = match *destdir {
        Some(ref dest) => dest.clone(),
        None => return path.to_path_buf(),
    };
    for part in path.components() {
        match part {
            Component::Normal(s) => ret.push(s),
            _ => {}
        }
    }
    return ret
}
<|endoftext|>


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

use super::scope::Stack;
use super::Composite::{List, Tuple};
use super::Op::{
    Add, Bang, Bind, Cp, DeRef, Div, Element, Equal, FCall, Field, Func, InitList, InitThunk,
    InitTuple, Jump, JumpIfFalse, JumpIfTrue, Module, Mul, Noop, Pop, Return, SelectJump, Sub, Sym,
    Val,
};
use super::Primitive::{Bool, Float, Int, Str};
use super::Value::{C, P};
use super::VM;

macro_rules! assert_cases {
    (__impl__ $cases:expr) => {
        for case in $cases.drain(0..) {
            let mut vm = VM::new(&case.0);
            vm.run().unwrap();
            assert_eq!(dbg!(vm.pop()).unwrap(), case.1);
        }
    };

    (($input:expr, $result:expr), $($tok:tt)*) => {
        assert_cases!(__impl__ vec![($input, $result), $($tok)*])
    };

    ($($input:expr => $result:expr, )* ) => {
        assert_cases!(
            $(($input, $result),)*
        )
    }
}

#[test]
fn test_math_ops() {
    assert_cases!(
        // 1+1;
        vec![Val(Int(1)), Val(Int(1)), Add] => P(Int(2)),
        // 1-1;
        vec![Val(Int(1)), Val(Int(1)), Sub] => P(Int(0)),
        // 2*2;
        vec![Val(Int(2)), Val(Int(2)), Mul] => P(Int(4)),
        // 6/3;
        vec![Val(Int(2)), Val(Int(6)), Div] => P(Int(3)),
        // 1.0+1.0;
        vec![Val(Float(1.0)), Val(Float(1.0)), Add] => P(Float(2.0)),
        // 1.0-1.0;
        vec![Val(Float(1.0)), Val(Float(1.0)), Sub] => P(Float(0.0)),
        // 2.0*2.0;
       vec![Val(Float(2.0)), Val(Float(2.0)), Mul] => P(Float(4.0)),
        // 6.0/3.0;
        vec![Val(Float(2.0)), Val(Float(6.0)), Div] => P(Float(3.0)),
        // string concatenation
        vec![Val(Str("bar".to_owned())), Val(Str("foo".to_owned())), Add] => P(Str("foobar".to_owned())),
        // Composite operations
        vec![
            Val(Int(1)),
            Val(Int(1)),
            Add, // 1 + 1
            Val(Int(1)),
            Add, // 2 + 1
            Val(Int(1)),
            Add, // 3 + 1
        ] => P(Int(4)),
    );
}

#[test]
fn test_bind_op() {
    let mut cases = vec![(
        vec![Sym("foo".to_owned()), Val(Int(1)), Bind],
        ("foo", P(Int(1))),
        vec![Sym("foo".to_owned()), Val(Int(1)), Val(Int(1)), Add, Bind],
        ("foo", P(Int(2))),
    )];

    for case in cases.drain(0..) {
        let mut vm = VM::new(&case.0);
        vm.run().unwrap();
        let (name, result) = case.1;
        let v = vm.get_binding(name).unwrap();
        assert_eq!(&result, v);
    }
}

#[test]
fn test_list_ops() {
    assert_cases!(
        vec![InitList] => C(List(Vec::new())),
        vec![InitList, Val(Int(1)), Element] => C(List(vec![P(Int(1))])),
        vec![InitList, Val(Int(2)), Element, Val(Int(1)), Element] => C(List(vec![P(Int(2)), P(Int(1))])),
        vec![
            InitList,
            Val(Int(1)),
            Element,
            Val(Int(1)),
            Val(Int(1)),
            Add,
            Element,
        ] => C(List(vec![P(Int(1)), P(Int(2))])),
    );
}

#[test]
fn test_tuple_ops() {
    assert_cases!(
        vec![InitTuple] => C(Tuple(Vec::new())),
        vec![InitTuple, Val(Str("foo".to_owned())), Val(Int(1)), Field] => C(Tuple(vec![("foo".to_owned(), P(Int(1)))])),
        vec![
            InitTuple,
            Sym("bar".to_owned()),
            Val(Str("quux".to_owned())),
            Field,
            Val(Str("foo".to_owned())),
            Val(Int(1)),
            Field,
        ] => C(Tuple(vec![
            ("bar".to_owned(), P(Str("quux".to_owned()))),
            ("foo".to_owned(), P(Int(1))),
        ])),
        vec![
            InitTuple,
            Sym("bar".to_owned()),
            Val(Str("quux".to_owned())),
            Field,
            Val(Str("foo".to_owned())),
            Val(Int(1)),
            Field,
            Val(Str("foo".to_owned())),
            Val(Int(2)),
            Field,
        ] => C(Tuple(vec![
            ("bar".to_owned(), P(Str("quux".to_owned()))),
            ("foo".to_owned(), P(Int(2))),
        ])),
        vec![
            InitTuple,
            Sym("bar".to_owned()),
            Val(Str("ux".to_owned())),
            Val(Str("qu".to_owned())),
            Add,
            Field,
            Val(Str("foo".to_owned())),
            Val(Int(1)),
            Field,
            Val(Str("foo".to_owned())),
            Val(Int(2)),
            Field,
        ] => C(Tuple(vec![
            ("bar".to_owned(), P(Str("quux".to_owned()))),
            ("foo".to_owned(), P(Int(2))),
        ])),
        vec![
            InitTuple, // Override tuple
            Val(Str("foo".to_owned())),
            Val(Int(2)),
            Field,
            InitTuple, // Target tuple
            Sym("bar".to_owned()),
            Val(Str("ux".to_owned())),
            Val(Str("qu".to_owned())),
            Add,
            Field,
            Val(Str("foo".to_owned())),
            Val(Int(1)),
            Field,
            Cp, // Do the tuple copy operation
        ] => C(Tuple(vec![
            ("bar".to_owned(), P(Str("quux".to_owned()))),
            ("foo".to_owned(), P(Int(2))),
        ])),
    );
}

#[test]
fn test_jump_ops() {
    assert_cases!(
        vec![Jump(1), Val(Int(1)), Noop, Val(Int(1))] => P(Int(1)),
    );
}

#[test]
fn test_equality_ops() {
    assert_cases![
        vec![
            Val(Str("foo".to_owned())),
            Val(Str("foo".to_owned())),
            Equal,
        ] => P(Bool(true)),
        vec![
            Val(Str("bar".to_owned())),
            Val(Str("foo".to_owned())),
            Equal,
        ] => P(Bool(false)),
        vec![Val(Int(1)), Val(Int(1)), Equal] => P(Bool(true)),
        vec![Val(Int(1)), Val(Int(2)), Equal] => P(Bool(false)),
        vec![Val(Bool(true)), Val(Bool(true)), Equal] => P(Bool(true)),
        vec![Val(Bool(false)), Val(Bool(false)), Equal] => P(Bool(true)),
        vec![Val(Bool(true)), Val(Bool(false)), Equal] => P(Bool(false)),
        vec![
            InitTuple,
            Val(Str("foo".to_owned())),
            Val(Int(1)),
            Field,
            InitTuple,
            Val(Str("foo".to_owned())),
            Val(Int(1)),
            Field,
            Equal,
        ] => P(Bool(true)),
        vec![
            InitTuple,
            Val(Str("foo".to_owned())),
            Val(Int(1)),
            Field,
            InitTuple,
            Val(Str("bar".to_owned())),
            Val(Int(1)),
            Field,
            Equal,
        ] => P(Bool(false)),
        vec![
            InitList,
            Val(Str("foo".to_owned())),
            Element,
            InitList,
            Val(Str("foo".to_owned())),
            Element,
            Equal,
        ] => P(Bool(true)),
        vec![
            InitList,
            Val(Str("foo".to_owned())),
            Element,
            InitList,
            Val(Str("bar".to_owned())),
            Element,
            Equal,
        ] => P(Bool(false)),
    ];
}

#[test]
fn test_conditional_jump_ops() {
    assert_cases![
        vec![
            Val(Bool(false)),
            JumpIfTrue(2),
            Val(Bool(true)),
            JumpIfTrue(2),
            Val(Int(1)),
            Jump(1),
            Val(Int(2)),
            Noop,
        ] => P(Int(2)),
        vec![
            Val(Bool(true)),
            JumpIfTrue(2),
            Val(Bool(false)),
            JumpIfTrue(2),
            Val(Int(1)),
            Jump(1),
            Val(Int(2)),
            Noop,
        ] => P(Int(1)),
        vec![
            Val(Int(1)),
            Val(Int(1)),
            Equal,
            JumpIfTrue(2),
            Val(Bool(false)),
            JumpIfTrue(2),
            Val(Int(1)),
            Jump(1),
            Val(Int(2)),
            Noop,
        ] => P(Int(1)),
        vec![
            Val(Int(1)),
            Val(Int(2)),
            Equal,
            JumpIfFalse(2),
            Val(Bool(false)),
            JumpIfTrue(2),
            Val(Int(1)),
            Jump(1),
            Val(Int(2)),
            Noop,
        ] => P(Int(1)),
    ];
}

#[test]
fn test_function_definition_and_call() {
    assert_cases![
        vec![
            Sym("f".to_owned()),     // 0
            InitList,                // 1
            Sym("arg".to_owned()),   // 2
            Element,                 // 3
            Func(6),                 // 4
            DeRef("arg".to_owned()), // 5
            Return,                  // 6
            Bind,                    // 7
            Val(Int(1)),             // 8
            DeRef("f".to_owned()),   // 9
            FCall,                   // 10
        ] => P(Int(1)),
        vec![
            Sym("closed".to_owned()),   // 0
            Val(Int(1)),                // 1
            Bind,                       // 2
            Sym("f".to_owned()),        // 3
            InitList,                   // 4
            Sym("arg".to_owned()),      // 5
            Element,                    // 6
            Func(11),                   // 7
            DeRef("arg".to_owned()),    // 8
            DeRef("closed".to_owned()), // 9
            Add,                        // 10
            Return,                     // 11
            Bind,                       // 12
            Val(Int(1)),                // 13
            DeRef("f".to_owned()),      // 14
            FCall,                      // 16
        ] => P(Int(2)),
    ];
}

#[test]
fn test_module_call() {
    assert_cases![
        vec![
            InitTuple,               // 0 // override tuple
            Sym("one".to_owned()),   // 1
            Val(Int(11)),            // 2
            Field,                   // 3
            Sym("m".to_owned()),     // 4 // binding name for module
            InitTuple,               // 5 // Module tuple bindings
            Sym("one".to_owned()),   // 6
            Val(Int(1)),             // 7
            Field,                   // 8
            Sym("two".to_owned()),   // 9
            Val(Int(2)),             // 10
            Field,                   // 11
            Module(17),              // 12 // Module definition
            Bind,                    // 13
            Sym("foo".to_owned()),   // 14
            DeRef("mod".to_owned()), // 15
            Bind,                    // 16 // bind mod tuple to foo
            Return,                  // 17 // end the module
            Bind,                    // 18 // bind module to the binding name
            DeRef("m".to_owned()),   // 19
            Cp,                      // 20
        ] => C(Tuple(vec![(
            "foo".to_owned(),
            C(Tuple(vec![
                ("one".to_owned(), P(Int(11))),
                ("two".to_owned(), P(Int(2))),
            ])),
        )])),
        vec![
            InitTuple,               // 0 // override tuple
            Sym("one".to_owned()),   // 1
            Val(Int(11)),            // 2
            Field,                   // 3
            Sym("m".to_owned()),     // 4 // binding name for module
            InitTuple,               // 5 // Module tuple bindings
            Sym("one".to_owned()),   // 6
            Val(Int(1)),             // 7
            Field,                   // 8
            Sym("two".to_owned()),   // 9
            Val(Int(2)),             // 10
            Field,                   // 11
            InitThunk(2),            // 12 // Module Return expression
            Val(Int(1)),             // 13
            Return,                  // 14
            Module(20),              // 15 // Module definition
            Bind,                    // 16
            Sym("foo".to_owned()),   // 17
            DeRef("mod".to_owned()), // 18
            Bind,                    // 19 // bind mod tuple to foo
            Return,                  // 20 // end the module
            Bind,                    // 21 // bind module to the binding name
            DeRef("m".to_owned()),   // 22
            Cp,                      // 23
        ] => P(Int(1)),
    ];
}

#[test]
fn test_select_short_circuit() {
    assert_cases![
        vec![
            Sym("field".to_owned()),              // 0 // search field
            Sym("not_field".to_owned()),          // 1 // first field to compare
            SelectJump(2),                        // 2
            Val(Str("not our value".to_owned())), // 3 // expression for first field
            Jump(4),                              // 4
            Sym("field".to_owned()),              // 5 // second field to compare
            SelectJump(2),                        // 6
            Val(Int(1)),                          // 7 // expression for second field
            Jump(2),                              // 8
            Pop,                                  // 9 // pop the search field off
            Bang,                                 // 10 // default case
        ] => P(Int(1)),
        vec![
            Sym("field".to_owned()),              // 0 // search field
            Sym("not_field".to_owned()),          // 1 // first field to compare
            SelectJump(2),                        // 2
            Val(Str("not our value".to_owned())), // 3 // expression for first field
            Jump(4),                              // 4
            Sym("als not field".to_owned()),      // 5 // second field to compare
            SelectJump(2),                        // 6
            Val(Int(1)),                          // 7 // expression for second field
            Jump(1),                              // 8
            Pop,                                  // 9 // pop the search field off
            Val(Int(1)),                          // 10 // default case
        ] => P(Int(1)),
    ];
}

#[test]
fn test_scope_stacks() {
    let mut stack = Stack::new();
    stack.add("one".to_owned(), P(Int(1)));
    let mut val = stack.get("one").unwrap();
    assert_eq!(val, &P(Int(1)));
    stack.push();
    assert!(stack.get("one").is_none());
    stack.to_open();
    val = stack.get("one").unwrap();
    assert_eq!(val, &P(Int(1)));
}
<commit_msg>FIX test input to be clear about the success condition.<commit_after>// Copyright 2019 Jeremy Wall
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use super::scope::Stack;
use super::Composite::{List, Tuple};
use super::Op::{
    Add, Bang, Bind, Cp, DeRef, Div, Element, Equal, FCall, Field, Func, InitList, InitThunk,
    InitTuple, Jump, JumpIfFalse, JumpIfTrue, Module, Mul, Noop, Pop, Return, SelectJump, Sub, Sym,
    Val,
};
use super::Primitive::{Bool, Float, Int, Str};
use super::Value::{C, P};
use super::VM;

macro_rules! assert_cases {
    (__impl__ $cases:expr) => {
        for case in $cases.drain(0..) {
            let mut vm = VM::new(&case.0);
            vm.run().unwrap();
            assert_eq!(dbg!(vm.pop()).unwrap(), case.1);
        }
    };

    (($input:expr, $result:expr), $($tok:tt)*) => {
        assert_cases!(__impl__ vec![($input, $result), $($tok)*])
    };

    ($($input:expr => $result:expr, )* ) => {
        assert_cases!(
            $(($input, $result),)*
        )
    }
}

#[test]
fn test_math_ops() {
    assert_cases!(
        // 1+1;
        vec![Val(Int(1)), Val(Int(1)), Add] => P(Int(2)),
        // 1-1;
        vec![Val(Int(1)), Val(Int(1)), Sub] => P(Int(0)),
        // 2*2;
        vec![Val(Int(2)), Val(Int(2)), Mul] => P(Int(4)),
        // 6/3;
        vec![Val(Int(2)), Val(Int(6)), Div] => P(Int(3)),
        // 1.0+1.0;
        vec![Val(Float(1.0)), Val(Float(1.0)), Add] => P(Float(2.0)),
        // 1.0-1.0;
        vec![Val(Float(1.0)), Val(Float(1.0)), Sub] => P(Float(0.0)),
        // 2.0*2.0;
       vec![Val(Float(2.0)), Val(Float(2.0)), Mul] => P(Float(4.0)),
        // 6.0/3.0;
        vec![Val(Float(2.0)), Val(Float(6.0)), Div] => P(Float(3.0)),
        // string concatenation
        vec![Val(Str("bar".to_owned())), Val(Str("foo".to_owned())), Add] => P(Str("foobar".to_owned())),
        // Composite operations
        vec![
            Val(Int(1)),
            Val(Int(1)),
            Add, // 1 + 1
            Val(Int(1)),
            Add, // 2 + 1
            Val(Int(1)),
            Add, // 3 + 1
        ] => P(Int(4)),
    );
}

#[test]
fn test_bind_op() {
    let mut cases = vec![(
        vec![Sym("foo".to_owned()), Val(Int(1)), Bind],
        ("foo", P(Int(1))),
        vec![Sym("foo".to_owned()), Val(Int(1)), Val(Int(1)), Add, Bind],
        ("foo", P(Int(2))),
    )];

    for case in cases.drain(0..) {
        let mut vm = VM::new(&case.0);
        vm.run().unwrap();
        let (name, result) = case.1;
        let v = vm.get_binding(name).unwrap();
        assert_eq!(&result, v);
    }
}

#[test]
fn test_list_ops() {
    assert_cases!(
        vec![InitList] => C(List(Vec::new())),
        vec![InitList, Val(Int(1)), Element] => C(List(vec![P(Int(1))])),
        vec![InitList, Val(Int(2)), Element, Val(Int(1)), Element] => C(List(vec![P(Int(2)), P(Int(1))])),
        vec![
            InitList,
            Val(Int(1)),
            Element,
            Val(Int(1)),
            Val(Int(1)),
            Add,
            Element,
        ] => C(List(vec![P(Int(1)), P(Int(2))])),
    );
}

#[test]
fn test_tuple_ops() {
    assert_cases!(
        vec![InitTuple] => C(Tuple(Vec::new())),
        vec![InitTuple, Val(Str("foo".to_owned())), Val(Int(1)), Field] => C(Tuple(vec![("foo".to_owned(), P(Int(1)))])),
        vec![
            InitTuple,
            Sym("bar".to_owned()),
            Val(Str("quux".to_owned())),
            Field,
            Val(Str("foo".to_owned())),
            Val(Int(1)),
            Field,
        ] => C(Tuple(vec![
            ("bar".to_owned(), P(Str("quux".to_owned()))),
            ("foo".to_owned(), P(Int(1))),
        ])),
        vec![
            InitTuple,
            Sym("bar".to_owned()),
            Val(Str("quux".to_owned())),
            Field,
            Val(Str("foo".to_owned())),
            Val(Int(1)),
            Field,
            Val(Str("foo".to_owned())),
            Val(Int(2)),
            Field,
        ] => C(Tuple(vec![
            ("bar".to_owned(), P(Str("quux".to_owned()))),
            ("foo".to_owned(), P(Int(2))),
        ])),
        vec![
            InitTuple,
            Sym("bar".to_owned()),
            Val(Str("ux".to_owned())),
            Val(Str("qu".to_owned())),
            Add,
            Field,
            Val(Str("foo".to_owned())),
            Val(Int(1)),
            Field,
            Val(Str("foo".to_owned())),
            Val(Int(2)),
            Field,
        ] => C(Tuple(vec![
            ("bar".to_owned(), P(Str("quux".to_owned()))),
            ("foo".to_owned(), P(Int(2))),
        ])),
        vec![
            InitTuple, // Override tuple
            Val(Str("foo".to_owned())),
            Val(Int(2)),
            Field,
            InitTuple, // Target tuple
            Sym("bar".to_owned()),
            Val(Str("ux".to_owned())),
            Val(Str("qu".to_owned())),
            Add,
            Field,
            Val(Str("foo".to_owned())),
            Val(Int(1)),
            Field,
            Cp, // Do the tuple copy operation
        ] => C(Tuple(vec![
            ("bar".to_owned(), P(Str("quux".to_owned()))),
            ("foo".to_owned(), P(Int(2))),
        ])),
    );
}

#[test]
fn test_jump_ops() {
    assert_cases!(
        vec![Jump(1), Val(Int(1)), Noop, Val(Int(1))] => P(Int(1)),
    );
}

#[test]
fn test_equality_ops() {
    assert_cases![
        vec![
            Val(Str("foo".to_owned())),
            Val(Str("foo".to_owned())),
            Equal,
        ] => P(Bool(true)),
        vec![
            Val(Str("bar".to_owned())),
            Val(Str("foo".to_owned())),
            Equal,
        ] => P(Bool(false)),
        vec![Val(Int(1)), Val(Int(1)), Equal] => P(Bool(true)),
        vec![Val(Int(1)), Val(Int(2)), Equal] => P(Bool(false)),
        vec![Val(Bool(true)), Val(Bool(true)), Equal] => P(Bool(true)),
        vec![Val(Bool(false)), Val(Bool(false)), Equal] => P(Bool(true)),
        vec![Val(Bool(true)), Val(Bool(false)), Equal] => P(Bool(false)),
        vec![
            InitTuple,
            Val(Str("foo".to_owned())),
            Val(Int(1)),
            Field,
            InitTuple,
            Val(Str("foo".to_owned())),
            Val(Int(1)),
            Field,
            Equal,
        ] => P(Bool(true)),
        vec![
            InitTuple,
            Val(Str("foo".to_owned())),
            Val(Int(1)),
            Field,
            InitTuple,
            Val(Str("bar".to_owned())),
            Val(Int(1)),
            Field,
            Equal,
        ] => P(Bool(false)),
        vec![
            InitList,
            Val(Str("foo".to_owned())),
            Element,
            InitList,
            Val(Str("foo".to_owned())),
            Element,
            Equal,
        ] => P(Bool(true)),
        vec![
            InitList,
            Val(Str("foo".to_owned())),
            Element,
            InitList,
            Val(Str("bar".to_owned())),
            Element,
            Equal,
        ] => P(Bool(false)),
    ];
}

#[test]
fn test_conditional_jump_ops() {
    assert_cases![
        vec![
            Val(Bool(false)),
            JumpIfTrue(2),
            Val(Bool(true)),
            JumpIfTrue(2),
            Val(Int(1)),
            Jump(1),
            Val(Int(2)),
            Noop,
        ] => P(Int(2)),
        vec![
            Val(Bool(true)),
            JumpIfTrue(2),
            Val(Bool(false)),
            JumpIfTrue(2),
            Val(Int(1)),
            Jump(1),
            Val(Int(2)),
            Noop,
        ] => P(Int(1)),
        vec![
            Val(Int(1)),
            Val(Int(1)),
            Equal,
            JumpIfTrue(2),
            Val(Bool(false)),
            JumpIfTrue(2),
            Val(Int(1)),
            Jump(1),
            Val(Int(2)),
            Noop,
        ] => P(Int(1)),
        vec![
            Val(Int(1)),
            Val(Int(2)),
            Equal,
            JumpIfFalse(2),
            Val(Bool(false)),
            JumpIfTrue(2),
            Val(Int(1)),
            Jump(1),
            Val(Int(2)),
            Noop,
        ] => P(Int(1)),
    ];
}

#[test]
fn test_function_definition_and_call() {
    assert_cases![
        vec![
            Sym("f".to_owned()),     // 0
            InitList,                // 1
            Sym("arg".to_owned()),   // 2
            Element,                 // 3
            Func(6),                 // 4
            DeRef("arg".to_owned()), // 5
            Return,                  // 6
            Bind,                    // 7
            Val(Int(1)),             // 8
            DeRef("f".to_owned()),   // 9
            FCall,                   // 10
        ] => P(Int(1)),
        vec![
            Sym("closed".to_owned()),   // 0
            Val(Int(1)),                // 1
            Bind,                       // 2
            Sym("f".to_owned()),        // 3
            InitList,                   // 4
            Sym("arg".to_owned()),      // 5
            Element,                    // 6
            Func(11),                   // 7
            DeRef("arg".to_owned()),    // 8
            DeRef("closed".to_owned()), // 9
            Add,                        // 10
            Return,                     // 11
            Bind,                       // 12
            Val(Int(1)),                // 13
            DeRef("f".to_owned()),      // 14
            FCall,                      // 16
        ] => P(Int(2)),
    ];
}

#[test]
fn test_module_call() {
    assert_cases![
        vec![
            InitTuple,               // 0 // override tuple
            Sym("one".to_owned()),   // 1
            Val(Int(11)),            // 2
            Field,                   // 3
            Sym("m".to_owned()),     // 4 // binding name for module
            InitTuple,               // 5 // Module tuple bindings
            Sym("one".to_owned()),   // 6
            Val(Int(1)),             // 7
            Field,                   // 8
            Sym("two".to_owned()),   // 9
            Val(Int(2)),             // 10
            Field,                   // 11
            Module(17),              // 12 // Module definition
            Bind,                    // 13
            Sym("foo".to_owned()),   // 14
            DeRef("mod".to_owned()), // 15
            Bind,                    // 16 // bind mod tuple to foo
            Return,                  // 17 // end the module
            Bind,                    // 18 // bind module to the binding name
            DeRef("m".to_owned()),   // 19
            Cp,                      // 20
        ] => C(Tuple(vec![(
            "foo".to_owned(),
            C(Tuple(vec![
                ("one".to_owned(), P(Int(11))),
                ("two".to_owned(), P(Int(2))),
            ])),
        )])),
        vec![
            InitTuple,               // 0 // override tuple
            Sym("one".to_owned()),   // 1
            Val(Int(11)),            // 2
            Field,                   // 3
            Sym("m".to_owned()),     // 4 // binding name for module
            InitTuple,               // 5 // Module tuple bindings
            Sym("one".to_owned()),   // 6
            Val(Int(1)),             // 7
            Field,                   // 8
            Sym("two".to_owned()),   // 9
            Val(Int(2)),             // 10
            Field,                   // 11
            InitThunk(2),            // 12 // Module Return expression
            Val(Int(1)),             // 13
            Return,                  // 14
            Module(20),              // 15 // Module definition
            Bind,                    // 16
            Sym("foo".to_owned()),   // 17
            DeRef("mod".to_owned()), // 18
            Bind,                    // 19 // bind mod tuple to foo
            Return,                  // 20 // end the module
            Bind,                    // 21 // bind module to the binding name
            DeRef("m".to_owned()),   // 22
            Cp,                      // 23
        ] => P(Int(1)),
    ];
}

#[test]
fn test_select_short_circuit() {
    assert_cases![
        vec![
            Sym("field".to_owned()),              // 0 // search field
            Sym("not_field".to_owned()),          // 1 // first field to compare
            SelectJump(2),                        // 2
            Val(Str("not our value".to_owned())), // 3 // expression for first field
            Jump(4),                              // 4
            Sym("field".to_owned()),              // 5 // second field to compare
            SelectJump(2),                        // 6
            Val(Int(1)),                          // 7 // expression for second field
            Jump(2),                              // 8
            Pop,                                  // 9 // pop the search field off
            Bang,                                 // 10 // default case
        ] => P(Int(1)),
        vec![
            Sym("field".to_owned()),              // 0 // search field
            Sym("not_field".to_owned()),          // 1 // first field to compare
            SelectJump(2),                        // 2
            Val(Str("not our value".to_owned())), // 3 // expression for first field
            Jump(4),                              // 4
            Sym("als not field".to_owned()),      // 5 // second field to compare
            SelectJump(2),                        // 6
            Val(Int(2)),                          // 7 // expression for second field
            Jump(1),                              // 8
            Pop,                                  // 9 // pop the search field off
            Val(Int(1)),                          // 10 // default case
        ] => P(Int(1)),
    ];
}

#[test]
fn test_scope_stacks() {
    let mut stack = Stack::new();
    stack.add("one".to_owned(), P(Int(1)));
    let mut val = stack.get("one").unwrap();
    assert_eq!(val, &P(Int(1)));
    stack.push();
    assert!(stack.get("one").is_none());
    stack.to_open();
    val = stack.get("one").unwrap();
    assert_eq!(val, &P(Int(1)));
}
<|endoftext|>


<commit_before>use std::fmt;
use std::fmt::{Show, Formatter};
use serialize::{Decodable, Decoder, Encodable, Encoder};

use url::Url;

use core::{Summary, Package, PackageId};
use sources::{PathSource, GitSource};
use sources::git;
use util::{Config, CargoResult, CargoError};
use util::errors::human;

/// A Source finds and downloads remote packages based on names and
/// versions.
pub trait Source {
    /// The update method performs any network operations required to
    /// get the entire list of all names, versions and dependencies of
    /// packages managed by the Source.
    fn update(&mut self) -> CargoResult<()>;

    /// The list method lists all names, versions and dependencies of
    /// packages managed by the source. It assumes that `update` has
    /// already been called and no additional network operations are
    /// required.
    fn list(&self) -> CargoResult<Vec<Summary>>;

    /// The download method fetches the full package for each name and
    /// version specified.
    fn download(&self, packages: &[PackageId]) -> CargoResult<()>;

    /// The get method returns the Path of each specified package on the
    /// local file system. It assumes that `download` was already called,
    /// and that the packages are already locally available on the file
    /// system.
    fn get(&self, packages: &[PackageId]) -> CargoResult<Vec<Package>>;

    /// Generates a unique string which represents the fingerprint of the
    /// current state of the source.
    ///
    /// This fingerprint is used to determine the "fresheness" of the source
    /// later on. It must be guaranteed that the fingerprint of a source is
    /// constant if and only if the output product will remain constant.
    ///
    /// The `pkg` argument is the package which this fingerprint should only be
    /// interested in for when this source may contain multiple packages.
    fn fingerprint(&self, pkg: &Package) -> CargoResult<String>;
}

#[deriving(Encodable, Decodable, Show, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SourceKind {
    /// GitKind(<git reference>) represents a git repository
    GitKind(String),
    /// represents a local path
    PathKind,
    /// represents the central registry
    RegistryKind
}

#[deriving(Clone, PartialEq, Eq, Hash)]
pub enum Location {
    Local(Path),
    Remote(Url),
}

type Error = Box<CargoError + Send>;

impl<E, D: Decoder<E>> Decodable<D, E> for Location {
    fn decode(d: &mut D) -> Result<Location, E> {
        let url: String  = raw_try!(Decodable::decode(d));
        Ok(Location::parse(url.as_slice()).unwrap())
    }
}

impl<E, S: Encoder<E>> Encodable<S, E> for Location {
    fn encode(&self, e: &mut S) -> Result<(), E> {
        self.to_string().encode(e)
    }
}

#[deriving(Encodable, Decodable, Clone, Eq, Hash)]
pub struct SourceId {
    pub kind: SourceKind,
    pub location: Location,
}

impl Show for Location {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            Local(ref p) => write!(f, "file:{}", p.display()),
            Remote(ref u) => write!(f, "{}", u),
        }
    }
}

impl Location {
    pub fn parse(s: &str) -> CargoResult<Location> {
        if s.starts_with("file:") {
            Ok(Local(Path::new(s.slice_from(5))))
        } else {
            Url::parse(s).map(Remote).map_err(|e| {
                human(format!("invalid url `{}`: `{}", s, e))
            })
        }
    }
}

impl Show for SourceId {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            SourceId { kind: PathKind, ref location } => {
                try!(write!(f, "{}", location))
            },
            SourceId { kind: GitKind(ref reference), ref location } => {
                try!(write!(f, "{}", location));
                if reference.as_slice() != "master" {
                    try!(write!(f, "#ref={}", reference));
                }
            },
            SourceId { kind: RegistryKind, .. } => {
                // TODO: Central registry vs. alternates
                try!(write!(f, "the package registry"));
            }
        }

        Ok(())
    }
}

// This custom implementation handles situations such as when two git sources
// point at *almost* the same URL, but not quite, even when they actually point
// to the same repository.
impl PartialEq for SourceId {
    fn eq(&self, other: &SourceId) -> bool {
        if self.kind != other.kind { return false }
        if self.location == other.location { return true }

        match (&self.kind, &other.kind, &self.location, &other.location) {
            (&GitKind(..), &GitKind(..),
             &Remote(ref u1), &Remote(ref u2)) => {
                git::canonicalize_url(u1.to_string().as_slice()) ==
                    git::canonicalize_url(u2.to_string().as_slice())
            }
            _ => false,
        }
    }
}

impl SourceId {
    pub fn new(kind: SourceKind, location: Location) -> SourceId {
        SourceId { kind: kind, location: location }
    }

    // Pass absolute path
    pub fn for_path(path: &Path) -> SourceId {
        SourceId::new(PathKind, Local(path.clone()))
    }

    pub fn for_git(url: &Url, reference: &str) -> SourceId {
        SourceId::new(GitKind(reference.to_string()), Remote(url.clone()))
    }

    pub fn for_central() -> SourceId {
        SourceId::new(RegistryKind,
                      Remote(Url::parse("https://example.com").unwrap()))
    }

    pub fn get_location<'a>(&'a self) -> &'a Location {
        &self.location
    }

    pub fn is_path(&self) -> bool {
        self.kind == PathKind
    }

    pub fn is_git(&self) -> bool {
        match self.kind {
            GitKind(_) => true,
            _ => false
        }
    }

    pub fn load(&self, config: &mut Config) -> Box<Source> {
        match self.kind {
            GitKind(..) => box GitSource::new(self, config) as Box<Source>,
            PathKind => {
                let path = match self.location {
                    Local(ref p) => p,
                    Remote(..) => fail!("path sources cannot be remote"),
                };
                box PathSource::new(path, self) as Box<Source>
            },
            RegistryKind => unimplemented!()
        }
    }
}

pub struct SourceSet {
    sources: Vec<Box<Source>>
}

impl SourceSet {
    pub fn new(sources: Vec<Box<Source>>) -> SourceSet {
        SourceSet { sources: sources }
    }
}

impl Source for SourceSet {
    fn update(&mut self) -> CargoResult<()> {
        for source in self.sources.mut_iter() {
            try!(source.update());
        }

        Ok(())
    }

    fn list(&self) -> CargoResult<Vec<Summary>> {
        let mut ret = Vec::new();

        for source in self.sources.iter() {
            ret.push_all(try!(source.list()).as_slice());
        }

        Ok(ret)
    }

    fn download(&self, packages: &[PackageId]) -> CargoResult<()> {
        for source in self.sources.iter() {
            try!(source.download(packages));
        }

        Ok(())
    }

    fn get(&self, packages: &[PackageId]) -> CargoResult<Vec<Package>> {
        let mut ret = Vec::new();

        for source in self.sources.iter() {
            ret.push_all(try!(source.get(packages)).as_slice());
        }

        Ok(ret)
    }

    fn fingerprint(&self, id: &Package) -> CargoResult<String> {
        let mut ret = String::new();
        for source in self.sources.iter() {
            ret.push_str(try!(source.fingerprint(id)).as_slice());
        }
        return Ok(ret);
    }
}

#[cfg(test)]
mod tests {
    use super::{SourceId, Remote, GitKind};

    #[test]
    fn github_sources_equal() {
        let loc = Remote(from_str("https://github.com/foo/bar").unwrap());
        let s1 = SourceId::new(GitKind("master".to_string()), loc);

        let loc = Remote(from_str("git://github.com/foo/bar").unwrap());
        let mut s2 = SourceId::new(GitKind("master".to_string()), loc);

        assert_eq!(s1, s2);

        s2.kind = GitKind("foo".to_string());
        assert!(s1 != s2);
    }
}
<commit_msg>auto merge of #224 : alexcrichton/cargo/canonical-hash, r=wycats<commit_after>use std::fmt;
use std::fmt::{Show, Formatter};
use std::hash;
use serialize::{Decodable, Decoder, Encodable, Encoder};

use url::Url;

use core::{Summary, Package, PackageId};
use sources::{PathSource, GitSource};
use sources::git;
use util::{Config, CargoResult, CargoError};
use util::errors::human;

/// A Source finds and downloads remote packages based on names and
/// versions.
pub trait Source {
    /// The update method performs any network operations required to
    /// get the entire list of all names, versions and dependencies of
    /// packages managed by the Source.
    fn update(&mut self) -> CargoResult<()>;

    /// The list method lists all names, versions and dependencies of
    /// packages managed by the source. It assumes that `update` has
    /// already been called and no additional network operations are
    /// required.
    fn list(&self) -> CargoResult<Vec<Summary>>;

    /// The download method fetches the full package for each name and
    /// version specified.
    fn download(&self, packages: &[PackageId]) -> CargoResult<()>;

    /// The get method returns the Path of each specified package on the
    /// local file system. It assumes that `download` was already called,
    /// and that the packages are already locally available on the file
    /// system.
    fn get(&self, packages: &[PackageId]) -> CargoResult<Vec<Package>>;

    /// Generates a unique string which represents the fingerprint of the
    /// current state of the source.
    ///
    /// This fingerprint is used to determine the "fresheness" of the source
    /// later on. It must be guaranteed that the fingerprint of a source is
    /// constant if and only if the output product will remain constant.
    ///
    /// The `pkg` argument is the package which this fingerprint should only be
    /// interested in for when this source may contain multiple packages.
    fn fingerprint(&self, pkg: &Package) -> CargoResult<String>;
}

#[deriving(Encodable, Decodable, Show, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SourceKind {
    /// GitKind(<git reference>) represents a git repository
    GitKind(String),
    /// represents a local path
    PathKind,
    /// represents the central registry
    RegistryKind
}

#[deriving(Clone, PartialEq, Eq, Hash)]
pub enum Location {
    Local(Path),
    Remote(Url),
}

type Error = Box<CargoError + Send>;

impl<E, D: Decoder<E>> Decodable<D, E> for Location {
    fn decode(d: &mut D) -> Result<Location, E> {
        let url: String  = raw_try!(Decodable::decode(d));
        Ok(Location::parse(url.as_slice()).unwrap())
    }
}

impl<E, S: Encoder<E>> Encodable<S, E> for Location {
    fn encode(&self, e: &mut S) -> Result<(), E> {
        self.to_string().encode(e)
    }
}

#[deriving(Encodable, Decodable, Clone, Eq)]
pub struct SourceId {
    pub kind: SourceKind,
    pub location: Location,
}

impl Show for Location {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            Local(ref p) => write!(f, "file:{}", p.display()),
            Remote(ref u) => write!(f, "{}", u),
        }
    }
}

impl Location {
    pub fn parse(s: &str) -> CargoResult<Location> {
        if s.starts_with("file:") {
            Ok(Local(Path::new(s.slice_from(5))))
        } else {
            Url::parse(s).map(Remote).map_err(|e| {
                human(format!("invalid url `{}`: `{}", s, e))
            })
        }
    }
}

impl Show for SourceId {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            SourceId { kind: PathKind, ref location } => {
                try!(write!(f, "{}", location))
            },
            SourceId { kind: GitKind(ref reference), ref location } => {
                try!(write!(f, "{}", location));
                if reference.as_slice() != "master" {
                    try!(write!(f, "#ref={}", reference));
                }
            },
            SourceId { kind: RegistryKind, .. } => {
                // TODO: Central registry vs. alternates
                try!(write!(f, "the package registry"));
            }
        }

        Ok(())
    }
}

// This custom implementation handles situations such as when two git sources
// point at *almost* the same URL, but not quite, even when they actually point
// to the same repository.
impl PartialEq for SourceId {
    fn eq(&self, other: &SourceId) -> bool {
        if self.kind != other.kind { return false }
        if self.location == other.location { return true }

        match (&self.kind, &other.kind, &self.location, &other.location) {
            (&GitKind(..), &GitKind(..),
             &Remote(ref u1), &Remote(ref u2)) => {
                git::canonicalize_url(u1.to_string().as_slice()) ==
                    git::canonicalize_url(u2.to_string().as_slice())
            }
            _ => false,
        }
    }
}

impl<S: hash::Writer> hash::Hash<S> for SourceId {
    fn hash(&self, into: &mut S) {
        match *self {
            SourceId {
                kind: ref kind @ GitKind(..),
                location: Remote(ref url)
            } => {
                kind.hash(into);
                git::canonicalize_url(url.to_string().as_slice()).hash(into);
            }
            _ => {
                self.kind.hash(into);
                self.location.hash(into);
            }
        }
    }
}

impl SourceId {
    pub fn new(kind: SourceKind, location: Location) -> SourceId {
        SourceId { kind: kind, location: location }
    }

    // Pass absolute path
    pub fn for_path(path: &Path) -> SourceId {
        SourceId::new(PathKind, Local(path.clone()))
    }

    pub fn for_git(url: &Url, reference: &str) -> SourceId {
        SourceId::new(GitKind(reference.to_string()), Remote(url.clone()))
    }

    pub fn for_central() -> SourceId {
        SourceId::new(RegistryKind,
                      Remote(Url::parse("https://example.com").unwrap()))
    }

    pub fn get_location<'a>(&'a self) -> &'a Location {
        &self.location
    }

    pub fn is_path(&self) -> bool {
        self.kind == PathKind
    }

    pub fn is_git(&self) -> bool {
        match self.kind {
            GitKind(_) => true,
            _ => false
        }
    }

    pub fn load(&self, config: &mut Config) -> Box<Source> {
        match self.kind {
            GitKind(..) => box GitSource::new(self, config) as Box<Source>,
            PathKind => {
                let path = match self.location {
                    Local(ref p) => p,
                    Remote(..) => fail!("path sources cannot be remote"),
                };
                box PathSource::new(path, self) as Box<Source>
            },
            RegistryKind => unimplemented!()
        }
    }
}

pub struct SourceSet {
    sources: Vec<Box<Source>>
}

impl SourceSet {
    pub fn new(sources: Vec<Box<Source>>) -> SourceSet {
        SourceSet { sources: sources }
    }
}

impl Source for SourceSet {
    fn update(&mut self) -> CargoResult<()> {
        for source in self.sources.mut_iter() {
            try!(source.update());
        }

        Ok(())
    }

    fn list(&self) -> CargoResult<Vec<Summary>> {
        let mut ret = Vec::new();

        for source in self.sources.iter() {
            ret.push_all(try!(source.list()).as_slice());
        }

        Ok(ret)
    }

    fn download(&self, packages: &[PackageId]) -> CargoResult<()> {
        for source in self.sources.iter() {
            try!(source.download(packages));
        }

        Ok(())
    }

    fn get(&self, packages: &[PackageId]) -> CargoResult<Vec<Package>> {
        let mut ret = Vec::new();

        for source in self.sources.iter() {
            ret.push_all(try!(source.get(packages)).as_slice());
        }

        Ok(ret)
    }

    fn fingerprint(&self, id: &Package) -> CargoResult<String> {
        let mut ret = String::new();
        for source in self.sources.iter() {
            ret.push_str(try!(source.fingerprint(id)).as_slice());
        }
        return Ok(ret);
    }
}

#[cfg(test)]
mod tests {
    use super::{SourceId, Remote, GitKind};

    #[test]
    fn github_sources_equal() {
        let loc = Remote(from_str("https://github.com/foo/bar").unwrap());
        let s1 = SourceId::new(GitKind("master".to_string()), loc);

        let loc = Remote(from_str("git://github.com/foo/bar").unwrap());
        let mut s2 = SourceId::new(GitKind("master".to_string()), loc);

        assert_eq!(s1, s2);

        s2.kind = GitKind("foo".to_string());
        assert!(s1 != s2);
    }
}
<|endoftext|>


<commit_before>use std::ops::{Add, Mul, Sub, Div};
use std::num::Zero;

#[derive(Debug,PartialEq)]
pub struct Vector<T> {
	vals: Vec<T>,
	dim: usize,
}

impl<T> Vector<T> {
	pub fn new(data: Vec<T>) -> Vector<T> {
		let dimension = data.len();
		Vector { vals: data, dim: dimension }
	}

	pub fn get(&self, index: usize) -> &T {
		if index >= self.dim {
			panic!("The index given is too high! The vector is not that larger!");
		}

		&self.vals[index]
	}

	pub fn set(&mut self, value: T, index: usize) {
		&self.vals.remove(index);
		&self.vals.insert(index, value);
	}

	pub fn dim(&self) -> usize {
		self.dim
	}

	pub fn data(&self) -> &Vec<T> {
		&self.vals
	}

	pub fn mut_data(&mut self) -> &Vec<T> {
		&mut self.vals
	}
}

impl<T: Zero> Vector<T> {
	pub fn zero(size: usize) -> Vector<T> {
		if size < 1 {
			panic!("The size of the vector must be a positive integer!");
		}

		let mut zero_vec: Vec<T> = Vec::with_capacity(size);
		for _ in 0..size {
			zero_vec.push(T::zero());
		}

		Vector { dim: size, vals: zero_vec }
	}
}

impl<T: Copy + Zero + Mul<T, Output = T> + Add<T, Output = T>> Vector<T> {
	pub fn dot(&self, v: &Vector<T>) -> T {
		if self.dim() != v.dim() {
			panic!("Vectors need to be of the same dimension to dot together!");
		}

		let mut sum: T = T::zero();
		for i in 0..self.dim {
			sum = sum + self.vals[i] * v.vals[i];
		}

		sum
	}
}

impl <T: Clone + Add<T, Output = T>> Vector<T> {
	pub fn add(&self, v: &Vector<T>) -> Vector<T> {
		if self.dim() != v.dim() {
			panic!("Vectors need to be of the same dimension to add!");
		}

		let mut out: Vector<T> = v.clone();
		for i in 0..self.dim {
			out.set(self.vals[i].clone() + v.vals[i].clone(), i);
		}

		out
	}
}

impl <T: Clone + Sub<T, Output = T>> Vector<T> {
	pub fn sub(&self, v: &Vector<T>) -> Vector<T> {
		if self.dim() != v.dim() {
			panic!("Vectors need to be of the same dimension to subtract!");
		}

		let mut out: Vector<T> = v.clone();
		for i in 0..self.dim {
			out.set(self.vals[i].clone() - v.vals[i].clone(), i);
		}

		out
	}
}

impl <T: Clone + Mul<T, Output = T>> Vector<T> {
	pub fn mul(&self, v: &Vector<T>) -> Vector<T> {
		if self.dim() != v.dim() {
			panic!("Vectors need to be of the same dimension to multiply!");
		}

		let mut out: Vector<T> = v.clone();
		for i in 0..self.dim {
			out.set(self.vals[i].clone() * v.vals[i].clone(), i);
		}

		out
	}
}

impl <T: Clone + Mul<T, Output = T>> Vector<T> {
	pub fn scalar_mul(&self, v: T) -> Vector<T> {
		if self.dim() < 1 {
			panic!("Vector needs to be of dimension at least one to scalar multiply!");
		}

		let mut out: Vector<T> = self.clone();
		for i in 0..self.dim {
			out.set(v.clone() * self.vals[i].clone(), i);
		}

		out
	}
}

impl<T: Clone> Clone for Vector<T> {
	fn clone(&self) -> Vector<T> {
		Vector { dim: self.dim, vals: self.vals.clone() }
	}
}

mod test {
	use super::Vector;

	#[test]
	fn check_size() {
		let vec = Vector::new(vec![8, 3, 9]);
		let dimension: usize = 3;
		assert_eq!(vec.dim(), dimension);
	}

	#[test]
	fn check_zero() {
		let vec: Vector<i32> = Vector::zero(3);
		let zero = Vector::new(vec![0, 0, 0]);

		assert_eq!(vec, zero);
	}

	#[test]
	fn check_dot() {
		let vec1 = Vector::new(vec![8, 3, 9]);
		let vec2 = Vector::new(vec![8, 3, 9]);

		assert_eq!(vec1.dot(&vec2), 154);
	}

	#[test]
	#[should_panic]
	fn panic_dot() {
		let vec1 = Vector::new(vec![8, 3]);
		let vec2 = Vector::new(vec![8, 3, 9]);

		assert_eq!(vec1.dot(&vec2), 154);
	}

	#[test]
	fn check_add() {
		let vec1 = Vector::new(vec![8, 3, 9]);
		let vec2 = Vector::new(vec![8, 3, 9]);

		assert_eq!(vec1.add(&vec2), Vector::new(vec![16, 6, 18]));
	}

	#[test]
	#[should_panic]
	fn panic_add() {
		let vec1 = Vector::new(vec![8, 3]);
		let vec2 = Vector::new(vec![8, 3, 9]);

		assert_eq!(vec1.add(&vec2), Vector::new(vec![16, 6, 18]));
	}

	#[test]
	fn check_sub() {
		let vec1 = Vector::new(vec![8,  3, 9]);
		let vec2 = Vector::new(vec![8,  3, 9]);
		let vec3 = Vector::new(vec![1,  4, 3]);
		let vec4 = Vector::new(vec![7, -1, 6]);

		assert_eq!(vec1.sub(&vec2), Vector::zero(3));
		assert_eq!(vec1.sub(&vec3), vec4);
	}

	#[test]
	#[should_panic]
	fn panic_sub() {
		let vec1 = Vector::new(vec![8, 3]);
		let vec2 = Vector::new(vec![1, 4, 3]);
		let vec3 = Vector::new(vec![7, -1, 6]);

		assert_eq!(vec1.sub(&vec2), vec3);
	}

	#[test]
	fn check_mul() {
		let vec1 = Vector::new(vec![ 8, 3, 9]);
		let vec2 = Vector::new(vec![ 3, 2, 1]);
		let vec3 = Vector::new(vec![24, 6, 9]);
		let vec4 = Vector::zero(3);

		assert_eq!(vec1.mul(&vec2), vec3);
		assert_eq!(vec1.mul(&vec4), Vector::zero(3));
	}

	#[test]
	#[should_panic]
	fn panic_mul() {
		let vec1 = Vector::new(vec![ 8, 3]);
		let vec2 = Vector::new(vec![ 3, 2, 1]);
		let vec3 = Vector::new(vec![24, 6, 9]);

		assert_eq!(vec1.mul(&vec2), vec3);
	}

	#[test]
	fn check_scalar_mul() {
		let vec1 = Vector::new(vec![ 8, 3,  9]);
		let vec2 = Vector::new(vec![16, 6, 18]);
		let vec3 = Vector::zero(3);

		assert_eq!(vec1.scalar_mul(2), vec2);
		assert_eq!(vec1.scalar_mul(0), vec3);
	}
}<commit_msg>Added rustdocs to the Vector module<commit_after>//! Vector Module
//!
//! This module implements a wrapper around the std::vec::Vect object that creates an
//! easy interface to compute standard vector operations. This includes basic addition,
//! multiplication, and subtraction, in addition to creating a zero vector and multiplying
//! by a scalar.

use std::ops::{Add, Mul, Sub, Div};
use std::num::Zero;

/// Vector struct
///
/// A struct that creates vectors of arbitrary types. Composed of a dimension and a vector
/// of the values.
#[derive(Debug,PartialEq)]
pub struct Vector<T> {
	vals: Vec<T>,
	dim: usize,
}

impl<T> Vector<T> {

	/// Creates a new Vector<T> given a Vec<T>
	pub fn new(data: Vec<T>) -> Vector<T> {
		let dimension = data.len();
		Vector { vals: data, dim: dimension }
	}

	/// Gets a reference to the value at position index
	pub fn get(&self, index: usize) -> &T {
		if index >= self.dim {
			panic!("The index given is too high! The vector is not that larger!");
		}

		&self.vals[index]
	}

	/// Sets the value at position index to a new value
	pub fn set(&mut self, value: T, index: usize) {
		&self.vals.remove(index);
		&self.vals.insert(index, value);
	}

	/// Returns the dimension of the Vector
	pub fn dim(&self) -> usize {
		self.dim
	}

	/// Returns a reference to the Vec<T> stored inside the Vector
	pub fn data(&self) -> &Vec<T> {
		&self.vals
	}

	/// Returns a mutable reference to the Vec<T> stored inside the Vector
	pub fn mut_data(&mut self) -> &Vec<T> {
		&mut self.vals
	}
}

impl<T: Zero> Vector<T> {
	/// Creates a Vector full of zero elements of a given size
	pub fn zero(size: usize) -> Vector<T> {
		if size < 1 {
			panic!("The size of the vector must be a positive integer!");
		}

		let mut zero_vec: Vec<T> = Vec::with_capacity(size);
		for _ in 0..size {
			zero_vec.push(T::zero());
		}

		Vector { dim: size, vals: zero_vec }
	}
}

impl<T: Copy + Mul<T, Output = T> + Add<T, Output = T>> Vector<T> {
	/// Calculates the dot product of two Vectors
	pub fn dot(&self, v: &Vector<T>) -> T {
		if self.dim() != v.dim() {
			panic!("Vectors need to be of the same dimension to dot together!");
		}

		let mut sum: T = self.vals[0] * v.vals[0];
		for i in 1..self.dim {
			sum = sum + self.vals[i] * v.vals[i];
		}

		sum
	}
}

impl <T: Clone + Add<T, Output = T>> Vector<T> {
	/// Adds two Vectors together
	pub fn add(&self, v: &Vector<T>) -> Vector<T> {
		if self.dim() != v.dim() {
			panic!("Vectors need to be of the same dimension to add!");
		}

		let mut out: Vector<T> = v.clone();
		for i in 0..self.dim {
			out.set(self.vals[i].clone() + v.vals[i].clone(), i);
		}

		out
	}
}

impl <T: Clone + Sub<T, Output = T>> Vector<T> {
	/// Subtracts one Vector from the other
	pub fn sub(&self, v: &Vector<T>) -> Vector<T> {
		if self.dim() != v.dim() {
			panic!("Vectors need to be of the same dimension to subtract!");
		}

		let mut out: Vector<T> = v.clone();
		for i in 0..self.dim {
			out.set(self.vals[i].clone() - v.vals[i].clone(), i);
		}

		out
	}
}

impl <T: Clone + Mul<T, Output = T>> Vector<T> {
	/// Multiplies the elements of one Vector by the corresponding elements of the second
	pub fn mul(&self, v: &Vector<T>) -> Vector<T> {
		if self.dim() != v.dim() {
			panic!("Vectors need to be of the same dimension to multiply!");
		}

		let mut out: Vector<T> = v.clone();
		for i in 0..self.dim {
			out.set(self.vals[i].clone() * v.vals[i].clone(), i);
		}

		out
	}
}

impl <T: Clone + Mul<T, Output = T>> Vector<T> {
	/// Multiplies each element of the Vector by a scalar
	pub fn scalar_mul(&self, v: T) -> Vector<T> {
		if self.dim() < 1 {
			panic!("Vector needs to be of dimension at least one to scalar multiply!");
		}

		let mut out: Vector<T> = self.clone();
		for i in 0..self.dim {
			out.set(v.clone() * self.vals[i].clone(), i);
		}

		out
	}
}

impl<T: Clone> Clone for Vector<T> {
	/// Creates a copy of the Vector
	fn clone(&self) -> Vector<T> {
		Vector { dim: self.dim, vals: self.vals.clone() }
	}
}

mod test {
	use super::Vector;

	#[test]
	fn check_size() {
		let vec = Vector::new(vec![8, 3, 9]);
		let dimension: usize = 3;
		assert_eq!(vec.dim(), dimension);
	}

	#[test]
	fn check_zero() {
		let vec: Vector<i32> = Vector::zero(3);
		let zero = Vector::new(vec![0, 0, 0]);

		assert_eq!(vec, zero);
	}

	#[test]
	fn check_dot() {
		let vec1 = Vector::new(vec![8, 3, 9]);
		let vec2 = Vector::new(vec![8, 3, 9]);

		assert_eq!(vec1.dot(&vec2), 154);
	}

	#[test]
	#[should_panic]
	fn panic_dot() {
		let vec1 = Vector::new(vec![8, 3]);
		let vec2 = Vector::new(vec![8, 3, 9]);

		assert_eq!(vec1.dot(&vec2), 154);
	}

	#[test]
	fn check_add() {
		let vec1 = Vector::new(vec![8, 3, 9]);
		let vec2 = Vector::new(vec![8, 3, 9]);

		assert_eq!(vec1.add(&vec2), Vector::new(vec![16, 6, 18]));
	}

	#[test]
	#[should_panic]
	fn panic_add() {
		let vec1 = Vector::new(vec![8, 3]);
		let vec2 = Vector::new(vec![8, 3, 9]);

		assert_eq!(vec1.add(&vec2), Vector::new(vec![16, 6, 18]));
	}

	#[test]
	fn check_sub() {
		let vec1 = Vector::new(vec![8,  3, 9]);
		let vec2 = Vector::new(vec![8,  3, 9]);
		let vec3 = Vector::new(vec![1,  4, 3]);
		let vec4 = Vector::new(vec![7, -1, 6]);

		assert_eq!(vec1.sub(&vec2), Vector::zero(3));
		assert_eq!(vec1.sub(&vec3), vec4);
	}

	#[test]
	#[should_panic]
	fn panic_sub() {
		let vec1 = Vector::new(vec![8, 3]);
		let vec2 = Vector::new(vec![1, 4, 3]);
		let vec3 = Vector::new(vec![7, -1, 6]);

		assert_eq!(vec1.sub(&vec2), vec3);
	}

	#[test]
	fn check_mul() {
		let vec1 = Vector::new(vec![ 8, 3, 9]);
		let vec2 = Vector::new(vec![ 3, 2, 1]);
		let vec3 = Vector::new(vec![24, 6, 9]);
		let vec4 = Vector::zero(3);

		assert_eq!(vec1.mul(&vec2), vec3);
		assert_eq!(vec1.mul(&vec4), Vector::zero(3));
	}

	#[test]
	#[should_panic]
	fn panic_mul() {
		let vec1 = Vector::new(vec![ 8, 3]);
		let vec2 = Vector::new(vec![ 3, 2, 1]);
		let vec3 = Vector::new(vec![24, 6, 9]);

		assert_eq!(vec1.mul(&vec2), vec3);
	}

	#[test]
	fn check_scalar_mul() {
		let vec1 = Vector::new(vec![ 8, 3,  9]);
		let vec2 = Vector::new(vec![16, 6, 18]);
		let vec3 = Vector::zero(3);

		assert_eq!(vec1.scalar_mul(2), vec2);
		assert_eq!(vec1.scalar_mul(0), vec3);
	}
}<|endoftext|>


<commit_before>use std::time::{Duration, Instant};
use tokio_core::reactor::Handle;
use component::Component;
use tokio_timer::Timer;
use futures::Stream;
use error::Error;
use time;

/// A `Clock` that automatically determines the refresh rate.
///
/// This uses the default [strftime](https://linux.die.net/man/3/strftime) syntax for formatting.
/// Escaping `%` using `%%` is however not supported.
pub struct Clock {
    format: String,
    refresh_rate: Duration,
}

impl Default for Clock {
    /// Creates a `Clock` without any arguments.
    ///
    /// This uses `%T` as the default time format.
    /// [Read more](https://doc.rust-lang.org/nightly/core/default/trait.Default.html#tymethod.default)
    fn default() -> Clock {
        Clock {
            format: "%T".into(),
            refresh_rate: Duration::from_secs(1),
        }
    }
}

impl Clock {
    /// Create a new `Clock` specifying the time format as argument.
    pub fn new<T: Into<String>>(format: T) -> Clock {
        let format = format.into();
        let refresh_rate = get_refresh_rate(&format);
        Clock {
            format: format,
            refresh_rate,
        }
    }
}

// Checks if seconds are part of the `strftime` format.
// If there are no seconds the refresh rate is one minute, otherwise it's one second.
fn get_refresh_rate(format: &str) -> Duration {
    for i in &["%c", "%r", "%S", "%T", "%X"] {
        if format.contains(i) {
            return Duration::from_secs(1);
        }
    }
    Duration::from_secs(60)
}

impl Component for Clock {
    type Error = Error;
    type Stream = Box<Stream<Item = String, Error = Error>>;

    fn stream(self, _: Handle) -> Self::Stream {
        let timer = Timer::default();

        let format = self.format.clone();
        timer.interval_at(Instant::now(), self.refresh_rate).and_then(move |()| {
            Ok(time::strftime(&format, &time::now()).unwrap())
        }).map_err(|_| "timer error".into()).boxed()
    }
}
<commit_msg>Refactor `get_refresh_rate`<commit_after>use std::time::{Duration, Instant};
use tokio_core::reactor::Handle;
use error::{Error, Result};
use component::Component;
use tokio_timer::Timer;
use futures::Stream;
use time;

/// A `Clock` that automatically determines the refresh rate.
///
/// This uses the default [strftime](http://man7.org/linux/man-pages/man3/strftime.3.html) syntax for formatting.
pub struct Clock {
    format: String,
    refresh_rate: Duration,
}

impl Default for Clock {
    /// Creates a `Clock` without any arguments.
    ///
    /// This uses `%T` as the default time format.
    /// [Read more](https://doc.rust-lang.org/nightly/core/default/trait.Default.html#tymethod.default)
    fn default() -> Clock {
        Clock {
            format: "%T".into(),
            refresh_rate: Duration::from_secs(1),
        }
    }
}

impl Clock {
    /// Create a new `Clock` specifying the time format as argument.
    ///
    /// # Errors
    ///
    /// Returns `Err` if the specified `strftime` format could not be parsed.
    pub fn new<T: Into<String>>(format: T) -> Result<Clock> {
        let format = format.into();
        let refresh_rate = get_refresh_rate(&format)?;
        Ok(Clock {
            format: format,
            refresh_rate,
        })
    }
}

// Checks if seconds are part of the `strftime` format.
// If there are no seconds the refresh rate is one minute, otherwise it's one second.
fn get_refresh_rate(format: &str) -> Result<Duration> {
    // Create a time with any non-zero second value
    let mut time = time::now();
    time.tm_sec = 15;

    // Convert to String and back to Tm
    let time_str = time::strftime(format, &time)
        .map_err(|_| "Invalid clock format.")?;
    let time = time::strptime(&time_str, format)
        .map_err(|_| "Invalid clock format.")?;

    // Check if seconds are still there
    if time.tm_sec != 0 {
        Ok(Duration::from_secs(1))
    } else {
        Ok(Duration::from_secs(60))
    }
}

impl Component for Clock {
    type Error = Error;
    type Stream = Box<Stream<Item = String, Error = Error>>;

    fn stream(self, _: Handle) -> Self::Stream {
        let timer = Timer::default();

        let format = self.format.clone();
        timer.interval_at(Instant::now(), self.refresh_rate).and_then(move |()| {
            Ok(time::strftime(&format, &time::now()).unwrap())
        }).map_err(|_| "timer error".into()).boxed()
    }
}
<|endoftext|>


<commit_before>use auto_enums::auto_enum;
use glob::{glob_with, MatchOptions};
use ion_shell::{
    expansion::{unescape, Expander},
    Shell,
};
use liner::{Completer, CursorPosition, Event, EventKind};
use std::{env, iter, path::PathBuf, str};

pub struct IonCompleter<'a, 'b> {
    shell:      &'b Shell<'a>,
    completion: CompletionType,
}

/// Escapes filenames from the completer so that special characters will be properly escaped.
///
/// NOTE: Perhaps we should submit a PR to Liner to add a &'static [u8] field to
/// `FilenameCompleter` so that we don't have to perform the escaping ourselves?
fn escape(input: &str) -> String {
    let mut output = Vec::with_capacity(input.len());
    for character in input.bytes() {
        match character {
            b'(' | b')' | b'[' | b']' | b'&' | b'$' | b'@' | b'{' | b'}' | b'<' | b'>' | b';'
            | b'"' | b'\'' | b'#' | b'^' | b'*' | b' ' => output.push(b'\\'),
            _ => (),
        }
        output.push(character);
    }
    unsafe { String::from_utf8_unchecked(output) }
}

enum CompletionType {
    Nothing,
    Command,
    VariableAndFiles,
}

impl<'a, 'b> IonCompleter<'a, 'b> {
    pub fn new(shell: &'b Shell<'a>) -> Self {
        IonCompleter { shell, completion: CompletionType::Nothing }
    }
}

impl<'a, 'b> Completer for IonCompleter<'a, 'b> {
    fn completions(&mut self, start: &str) -> Vec<String> {
        let mut completions = IonFileCompleter::new(None, &self.shell).completions(start);
        let vars = self.shell.variables();

        match self.completion {
            CompletionType::VariableAndFiles => {
                // Initialize a new completer from the definitions collected.
                // Creates a list of definitions from the shell environment that
                // will be used
                // in the creation of a custom completer.
                if start.is_empty() {
                    completions.extend(vars.string_vars().map(|(s, _)| format!("${}", s)));
                    completions.extend(vars.arrays().map(|(s, _)| format!("@{}", s)));
                } else if start.starts_with('$') {
                    completions.extend(
                        // Add the list of available variables to the completer's
                        // definitions. TODO: We should make
                        // it free to do String->SmallString
                        //       and mostly free to go back (free if allocated)
                        vars.string_vars()
                            .filter(|(s, _)| s.starts_with(&start[1..]))
                            .map(|(s, _)| format!("${}", &s)),
                    );
                } else if start.starts_with('@') {
                    completions.extend(
                        vars.arrays()
                            .filter(|(s, _)| s.starts_with(&start[1..]))
                            .map(|(s, _)| format!("@{}", &s)),
                    );
                }
            }
            CompletionType::Command => {
                // Initialize a new completer from the definitions collected.
                // Creates a list of definitions from the shell environment that
                // will be used
                // in the creation of a custom completer.
                completions.extend(
                    self.shell
                        .builtins()
                        .keys()
                        // Add built-in commands to the completer's definitions.
                        .map(ToString::to_string)
                        // Add the aliases to the completer's definitions.
                        .chain(vars.aliases().map(|(key, _)| key.to_string()))
                        // Add the list of available functions to the completer's
                        // definitions.
                        .chain(vars.functions().map(|(key, _)| key.to_string()))
                        .filter(|s| s.starts_with(start)),
                );
                // Creates completers containing definitions from all directories
                // listed
                // in the environment's **$PATH** variable.
                let file_completers: Vec<_> = if let Some(paths) = env::var_os("PATH") {
                    env::split_paths(&paths)
                        .map(|s| {
                            let s = if !s.to_string_lossy().ends_with("/") {
                                let mut oss = s.into_os_string();
                                oss.push("/");
                                oss.into()
                            } else {
                                s
                            };
                            IonFileCompleter::new(Some(s), &self.shell)
                        })
                        .collect()
                } else {
                    vec![IonFileCompleter::new(Some("/bin/".into()), &self.shell)]
                };
                // Merge the collected definitions with the file path definitions.
                completions.extend(MultiCompleter::new(file_completers).completions(start));
            }
            CompletionType::Nothing => (),
        }

        completions
    }

    fn on_event<W: std::io::Write>(&mut self, event: Event<'_, '_, W>) {
        if let EventKind::BeforeComplete = event.kind {
            let (words, pos) = event.editor.get_words_and_cursor_position();
            self.completion = match pos {
                _ if words.is_empty() => CompletionType::Nothing,
                CursorPosition::InWord(0) => CompletionType::Command,
                CursorPosition::OnWordRightEdge(index) => {
                    if index == 0 {
                        CompletionType::Command
                    } else {
                        let is_pipe = words
                            .into_iter()
                            .nth(index - 1)
                            .map(|(start, end)| event.editor.current_buffer().range(start, end))
                            .filter(|filename| {
                                filename.ends_with("|")
                                    || filename.ends_with("&")
                                    || filename.ends_with(";")
                            })
                            .is_some();
                        if is_pipe {
                            CompletionType::Command
                        } else {
                            CompletionType::VariableAndFiles
                        }
                    }
                }
                _ => CompletionType::VariableAndFiles,
            };
        }
    }
}

/// Performs escaping to an inner `FilenameCompleter` to enable a handful of special cases
/// needed by the shell, such as expanding '~' to a home directory, or adding a backslash
/// when a special character is contained within an expanded filename.
pub struct IonFileCompleter<'a, 'b> {
    shell: &'b Shell<'a>,
    /// The directory the expansion takes place in
    path: PathBuf,
    for_command: bool,
}

impl<'a, 'b> IonFileCompleter<'a, 'b> {
    pub fn new(path: Option<PathBuf>, shell: &'b Shell<'a>) -> Self {
        // The only time a path is Some is when looking for a command not a directory
        // so save this fact to strip the paths when completing commands.
        let for_command = path.is_some();
        let path = path.unwrap_or_default();
        IonFileCompleter { shell, path, for_command }
    }
}

impl<'a, 'b> Completer for IonFileCompleter<'a, 'b> {
    /// When the tab key is pressed, **Liner** will use this method to perform completions of
    /// filenames. As our `IonFileCompleter` is a wrapper around **Liner**'s
    /// `FilenameCompleter`,
    /// the purpose of our custom `Completer` is to expand possible `~` characters in the
    /// `start`
    /// value that we receive from the prompt, grab completions from the inner
    /// `FilenameCompleter`,
    /// and then escape the resulting filenames, as well as remove the expanded form of the `~`
    /// character and re-add the `~` character in it's place.
    fn completions(&mut self, start: &str) -> Vec<String> {
        // Dereferencing the raw pointers here should be entirely safe, theoretically,
        // because no changes will occur to either of the underlying references in the
        // duration between creation of the completers and execution of their
        // completions.
        let expanded = match self.shell.tilde(start) {
            Ok(expanded) => expanded,
            Err(why) => {
                eprintln!("ion: {}", why);
                return vec![start.into()];
            }
        };
        // Now we obtain completions for the `expanded` form of the `start` value.
        let completions = filename_completion(&expanded, &self.path);
        if expanded == start {
            return if self.for_command {
                completions.map(|s| s.rsplit('/').next().map(|s| s.to_string()).unwrap_or(s)).collect()
            } else {
                completions.collect()
            };
        }
        // We can do that by obtaining the index position where the tilde character
        // ends. We don't search with `~` because we also want to
        // handle other tilde variants.
        let t_index = start.find('/').unwrap_or(1);
        // `tilde` is the tilde pattern, and `search` is the pattern that follows.
        let (tilde, search) = start.split_at(t_index);

        if search.len() < 2 {
            // If the length of the search pattern is less than 2, the search pattern is
            // empty, and thus the completions actually contain files and directories in
            // the home directory.

            // The tilde pattern will actually be our `start` command in itself,
            // and the completed form will be all of the characters beyond the length of
            // the expanded form of the tilde pattern.
            completions.map(|completion| [start, &completion[expanded.len()..]].concat()).collect()
        // To save processing time, we should get obtain the index position where our
        // search pattern begins, and re-use that index to slice the completions so
        // that we may re-add the tilde character with the completion that follows.
        } else if let Some(e_index) = expanded.rfind(search) {
            // And then we will need to take those completions and remove the expanded form
            // of the tilde pattern and replace it with that pattern yet again.
            completions
                .map(|completion| escape(&[tilde, &completion[e_index..]].concat()))
                .collect()
        } else {
            Vec::new()
        }
    }
}

#[auto_enum]
fn filename_completion<'a>(start: &'a str, path: &'a PathBuf) -> impl Iterator<Item = String> + 'a {
    let unescaped_start = unescape(start);

    let mut split_start = unescaped_start.split('/');
    let mut string = String::with_capacity(128);

    // When 'start' is an absolute path, "/..." gets split to ["", "..."]
    // So we skip the first element and add "/" to the start of the string
    if unescaped_start.starts_with('/') {
        split_start.next();
        string.push('/');
    } else {
        string.push_str(&path.to_string_lossy());
    }

    for element in split_start {
        string.push_str(element);
        if element != "." && element != ".." {
            string.push('*');
        }
        string.push('/');
    }

    string.pop(); // pop out the last '/' character
    if string.ends_with('.') {
        string.push('*')
    }
    let globs = glob_with(
        &string,
        MatchOptions {
            case_sensitive:              true,
            require_literal_separator:   true,
            require_literal_leading_dot: false,
        },
    )
    .ok()
    .map(|completions| {
        completions.filter_map(Result::ok).filter_map(move |file| {
            let out = file.to_str()?;
            let mut joined = String::with_capacity(out.len() + 3); // worst case senario
            if unescaped_start.starts_with("./") {
                joined.push_str("./");
            }
            joined.push_str(out);
            if file.is_dir() {
                joined.push('/');
            }
            Some(escape(&joined))
        })
    });

    #[auto_enum(Iterator)]
    match globs {
        Some(iter) => iter,
        None => iter::once(escape(start)),
    }
}

/// A completer that combines suggestions from multiple completers.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MultiCompleter<A>(Vec<A>);

impl<A> MultiCompleter<A> {
    pub fn new(completions: Vec<A>) -> Self { MultiCompleter(completions) }
}

impl<A> Completer for MultiCompleter<A>
where
    A: Completer,
{
    fn completions(&mut self, start: &str) -> Vec<String> {
        self.0.iter_mut().flat_map(|comp| comp.completions(start)).collect()
    }
}

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

    #[test]
    fn filename_completion() {
        let shell = Shell::default();
        let mut completer = IonFileCompleter::new(None, &shell);
        assert_eq!(completer.completions("testing"), vec!["testing/"]);
        assert_eq!(completer.completions("testing/file"), vec!["testing/file_with_text"]);
        assert_eq!(completer.completions("~"), vec!["~/"]);
        assert_eq!(completer.completions("tes/fil"), vec!["testing/file_with_text"]);
    }
}
<commit_msg>Forgot the format.<commit_after>use auto_enums::auto_enum;
use glob::{glob_with, MatchOptions};
use ion_shell::{
    expansion::{unescape, Expander},
    Shell,
};
use liner::{Completer, CursorPosition, Event, EventKind};
use std::{env, iter, path::PathBuf, str};

pub struct IonCompleter<'a, 'b> {
    shell:      &'b Shell<'a>,
    completion: CompletionType,
}

/// Escapes filenames from the completer so that special characters will be properly escaped.
///
/// NOTE: Perhaps we should submit a PR to Liner to add a &'static [u8] field to
/// `FilenameCompleter` so that we don't have to perform the escaping ourselves?
fn escape(input: &str) -> String {
    let mut output = Vec::with_capacity(input.len());
    for character in input.bytes() {
        match character {
            b'(' | b')' | b'[' | b']' | b'&' | b'$' | b'@' | b'{' | b'}' | b'<' | b'>' | b';'
            | b'"' | b'\'' | b'#' | b'^' | b'*' | b' ' => output.push(b'\\'),
            _ => (),
        }
        output.push(character);
    }
    unsafe { String::from_utf8_unchecked(output) }
}

enum CompletionType {
    Nothing,
    Command,
    VariableAndFiles,
}

impl<'a, 'b> IonCompleter<'a, 'b> {
    pub fn new(shell: &'b Shell<'a>) -> Self {
        IonCompleter { shell, completion: CompletionType::Nothing }
    }
}

impl<'a, 'b> Completer for IonCompleter<'a, 'b> {
    fn completions(&mut self, start: &str) -> Vec<String> {
        let mut completions = IonFileCompleter::new(None, &self.shell).completions(start);
        let vars = self.shell.variables();

        match self.completion {
            CompletionType::VariableAndFiles => {
                // Initialize a new completer from the definitions collected.
                // Creates a list of definitions from the shell environment that
                // will be used
                // in the creation of a custom completer.
                if start.is_empty() {
                    completions.extend(vars.string_vars().map(|(s, _)| format!("${}", s)));
                    completions.extend(vars.arrays().map(|(s, _)| format!("@{}", s)));
                } else if start.starts_with('$') {
                    completions.extend(
                        // Add the list of available variables to the completer's
                        // definitions. TODO: We should make
                        // it free to do String->SmallString
                        //       and mostly free to go back (free if allocated)
                        vars.string_vars()
                            .filter(|(s, _)| s.starts_with(&start[1..]))
                            .map(|(s, _)| format!("${}", &s)),
                    );
                } else if start.starts_with('@') {
                    completions.extend(
                        vars.arrays()
                            .filter(|(s, _)| s.starts_with(&start[1..]))
                            .map(|(s, _)| format!("@{}", &s)),
                    );
                }
            }
            CompletionType::Command => {
                // Initialize a new completer from the definitions collected.
                // Creates a list of definitions from the shell environment that
                // will be used
                // in the creation of a custom completer.
                completions.extend(
                    self.shell
                        .builtins()
                        .keys()
                        // Add built-in commands to the completer's definitions.
                        .map(ToString::to_string)
                        // Add the aliases to the completer's definitions.
                        .chain(vars.aliases().map(|(key, _)| key.to_string()))
                        // Add the list of available functions to the completer's
                        // definitions.
                        .chain(vars.functions().map(|(key, _)| key.to_string()))
                        .filter(|s| s.starts_with(start)),
                );
                // Creates completers containing definitions from all directories
                // listed
                // in the environment's **$PATH** variable.
                let file_completers: Vec<_> = if let Some(paths) = env::var_os("PATH") {
                    env::split_paths(&paths)
                        .map(|s| {
                            let s = if !s.to_string_lossy().ends_with("/") {
                                let mut oss = s.into_os_string();
                                oss.push("/");
                                oss.into()
                            } else {
                                s
                            };
                            IonFileCompleter::new(Some(s), &self.shell)
                        })
                        .collect()
                } else {
                    vec![IonFileCompleter::new(Some("/bin/".into()), &self.shell)]
                };
                // Merge the collected definitions with the file path definitions.
                completions.extend(MultiCompleter::new(file_completers).completions(start));
            }
            CompletionType::Nothing => (),
        }

        completions
    }

    fn on_event<W: std::io::Write>(&mut self, event: Event<'_, '_, W>) {
        if let EventKind::BeforeComplete = event.kind {
            let (words, pos) = event.editor.get_words_and_cursor_position();
            self.completion = match pos {
                _ if words.is_empty() => CompletionType::Nothing,
                CursorPosition::InWord(0) => CompletionType::Command,
                CursorPosition::OnWordRightEdge(index) => {
                    if index == 0 {
                        CompletionType::Command
                    } else {
                        let is_pipe = words
                            .into_iter()
                            .nth(index - 1)
                            .map(|(start, end)| event.editor.current_buffer().range(start, end))
                            .filter(|filename| {
                                filename.ends_with("|")
                                    || filename.ends_with("&")
                                    || filename.ends_with(";")
                            })
                            .is_some();
                        if is_pipe {
                            CompletionType::Command
                        } else {
                            CompletionType::VariableAndFiles
                        }
                    }
                }
                _ => CompletionType::VariableAndFiles,
            };
        }
    }
}

/// Performs escaping to an inner `FilenameCompleter` to enable a handful of special cases
/// needed by the shell, such as expanding '~' to a home directory, or adding a backslash
/// when a special character is contained within an expanded filename.
pub struct IonFileCompleter<'a, 'b> {
    shell: &'b Shell<'a>,
    /// The directory the expansion takes place in
    path: PathBuf,
    for_command: bool,
}

impl<'a, 'b> IonFileCompleter<'a, 'b> {
    pub fn new(path: Option<PathBuf>, shell: &'b Shell<'a>) -> Self {
        // The only time a path is Some is when looking for a command not a directory
        // so save this fact to strip the paths when completing commands.
        let for_command = path.is_some();
        let path = path.unwrap_or_default();
        IonFileCompleter { shell, path, for_command }
    }
}

impl<'a, 'b> Completer for IonFileCompleter<'a, 'b> {
    /// When the tab key is pressed, **Liner** will use this method to perform completions of
    /// filenames. As our `IonFileCompleter` is a wrapper around **Liner**'s
    /// `FilenameCompleter`,
    /// the purpose of our custom `Completer` is to expand possible `~` characters in the
    /// `start`
    /// value that we receive from the prompt, grab completions from the inner
    /// `FilenameCompleter`,
    /// and then escape the resulting filenames, as well as remove the expanded form of the `~`
    /// character and re-add the `~` character in it's place.
    fn completions(&mut self, start: &str) -> Vec<String> {
        // Dereferencing the raw pointers here should be entirely safe, theoretically,
        // because no changes will occur to either of the underlying references in the
        // duration between creation of the completers and execution of their
        // completions.
        let expanded = match self.shell.tilde(start) {
            Ok(expanded) => expanded,
            Err(why) => {
                eprintln!("ion: {}", why);
                return vec![start.into()];
            }
        };
        // Now we obtain completions for the `expanded` form of the `start` value.
        let completions = filename_completion(&expanded, &self.path);
        if expanded == start {
            return if self.for_command {
                completions
                    .map(|s| s.rsplit('/').next().map(|s| s.to_string()).unwrap_or(s))
                    .collect()
            } else {
                completions.collect()
            };
        }
        // We can do that by obtaining the index position where the tilde character
        // ends. We don't search with `~` because we also want to
        // handle other tilde variants.
        let t_index = start.find('/').unwrap_or(1);
        // `tilde` is the tilde pattern, and `search` is the pattern that follows.
        let (tilde, search) = start.split_at(t_index);

        if search.len() < 2 {
            // If the length of the search pattern is less than 2, the search pattern is
            // empty, and thus the completions actually contain files and directories in
            // the home directory.

            // The tilde pattern will actually be our `start` command in itself,
            // and the completed form will be all of the characters beyond the length of
            // the expanded form of the tilde pattern.
            completions.map(|completion| [start, &completion[expanded.len()..]].concat()).collect()
        // To save processing time, we should get obtain the index position where our
        // search pattern begins, and re-use that index to slice the completions so
        // that we may re-add the tilde character with the completion that follows.
        } else if let Some(e_index) = expanded.rfind(search) {
            // And then we will need to take those completions and remove the expanded form
            // of the tilde pattern and replace it with that pattern yet again.
            completions
                .map(|completion| escape(&[tilde, &completion[e_index..]].concat()))
                .collect()
        } else {
            Vec::new()
        }
    }
}

#[auto_enum]
fn filename_completion<'a>(start: &'a str, path: &'a PathBuf) -> impl Iterator<Item = String> + 'a {
    let unescaped_start = unescape(start);

    let mut split_start = unescaped_start.split('/');
    let mut string = String::with_capacity(128);

    // When 'start' is an absolute path, "/..." gets split to ["", "..."]
    // So we skip the first element and add "/" to the start of the string
    if unescaped_start.starts_with('/') {
        split_start.next();
        string.push('/');
    } else {
        string.push_str(&path.to_string_lossy());
    }

    for element in split_start {
        string.push_str(element);
        if element != "." && element != ".." {
            string.push('*');
        }
        string.push('/');
    }

    string.pop(); // pop out the last '/' character
    if string.ends_with('.') {
        string.push('*')
    }
    let globs = glob_with(
        &string,
        MatchOptions {
            case_sensitive:              true,
            require_literal_separator:   true,
            require_literal_leading_dot: false,
        },
    )
    .ok()
    .map(|completions| {
        completions.filter_map(Result::ok).filter_map(move |file| {
            let out = file.to_str()?;
            let mut joined = String::with_capacity(out.len() + 3); // worst case senario
            if unescaped_start.starts_with("./") {
                joined.push_str("./");
            }
            joined.push_str(out);
            if file.is_dir() {
                joined.push('/');
            }
            Some(escape(&joined))
        })
    });

    #[auto_enum(Iterator)]
    match globs {
        Some(iter) => iter,
        None => iter::once(escape(start)),
    }
}

/// A completer that combines suggestions from multiple completers.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MultiCompleter<A>(Vec<A>);

impl<A> MultiCompleter<A> {
    pub fn new(completions: Vec<A>) -> Self { MultiCompleter(completions) }
}

impl<A> Completer for MultiCompleter<A>
where
    A: Completer,
{
    fn completions(&mut self, start: &str) -> Vec<String> {
        self.0.iter_mut().flat_map(|comp| comp.completions(start)).collect()
    }
}

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

    #[test]
    fn filename_completion() {
        let shell = Shell::default();
        let mut completer = IonFileCompleter::new(None, &shell);
        assert_eq!(completer.completions("testing"), vec!["testing/"]);
        assert_eq!(completer.completions("testing/file"), vec!["testing/file_with_text"]);
        assert_eq!(completer.completions("~"), vec!["~/"]);
        assert_eq!(completer.completions("tes/fil"), vec!["testing/file_with_text"]);
    }
}
<|endoftext|>