<commit_before>extern crate rust_install;
extern crate tempdir;

use rust_install::component::Components;
use rust_install::component::{DirectoryPackage, Package};
use rust_install::component::Transaction;
use rust_install::temp;
use rust_install::utils;
use rust_install::{InstallType, InstallPrefix, NotifyHandler};
use std::fs::{self, OpenOptions, File};
use std::io::Write;
use std::path::Path;
use tempdir::TempDir;

struct MockInstallerBuilder {
    components: Vec<(&'static str, Vec<Command>, Vec<(&'static str, &'static str)>)>,
}

enum Command {
    File(&'static str),
    Dir(&'static str)
}

impl MockInstallerBuilder {
    fn build(&self, path: &Path) {
        for &(name, ref commands, ref files) in &self.components {
            // Update the components file
            let comp_file = path.join("components");
            let ref mut comp_file = OpenOptions::new().write(true).append(true).create(true)
                .open(comp_file).unwrap();
            writeln!(comp_file, "{}", name).unwrap();

            // Create the component directory
            let component_dir = path.join(name);
            fs::create_dir(&component_dir).unwrap();

            // Create the component manifest
            let ref mut manifest = File::create(component_dir.join("manifest.in")).unwrap();
            for command in commands {
                match command {
                    &Command::File(f) => writeln!(manifest, "file:{}", f).unwrap(),
                    &Command::Dir(d) => writeln!(manifest, "dir:{}", d).unwrap(),
                }
            }

            // Create the component files
            for &(ref f_path, ref content) in files {
                let dir_path = component_dir.join(f_path);
                let dir_path = dir_path.parent().unwrap();
                fs::create_dir_all(dir_path).unwrap();

                let ref mut f = File::create(component_dir.join(f_path)).unwrap();
                f.write(content.as_bytes()).unwrap();
            }
        }

        let mut ver = File::create(path.join("rust-installer-version")).unwrap();
        writeln!(ver, "3").unwrap();
    }
}

// Just testing that the mocks work
#[test]
fn mock_smoke_test() {
    let tempdir = TempDir::new("multirust").unwrap();

    let mock = MockInstallerBuilder {
        components: vec![("mycomponent",
                          vec![Command::File("bin/foo"),
                               Command::File("lib/bar"),
                               Command::Dir("doc/stuff")],
                          vec![("bin/foo", "foo"),
                               ("lib/bar", "bar"),
                               ("doc/stuff/doc1", ""),
                               ("doc/stuff/doc2", "")]),
                         ("mycomponent2",
                          vec![Command::File("bin/quux")],
                          vec![("bin/quux", "quux")]
                          )]
    };

    mock.build(tempdir.path());

    assert!(tempdir.path().join("components").exists());
    assert!(tempdir.path().join("mycomponent/manifest.in").exists());
    assert!(tempdir.path().join("mycomponent/bin/foo").exists());
    assert!(tempdir.path().join("mycomponent/lib/bar").exists());
    assert!(tempdir.path().join("mycomponent/doc/stuff/doc1").exists());
    assert!(tempdir.path().join("mycomponent/doc/stuff/doc2").exists());
    assert!(tempdir.path().join("mycomponent2/manifest.in").exists());
    assert!(tempdir.path().join("mycomponent2/bin/quux").exists());
}

#[test]
fn package_contains() {
    let tempdir = TempDir::new("multirust").unwrap();

    let mock = MockInstallerBuilder {
        components: vec![("mycomponent",
                          vec![Command::File("bin/foo")],
                          vec![("bin/foo", "foo")],
                          ),
                         ("mycomponent2",
                          vec![Command::File("bin/bar")],
                          vec![("bin/bar", "bar")]
                          )]
    };

    mock.build(tempdir.path());

    let package = DirectoryPackage::new(tempdir.path().to_owned()).unwrap();
    assert!(package.contains("mycomponent", None));
    assert!(package.contains("mycomponent2", None));
}

#[test]
fn package_bad_version() {
    let tempdir = TempDir::new("multirust").unwrap();

    let mock = MockInstallerBuilder {
        components: vec![("mycomponent",
                          vec![Command::File("bin/foo")],
                          vec![("bin/foo", "foo")])]
    };

    mock.build(tempdir.path());

    let mut ver = File::create(tempdir.path().join("rust-installer-version")).unwrap();
    writeln!(ver, "100").unwrap();

    assert!(DirectoryPackage::new(tempdir.path().to_owned()).is_err());
}

#[test]
fn basic_install() {
    let pkgdir = TempDir::new("multirust").unwrap();

    let mock = MockInstallerBuilder {
        components: vec![("mycomponent",
                          vec![Command::File("bin/foo"),
                               Command::File("lib/bar"),
                               Command::Dir("doc/stuff")],
                          vec![("bin/foo", "foo"),
                               ("lib/bar", "bar"),
                               ("doc/stuff/doc1", ""),
                               ("doc/stuff/doc2", "")])]
    };

    mock.build(pkgdir.path());

    let instdir = TempDir::new("multirust").unwrap();
    let prefix = InstallPrefix::from(instdir.path().to_owned(),
                                     InstallType::Owned);

    let notify = temp::SharedNotifyHandler::none();
    let tmpdir = TempDir::new("multirust").unwrap();
    let tmpcfg = temp::Cfg::new(tmpdir.path().to_owned(), notify);
    let notify = NotifyHandler::none();
    let tx = Transaction::new(prefix.clone(), &tmpcfg, notify);

    let components = Components::open(prefix.clone()).unwrap();

    let pkg = DirectoryPackage::new(pkgdir.path().to_owned()).unwrap();

    let tx = pkg.install(&components, "mycomponent", None, tx).unwrap();
    tx.commit();

    assert!(utils::path_exists(instdir.path().join("bin/foo")));
    assert!(utils::path_exists(instdir.path().join("lib/bar")));
    assert!(utils::path_exists(instdir.path().join("doc/stuff/doc1")));
    assert!(utils::path_exists(instdir.path().join("doc/stuff/doc2")));

    assert!(components.find("mycomponent").unwrap().is_some());
}

#[test]
fn multiple_component_install() {
    let pkgdir = TempDir::new("multirust").unwrap();

    let mock = MockInstallerBuilder {
        components: vec![("mycomponent",
                          vec![Command::File("bin/foo")],
                          vec![("bin/foo", "foo")]),
                         ("mycomponent2",
                          vec![Command::File("lib/bar")],
                          vec![("lib/bar", "bar")])]
    };

    mock.build(pkgdir.path());

    let instdir = TempDir::new("multirust").unwrap();
    let prefix = InstallPrefix::from(instdir.path().to_owned(),
                                     InstallType::Owned);

    let notify = temp::SharedNotifyHandler::none();
    let tmpdir = TempDir::new("multirust").unwrap();
    let tmpcfg = temp::Cfg::new(tmpdir.path().to_owned(), notify);
    let notify = NotifyHandler::none();
    let tx = Transaction::new(prefix.clone(), &tmpcfg, notify);

    let components = Components::open(prefix.clone()).unwrap();

    let pkg = DirectoryPackage::new(pkgdir.path().to_owned()).unwrap();

    let tx = pkg.install(&components, "mycomponent", None, tx).unwrap();
    let tx = pkg.install(&components, "mycomponent2", None, tx).unwrap();
    tx.commit();

    assert!(utils::path_exists(instdir.path().join("bin/foo")));
    assert!(utils::path_exists(instdir.path().join("lib/bar")));

    assert!(components.find("mycomponent").unwrap().is_some());
    assert!(components.find("mycomponent2").unwrap().is_some());
}

#[test]
fn uninstall() {
    let pkgdir = TempDir::new("multirust").unwrap();

    let mock = MockInstallerBuilder {
        components: vec![("mycomponent",
                          vec![Command::File("bin/foo"),
                               Command::File("lib/bar"),
                               Command::Dir("doc/stuff")],
                          vec![("bin/foo", "foo"),
                               ("lib/bar", "bar"),
                               ("doc/stuff/doc1", ""),
                               ("doc/stuff/doc2", "")]),
                         ("mycomponent2",
                          vec![Command::File("lib/quux")],
                          vec![("lib/quux", "quux")])]
    };

    mock.build(pkgdir.path());

    let instdir = TempDir::new("multirust").unwrap();
    let prefix = InstallPrefix::from(instdir.path().to_owned(),
                                     InstallType::Owned);

    let notify = temp::SharedNotifyHandler::none();
    let tmpdir = TempDir::new("multirust").unwrap();
    let tmpcfg = temp::Cfg::new(tmpdir.path().to_owned(), notify);
    let notify = NotifyHandler::none();
    let tx = Transaction::new(prefix.clone(), &tmpcfg, notify);

    let components = Components::open(prefix.clone()).unwrap();

    let pkg = DirectoryPackage::new(pkgdir.path().to_owned()).unwrap();

    let tx = pkg.install(&components, "mycomponent", None, tx).unwrap();
    let tx = pkg.install(&components, "mycomponent2", None, tx).unwrap();
    tx.commit();

    // Now uninstall
    let notify = NotifyHandler::none();
    let mut tx = Transaction::new(prefix.clone(), &tmpcfg, notify);
    for component in components.list().unwrap() {
        tx = component.uninstall(tx).unwrap();
    }
    tx.commit();

    assert!(!utils::path_exists(instdir.path().join("bin/foo")));
    assert!(!utils::path_exists(instdir.path().join("lib/bar")));
    assert!(!utils::path_exists(instdir.path().join("doc/stuff/doc1")));
    assert!(!utils::path_exists(instdir.path().join("doc/stuff/doc2")));
    assert!(!utils::path_exists(instdir.path().join("doc/stuff")));
    assert!(components.find("mycomponent").unwrap().is_none());
    assert!(components.find("mycomponent2").unwrap().is_none());
}

#[test]
fn component_bad_version() {
    let pkgdir = TempDir::new("multirust").unwrap();

    let mock = MockInstallerBuilder {
        components: vec![("mycomponent",
                          vec![Command::File("bin/foo")],
                          vec![("bin/foo", "foo")])]
    };

    mock.build(pkgdir.path());

    let instdir = TempDir::new("multirust").unwrap();
    let prefix = InstallPrefix::from(instdir.path().to_owned(),
                                     InstallType::Owned);

    let notify = temp::SharedNotifyHandler::none();
    let tmpdir = TempDir::new("multirust").unwrap();
    let tmpcfg = temp::Cfg::new(tmpdir.path().to_owned(), notify);
    let notify = NotifyHandler::none();
    let tx = Transaction::new(prefix.clone(), &tmpcfg, notify);

    let components = Components::open(prefix.clone()).unwrap();

    let pkg = DirectoryPackage::new(pkgdir.path().to_owned()).unwrap();

    let tx = pkg.install(&components, "mycomponent", None, tx).unwrap();
    tx.commit();

    // Write a bogus version to the component manifest directory
    utils::write_file("", &prefix.manifest_file("rust-installer-version"), "100\n").unwrap();

    // Can't open components now
    let e = Components::open(prefix.clone()).unwrap_err();
    if let rust_install::Error::BadInstalledMetadataVersion(_) = e { } else { panic!() }
}

// Directories should be 0755, normal files 0644, files that come
// from the bin/ directory 0755.
#[test]
#[cfg(unix)]
fn unix_permissions() {
    use std::os::unix::fs::PermissionsExt;

    let pkgdir = TempDir::new("multirust").unwrap();

    let mock = MockInstallerBuilder {
        components: vec![("mycomponent",
                          vec![Command::File("bin/foo"),
                               Command::File("lib/bar"),
                               Command::Dir("doc/stuff")],
                          vec![("bin/foo", "foo"),
                               ("lib/bar", "bar"),
                               ("doc/stuff/doc1", ""),
                               ("doc/stuff/morestuff/doc2", "")])]
    };

    mock.build(pkgdir.path());

    let instdir = TempDir::new("multirust").unwrap();
    let prefix = InstallPrefix::from(instdir.path().to_owned(),
                                     InstallType::Owned);

    let notify = temp::SharedNotifyHandler::none();
    let tmpdir = TempDir::new("multirust").unwrap();
    let tmpcfg = temp::Cfg::new(tmpdir.path().to_owned(), notify);
    let notify = NotifyHandler::none();
    let tx = Transaction::new(prefix.clone(), &tmpcfg, notify);

    let components = Components::open(prefix.clone()).unwrap();

    let pkg = DirectoryPackage::new(pkgdir.path().to_owned()).unwrap();

    let tx = pkg.install(&components, "mycomponent", None, tx).unwrap();
    tx.commit();

    let m = fs::metadata(instdir.path().join("bin/foo")).unwrap().permissions().mode();
    assert_eq!(m, 0o755);
    let m = fs::metadata(instdir.path().join("lib/bar")).unwrap().permissions().mode();
    assert_eq!(m, 0o644);
    let m = fs::metadata(instdir.path().join("doc/stuff/")).unwrap().permissions().mode();
    assert_eq!(m, 0o755);
    let m = fs::metadata(instdir.path().join("doc/stuff/doc1")).unwrap().permissions().mode();
    assert_eq!(m, 0o644);
    let m = fs::metadata(instdir.path().join("doc/stuff/morestuff")).unwrap().permissions().mode();
    assert_eq!(m, 0o755);
    let m = fs::metadata(instdir.path().join("doc/stuff/morestuff/doc2")).unwrap().permissions().mode();
    assert_eq!(m, 0o644);
}
<commit_msg>Temporarily ignore a test on windows<commit_after>extern crate rust_install;
extern crate tempdir;

use rust_install::component::Components;
use rust_install::component::{DirectoryPackage, Package};
use rust_install::component::Transaction;
use rust_install::temp;
use rust_install::utils;
use rust_install::{InstallType, InstallPrefix, NotifyHandler};
use std::fs::{self, OpenOptions, File};
use std::io::Write;
use std::path::Path;
use tempdir::TempDir;

struct MockInstallerBuilder {
    components: Vec<(&'static str, Vec<Command>, Vec<(&'static str, &'static str)>)>,
}

enum Command {
    File(&'static str),
    Dir(&'static str)
}

impl MockInstallerBuilder {
    fn build(&self, path: &Path) {
        for &(name, ref commands, ref files) in &self.components {
            // Update the components file
            let comp_file = path.join("components");
            let ref mut comp_file = OpenOptions::new().write(true).append(true).create(true)
                .open(comp_file).unwrap();
            writeln!(comp_file, "{}", name).unwrap();

            // Create the component directory
            let component_dir = path.join(name);
            fs::create_dir(&component_dir).unwrap();

            // Create the component manifest
            let ref mut manifest = File::create(component_dir.join("manifest.in")).unwrap();
            for command in commands {
                match command {
                    &Command::File(f) => writeln!(manifest, "file:{}", f).unwrap(),
                    &Command::Dir(d) => writeln!(manifest, "dir:{}", d).unwrap(),
                }
            }

            // Create the component files
            for &(ref f_path, ref content) in files {
                let dir_path = component_dir.join(f_path);
                let dir_path = dir_path.parent().unwrap();
                fs::create_dir_all(dir_path).unwrap();

                let ref mut f = File::create(component_dir.join(f_path)).unwrap();
                f.write(content.as_bytes()).unwrap();
            }
        }

        let mut ver = File::create(path.join("rust-installer-version")).unwrap();
        writeln!(ver, "3").unwrap();
    }
}

// Just testing that the mocks work
#[test]
fn mock_smoke_test() {
    let tempdir = TempDir::new("multirust").unwrap();

    let mock = MockInstallerBuilder {
        components: vec![("mycomponent",
                          vec![Command::File("bin/foo"),
                               Command::File("lib/bar"),
                               Command::Dir("doc/stuff")],
                          vec![("bin/foo", "foo"),
                               ("lib/bar", "bar"),
                               ("doc/stuff/doc1", ""),
                               ("doc/stuff/doc2", "")]),
                         ("mycomponent2",
                          vec![Command::File("bin/quux")],
                          vec![("bin/quux", "quux")]
                          )]
    };

    mock.build(tempdir.path());

    assert!(tempdir.path().join("components").exists());
    assert!(tempdir.path().join("mycomponent/manifest.in").exists());
    assert!(tempdir.path().join("mycomponent/bin/foo").exists());
    assert!(tempdir.path().join("mycomponent/lib/bar").exists());
    assert!(tempdir.path().join("mycomponent/doc/stuff/doc1").exists());
    assert!(tempdir.path().join("mycomponent/doc/stuff/doc2").exists());
    assert!(tempdir.path().join("mycomponent2/manifest.in").exists());
    assert!(tempdir.path().join("mycomponent2/bin/quux").exists());
}

#[test]
fn package_contains() {
    let tempdir = TempDir::new("multirust").unwrap();

    let mock = MockInstallerBuilder {
        components: vec![("mycomponent",
                          vec![Command::File("bin/foo")],
                          vec![("bin/foo", "foo")],
                          ),
                         ("mycomponent2",
                          vec![Command::File("bin/bar")],
                          vec![("bin/bar", "bar")]
                          )]
    };

    mock.build(tempdir.path());

    let package = DirectoryPackage::new(tempdir.path().to_owned()).unwrap();
    assert!(package.contains("mycomponent", None));
    assert!(package.contains("mycomponent2", None));
}

#[test]
fn package_bad_version() {
    let tempdir = TempDir::new("multirust").unwrap();

    let mock = MockInstallerBuilder {
        components: vec![("mycomponent",
                          vec![Command::File("bin/foo")],
                          vec![("bin/foo", "foo")])]
    };

    mock.build(tempdir.path());

    let mut ver = File::create(tempdir.path().join("rust-installer-version")).unwrap();
    writeln!(ver, "100").unwrap();

    assert!(DirectoryPackage::new(tempdir.path().to_owned()).is_err());
}

#[test]
fn basic_install() {
    let pkgdir = TempDir::new("multirust").unwrap();

    let mock = MockInstallerBuilder {
        components: vec![("mycomponent",
                          vec![Command::File("bin/foo"),
                               Command::File("lib/bar"),
                               Command::Dir("doc/stuff")],
                          vec![("bin/foo", "foo"),
                               ("lib/bar", "bar"),
                               ("doc/stuff/doc1", ""),
                               ("doc/stuff/doc2", "")])]
    };

    mock.build(pkgdir.path());

    let instdir = TempDir::new("multirust").unwrap();
    let prefix = InstallPrefix::from(instdir.path().to_owned(),
                                     InstallType::Owned);

    let notify = temp::SharedNotifyHandler::none();
    let tmpdir = TempDir::new("multirust").unwrap();
    let tmpcfg = temp::Cfg::new(tmpdir.path().to_owned(), notify);
    let notify = NotifyHandler::none();
    let tx = Transaction::new(prefix.clone(), &tmpcfg, notify);

    let components = Components::open(prefix.clone()).unwrap();

    let pkg = DirectoryPackage::new(pkgdir.path().to_owned()).unwrap();

    let tx = pkg.install(&components, "mycomponent", None, tx).unwrap();
    tx.commit();

    assert!(utils::path_exists(instdir.path().join("bin/foo")));
    assert!(utils::path_exists(instdir.path().join("lib/bar")));
    assert!(utils::path_exists(instdir.path().join("doc/stuff/doc1")));
    assert!(utils::path_exists(instdir.path().join("doc/stuff/doc2")));

    assert!(components.find("mycomponent").unwrap().is_some());
}

#[test]
fn multiple_component_install() {
    let pkgdir = TempDir::new("multirust").unwrap();

    let mock = MockInstallerBuilder {
        components: vec![("mycomponent",
                          vec![Command::File("bin/foo")],
                          vec![("bin/foo", "foo")]),
                         ("mycomponent2",
                          vec![Command::File("lib/bar")],
                          vec![("lib/bar", "bar")])]
    };

    mock.build(pkgdir.path());

    let instdir = TempDir::new("multirust").unwrap();
    let prefix = InstallPrefix::from(instdir.path().to_owned(),
                                     InstallType::Owned);

    let notify = temp::SharedNotifyHandler::none();
    let tmpdir = TempDir::new("multirust").unwrap();
    let tmpcfg = temp::Cfg::new(tmpdir.path().to_owned(), notify);
    let notify = NotifyHandler::none();
    let tx = Transaction::new(prefix.clone(), &tmpcfg, notify);

    let components = Components::open(prefix.clone()).unwrap();

    let pkg = DirectoryPackage::new(pkgdir.path().to_owned()).unwrap();

    let tx = pkg.install(&components, "mycomponent", None, tx).unwrap();
    let tx = pkg.install(&components, "mycomponent2", None, tx).unwrap();
    tx.commit();

    assert!(utils::path_exists(instdir.path().join("bin/foo")));
    assert!(utils::path_exists(instdir.path().join("lib/bar")));

    assert!(components.find("mycomponent").unwrap().is_some());
    assert!(components.find("mycomponent2").unwrap().is_some());
}

#[ignore] // FIXME windows
#[test]
fn uninstall() {
    let pkgdir = TempDir::new("multirust").unwrap();

    let mock = MockInstallerBuilder {
        components: vec![("mycomponent",
                          vec![Command::File("bin/foo"),
                               Command::File("lib/bar"),
                               Command::Dir("doc/stuff")],
                          vec![("bin/foo", "foo"),
                               ("lib/bar", "bar"),
                               ("doc/stuff/doc1", ""),
                               ("doc/stuff/doc2", "")]),
                         ("mycomponent2",
                          vec![Command::File("lib/quux")],
                          vec![("lib/quux", "quux")])]
    };

    mock.build(pkgdir.path());

    let instdir = TempDir::new("multirust").unwrap();
    let prefix = InstallPrefix::from(instdir.path().to_owned(),
                                     InstallType::Owned);

    let notify = temp::SharedNotifyHandler::none();
    let tmpdir = TempDir::new("multirust").unwrap();
    let tmpcfg = temp::Cfg::new(tmpdir.path().to_owned(), notify);
    let notify = NotifyHandler::none();
    let tx = Transaction::new(prefix.clone(), &tmpcfg, notify);

    let components = Components::open(prefix.clone()).unwrap();

    let pkg = DirectoryPackage::new(pkgdir.path().to_owned()).unwrap();

    let tx = pkg.install(&components, "mycomponent", None, tx).unwrap();
    let tx = pkg.install(&components, "mycomponent2", None, tx).unwrap();
    tx.commit();

    // Now uninstall
    let notify = NotifyHandler::none();
    let mut tx = Transaction::new(prefix.clone(), &tmpcfg, notify);
    for component in components.list().unwrap() {
        tx = component.uninstall(tx).unwrap();
    }
    tx.commit();

    assert!(!utils::path_exists(instdir.path().join("bin/foo")));
    assert!(!utils::path_exists(instdir.path().join("lib/bar")));
    assert!(!utils::path_exists(instdir.path().join("doc/stuff/doc1")));
    assert!(!utils::path_exists(instdir.path().join("doc/stuff/doc2")));
    assert!(!utils::path_exists(instdir.path().join("doc/stuff")));
    assert!(components.find("mycomponent").unwrap().is_none());
    assert!(components.find("mycomponent2").unwrap().is_none());
}

#[test]
fn component_bad_version() {
    let pkgdir = TempDir::new("multirust").unwrap();

    let mock = MockInstallerBuilder {
        components: vec![("mycomponent",
                          vec![Command::File("bin/foo")],
                          vec![("bin/foo", "foo")])]
    };

    mock.build(pkgdir.path());

    let instdir = TempDir::new("multirust").unwrap();
    let prefix = InstallPrefix::from(instdir.path().to_owned(),
                                     InstallType::Owned);

    let notify = temp::SharedNotifyHandler::none();
    let tmpdir = TempDir::new("multirust").unwrap();
    let tmpcfg = temp::Cfg::new(tmpdir.path().to_owned(), notify);
    let notify = NotifyHandler::none();
    let tx = Transaction::new(prefix.clone(), &tmpcfg, notify);

    let components = Components::open(prefix.clone()).unwrap();

    let pkg = DirectoryPackage::new(pkgdir.path().to_owned()).unwrap();

    let tx = pkg.install(&components, "mycomponent", None, tx).unwrap();
    tx.commit();

    // Write a bogus version to the component manifest directory
    utils::write_file("", &prefix.manifest_file("rust-installer-version"), "100\n").unwrap();

    // Can't open components now
    let e = Components::open(prefix.clone()).unwrap_err();
    if let rust_install::Error::BadInstalledMetadataVersion(_) = e { } else { panic!() }
}

// Directories should be 0755, normal files 0644, files that come
// from the bin/ directory 0755.
#[test]
#[cfg(unix)]
fn unix_permissions() {
    use std::os::unix::fs::PermissionsExt;

    let pkgdir = TempDir::new("multirust").unwrap();

    let mock = MockInstallerBuilder {
        components: vec![("mycomponent",
                          vec![Command::File("bin/foo"),
                               Command::File("lib/bar"),
                               Command::Dir("doc/stuff")],
                          vec![("bin/foo", "foo"),
                               ("lib/bar", "bar"),
                               ("doc/stuff/doc1", ""),
                               ("doc/stuff/morestuff/doc2", "")])]
    };

    mock.build(pkgdir.path());

    let instdir = TempDir::new("multirust").unwrap();
    let prefix = InstallPrefix::from(instdir.path().to_owned(),
                                     InstallType::Owned);

    let notify = temp::SharedNotifyHandler::none();
    let tmpdir = TempDir::new("multirust").unwrap();
    let tmpcfg = temp::Cfg::new(tmpdir.path().to_owned(), notify);
    let notify = NotifyHandler::none();
    let tx = Transaction::new(prefix.clone(), &tmpcfg, notify);

    let components = Components::open(prefix.clone()).unwrap();

    let pkg = DirectoryPackage::new(pkgdir.path().to_owned()).unwrap();

    let tx = pkg.install(&components, "mycomponent", None, tx).unwrap();
    tx.commit();

    let m = fs::metadata(instdir.path().join("bin/foo")).unwrap().permissions().mode();
    assert_eq!(m, 0o755);
    let m = fs::metadata(instdir.path().join("lib/bar")).unwrap().permissions().mode();
    assert_eq!(m, 0o644);
    let m = fs::metadata(instdir.path().join("doc/stuff/")).unwrap().permissions().mode();
    assert_eq!(m, 0o755);
    let m = fs::metadata(instdir.path().join("doc/stuff/doc1")).unwrap().permissions().mode();
    assert_eq!(m, 0o644);
    let m = fs::metadata(instdir.path().join("doc/stuff/morestuff")).unwrap().permissions().mode();
    assert_eq!(m, 0o755);
    let m = fs::metadata(instdir.path().join("doc/stuff/morestuff/doc2")).unwrap().permissions().mode();
    assert_eq!(m, 0o644);
}
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc. All rights reserved.
//
// 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.

//! Compute line wrapping breaks for text.

use time;

use xi_rope::rope::{LinesMetric, Rope, RopeInfo};
use xi_rope::tree::Cursor;
use xi_rope::interval::Interval;
use xi_rope::breaks::{Breaks, BreakBuilder, BreaksBaseMetric};
use xi_rope::spans::Spans;
use xi_unicode::LineBreakLeafIter;

use styles::Style;
use tabs::DocumentCtx;
use width_cache::{Token, WidthCache};

struct LineBreakCursor<'a> {
    inner: Cursor<'a, RopeInfo>,
    lb_iter: LineBreakLeafIter,
    last_byte: u8,
}

impl<'a> LineBreakCursor<'a> {
    fn new(text: &'a Rope, pos: usize) -> LineBreakCursor<'a> {
        let inner = Cursor::new(text, pos);
        let lb_iter = match inner.get_leaf() {
            Some((s, offset)) if !s.is_empty() =>
                LineBreakLeafIter::new(s.as_str(), offset),
            _ => LineBreakLeafIter::default()
        };
        LineBreakCursor {
            inner,
            lb_iter,
            last_byte: 0,
        }
    }

    // position and whether break is hard; up to caller to stop calling after EOT
    fn next(&mut self) -> (usize, bool) {
        let mut leaf = self.inner.get_leaf();
        loop {
            match leaf {
                Some((s, offset)) => {
                    let (next, hard) = self.lb_iter.next(s.as_str());
                    if next < s.len() {
                        return (self.inner.pos() - offset + next, hard);
                    }
                    if !s.is_empty() {
                        self.last_byte = s.as_bytes()[s.len() - 1];
                    }
                    leaf = self.inner.next_leaf();
                }
                // A little hacky but only reports last break as hard if final newline
                None => return (self.inner.pos(), self.last_byte == b'\n')
            }
        }
    }
}

pub fn linewrap(text: &Rope, cols: usize) -> Breaks {
    let start_time = time::now();
    let mut lb_cursor = LineBreakCursor::new(text, 0);
    let mut builder = BreakBuilder::new();
    let mut last_pos = 0;
    let mut last_break_pos = 0;
    let mut width = 0;
    loop {
        let (pos, hard) = lb_cursor.next();
        let word_width = pos - last_pos;
        if width > 0 && width + word_width > cols {
            builder.add_break(width);
            //eprintln!("soft break {}", width);
            last_break_pos += width;
            width = 0;
        }
        width += word_width;
        if hard {
            builder.add_break(width);
            //eprintln!("hard break {}", width);
            last_break_pos += width;
            width = 0;
        }
        last_pos = pos;
        if pos == text.len() { break; }
    }
    builder.add_no_break(text.len() - last_break_pos);
    let result = builder.build();
    let time_ms = (time::now() - start_time).num_nanoseconds().unwrap() as f64 * 1e-6;
    eprintln!("time to wrap {} bytes: {:.2}ms", text.len(), time_ms);
    result
}

// `text` is string _after_ editing.
pub fn rewrap(breaks: &mut Breaks, text: &Rope, iv: Interval, newsize: usize, cols: usize) {
    let (edit_iv, new_breaks) = {
        let start_time = time::now();
        let (start, end) = iv.start_end();
        let mut bk_cursor = Cursor::new(breaks, start);
        // start of range to invalidate
        let mut inval_start = bk_cursor.prev::<BreaksBaseMetric>().unwrap_or(0);
        if inval_start > 0 {
            // edit on this line can invalidate break at end of previous
            inval_start = bk_cursor.prev::<BreaksBaseMetric>().unwrap_or(0);
        }
        bk_cursor.set(end);
        // compute end position in edited rope
        let mut inval_end = bk_cursor.next::<BreaksBaseMetric>().map_or(text.len(), |pos|
            pos - (end - start) + newsize);
        let mut lb_cursor = LineBreakCursor::new(text, inval_start);
        let mut builder = BreakBuilder::new();
        let mut last_pos = inval_start;
        let mut last_break_pos = inval_start;
        let mut width = 0;
        loop {
            let (pos, hard) = lb_cursor.next();
            let word_width = pos - last_pos;
            if width > 0 && width + word_width > cols {
                builder.add_break(width);
                last_break_pos += width;
                width = 0;
                while last_break_pos > inval_end {
                    inval_end = bk_cursor.next::<BreaksBaseMetric>().map_or(text.len(), |pos|
                        pos - (end - start) + newsize);
                }
                if last_break_pos == inval_end {
                    break;
                }
            }
            width += word_width;
            if hard {
                // TODO: DRY
                builder.add_break(width);
                last_break_pos += width;
                width = 0;
                while last_break_pos > inval_end {
                    inval_end = bk_cursor.next::<BreaksBaseMetric>().map_or(text.len(), |pos|
                        pos - (end - start) + newsize);
                }
                if last_break_pos == inval_end {
                    break;
                }
            }
            last_pos = pos;
            if pos == text.len() {
                break;
            }
        }
        builder.add_no_break(inval_end - last_break_pos);
        let time_ms = (time::now() - start_time).num_nanoseconds().unwrap() as f64 * 1e-6;
        eprintln!("time to wrap {} bytes: {:.2}ms (not counting build+edit)",
            inval_end - inval_start, time_ms);
        (Interval::new_open_closed(inval_start, inval_end + (end - start) - newsize), builder.build())
    };
    breaks.edit(edit_iv, new_breaks);
}

/// A potential opportunity to insert a break. In this representation, the widths
/// have been requested (in a batch request) but are not necessarily known until
/// the request is issued.
struct PotentialBreak {
    pos: usize,  // offset within text of the end of the word
    tok: Token,
    hard: bool,
}

// State for a rewrap in progress
struct RewrapCtx<'a> {
    text: &'a Rope,
    lb_cursor: LineBreakCursor<'a>,
    lb_cursor_pos: usize,
    width_cache: &'a mut WidthCache,
    doc_ctx: &'a DocumentCtx,
    pot_breaks: Vec<PotentialBreak>,
    pot_break_ix: usize,  // index within pot_breaks
    max_offset: usize, // offset of maximum break (ie hard break following edit)
    max_width: f64,
}

// This constant should be tuned so that the RPC takes about 1ms. Less than that,
// RPC overhead becomes significant. More than that, interactivity suffers.
const MAX_POT_BREAKS: usize = 10_000;

impl<'a> RewrapCtx<'a> {
    fn new(text: &'a Rope, /* _style_spans: &Spans<Style>, */ doc_ctx: &'a DocumentCtx,
        max_width: f64, width_cache: &'a mut WidthCache, start: usize) -> RewrapCtx<'a>
    {
        let lb_cursor_pos = start;
        let lb_cursor = LineBreakCursor::new(text, start);
        RewrapCtx {
            text,
            lb_cursor,
            lb_cursor_pos,
            width_cache,
            doc_ctx,
            pot_breaks: Vec::new(),
            pot_break_ix: 0,
            max_offset: text.len(),
            max_width,
        }
    }

    fn refill_pot_breaks(&mut self) {
        let style_id = 2;  // TODO: derive from style spans rather than assuming.

        let mut req = self.width_cache.batch_req();

        self.pot_breaks.clear();
        self.pot_break_ix = 0;
        let mut pos = self.lb_cursor_pos;
        while pos < self.max_offset && self.pot_breaks.len() < MAX_POT_BREAKS {
            let (next, hard) = self.lb_cursor.next();
            // TODO: avoid allocating string
            let word = self.text.slice_to_string(pos, next);
            let tok = req.request(style_id, &word);
            pos = next;
            self.pot_breaks.push(PotentialBreak { pos, tok, hard });
        }
        req.issue(self.doc_ctx).unwrap();
        self.lb_cursor_pos = pos;
    }

    // Compute the next break, assuming `start` is a valid break.
    //
    // Invariant: start corresponds to the start of the word referenced by pot_break_ix.
    fn wrap_one_line(&mut self, start: usize) -> Option<usize> {
        let mut line_width = 0.0;
        let mut pos = start;
        while pos < self.max_offset {
            if self.pot_break_ix >= self.pot_breaks.len() {
                self.refill_pot_breaks();
            }
            let pot_break = &self.pot_breaks[self.pot_break_ix];
            if pot_break.hard {
                self.pot_break_ix += 1;
                return Some(pot_break.pos);
            }
            let width = self.width_cache.resolve(pot_break.tok);
            if line_width == 0.0 && width >= self.max_width {
                self.pot_break_ix += 1;
                return Some(pot_break.pos);
            }
            line_width += width;
            if line_width > self.max_width {
                return Some(pos);
            }
            self.pot_break_ix += 1;
            pos = pot_break.pos;
        }
        None
    }
}

/// Wrap the text (in batch mode) using width measurement.
pub fn linewrap_width(text: &Rope, _style_spans: &Spans<Style>, doc_ctx: &DocumentCtx,
        max_width: f64) -> Breaks
{
    // TODO: this should be scoped to the FE. However, it will work (just with
    // degraded performance).
    let mut width_cache = WidthCache::new();
    let mut ctx = RewrapCtx::new(text, /* style_spans, */ doc_ctx, max_width, &mut width_cache, 0);
    let mut builder = BreakBuilder::new();
    let mut pos = 0;
    while let Some(next) = ctx.wrap_one_line(pos) {
        builder.add_break(next - pos);
        pos = next;
    }
    builder.add_no_break(text.len() - pos);
    builder.build()
}

/// Compute a new chunk of breaks after an edit. Returns new breaks to replace
/// the old ones. The interval [start..end] represents a frontier.
fn compute_rewrap_width(text: &Rope, /* style_spans: &Spans<Style>, */ doc_ctx: &DocumentCtx,
    max_width: f64, breaks: &Breaks, start: usize, end: usize) -> Breaks
{
    let mut width_cache = WidthCache::new();
    let mut ctx = RewrapCtx::new(text, /* style_spans, */ doc_ctx, max_width, &mut width_cache, start);
    let mut builder = BreakBuilder::new();
    let mut pos = start;
    let mut break_cursor = Cursor::new(&breaks, end);
    // TODO: factor this into `at_or_next` method on cursor.
    let mut next_break = if break_cursor.is_boundary::<BreaksBaseMetric>() {
        Some(end)
    } else {
        break_cursor.next::<BreaksBaseMetric>()
    };
    loop {
        // iterate newly computed breaks and existing breaks until they converge
        if let Some(new_next) = ctx.wrap_one_line(pos) {
            while let Some(old_next) = next_break {
                if old_next >= new_next {
                    break;
                }
                next_break = break_cursor.next::<BreaksBaseMetric>();
            }
            // TODO: we might be able to tighten the logic, avoiding this last break,
            // in some cases (resulting in a smaller delta).
            builder.add_break(new_next - pos);
            if let Some(old_next) = next_break {
                if new_next == old_next {
                    // Breaking process has converged.
                    break;
                }
            }
            pos = new_next;
        } else {
            // EOF
            builder.add_no_break(text.len() - pos);
            break;
        }
    }
    return builder.build();
}

pub fn rewrap_width(breaks: &mut Breaks, text: &Rope, /* style_spans: &Spans<Style>, */
    doc_ctx: &DocumentCtx, iv: Interval, newsize: usize, max_width: f64)
{
    // First, remove any breaks in edited section.
    let mut builder = BreakBuilder::new();
    builder.add_no_break(newsize);
    breaks.edit(iv, builder.build());
    // At this point, breaks is aligned with text.

    let mut start = iv.start();
    let end = start + newsize;
    // [start..end] is edited range in text

    // Find a point earlier than any possible breaks change. For simplicity, this is the
    // beginning of the paragraph, but going back two breaks would be better.
    let mut cursor = Cursor::new(&text, start);
    if !cursor.is_boundary::<LinesMetric>() {
        start = cursor.prev::<LinesMetric>().unwrap_or(0);
    }

    let new_breaks = compute_rewrap_width(text, /* style_spans, */ doc_ctx, max_width, breaks,
        start, end);
    let edit_iv = Interval::new_open_closed(start, start + new_breaks.len());
    breaks.edit(edit_iv, new_breaks);
}
<commit_msg>More precise end for measure request<commit_after>// Copyright 2016 Google Inc. All rights reserved.
//
// 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.

//! Compute line wrapping breaks for text.

use time;

use xi_rope::rope::{LinesMetric, Rope, RopeInfo};
use xi_rope::tree::Cursor;
use xi_rope::interval::Interval;
use xi_rope::breaks::{Breaks, BreakBuilder, BreaksBaseMetric};
use xi_rope::spans::Spans;
use xi_unicode::LineBreakLeafIter;

use styles::Style;
use tabs::DocumentCtx;
use width_cache::{Token, WidthCache};

struct LineBreakCursor<'a> {
    inner: Cursor<'a, RopeInfo>,
    lb_iter: LineBreakLeafIter,
    last_byte: u8,
}

impl<'a> LineBreakCursor<'a> {
    fn new(text: &'a Rope, pos: usize) -> LineBreakCursor<'a> {
        let inner = Cursor::new(text, pos);
        let lb_iter = match inner.get_leaf() {
            Some((s, offset)) if !s.is_empty() =>
                LineBreakLeafIter::new(s.as_str(), offset),
            _ => LineBreakLeafIter::default()
        };
        LineBreakCursor {
            inner,
            lb_iter,
            last_byte: 0,
        }
    }

    // position and whether break is hard; up to caller to stop calling after EOT
    fn next(&mut self) -> (usize, bool) {
        let mut leaf = self.inner.get_leaf();
        loop {
            match leaf {
                Some((s, offset)) => {
                    let (next, hard) = self.lb_iter.next(s.as_str());
                    if next < s.len() {
                        return (self.inner.pos() - offset + next, hard);
                    }
                    if !s.is_empty() {
                        self.last_byte = s.as_bytes()[s.len() - 1];
                    }
                    leaf = self.inner.next_leaf();
                }
                // A little hacky but only reports last break as hard if final newline
                None => return (self.inner.pos(), self.last_byte == b'\n')
            }
        }
    }
}

pub fn linewrap(text: &Rope, cols: usize) -> Breaks {
    let start_time = time::now();
    let mut lb_cursor = LineBreakCursor::new(text, 0);
    let mut builder = BreakBuilder::new();
    let mut last_pos = 0;
    let mut last_break_pos = 0;
    let mut width = 0;
    loop {
        let (pos, hard) = lb_cursor.next();
        let word_width = pos - last_pos;
        if width > 0 && width + word_width > cols {
            builder.add_break(width);
            //eprintln!("soft break {}", width);
            last_break_pos += width;
            width = 0;
        }
        width += word_width;
        if hard {
            builder.add_break(width);
            //eprintln!("hard break {}", width);
            last_break_pos += width;
            width = 0;
        }
        last_pos = pos;
        if pos == text.len() { break; }
    }
    builder.add_no_break(text.len() - last_break_pos);
    let result = builder.build();
    let time_ms = (time::now() - start_time).num_nanoseconds().unwrap() as f64 * 1e-6;
    eprintln!("time to wrap {} bytes: {:.2}ms", text.len(), time_ms);
    result
}

// `text` is string _after_ editing.
pub fn rewrap(breaks: &mut Breaks, text: &Rope, iv: Interval, newsize: usize, cols: usize) {
    let (edit_iv, new_breaks) = {
        let start_time = time::now();
        let (start, end) = iv.start_end();
        let mut bk_cursor = Cursor::new(breaks, start);
        // start of range to invalidate
        let mut inval_start = bk_cursor.prev::<BreaksBaseMetric>().unwrap_or(0);
        if inval_start > 0 {
            // edit on this line can invalidate break at end of previous
            inval_start = bk_cursor.prev::<BreaksBaseMetric>().unwrap_or(0);
        }
        bk_cursor.set(end);
        // compute end position in edited rope
        let mut inval_end = bk_cursor.next::<BreaksBaseMetric>().map_or(text.len(), |pos|
            pos - (end - start) + newsize);
        let mut lb_cursor = LineBreakCursor::new(text, inval_start);
        let mut builder = BreakBuilder::new();
        let mut last_pos = inval_start;
        let mut last_break_pos = inval_start;
        let mut width = 0;
        loop {
            let (pos, hard) = lb_cursor.next();
            let word_width = pos - last_pos;
            if width > 0 && width + word_width > cols {
                builder.add_break(width);
                last_break_pos += width;
                width = 0;
                while last_break_pos > inval_end {
                    inval_end = bk_cursor.next::<BreaksBaseMetric>().map_or(text.len(), |pos|
                        pos - (end - start) + newsize);
                }
                if last_break_pos == inval_end {
                    break;
                }
            }
            width += word_width;
            if hard {
                // TODO: DRY
                builder.add_break(width);
                last_break_pos += width;
                width = 0;
                while last_break_pos > inval_end {
                    inval_end = bk_cursor.next::<BreaksBaseMetric>().map_or(text.len(), |pos|
                        pos - (end - start) + newsize);
                }
                if last_break_pos == inval_end {
                    break;
                }
            }
            last_pos = pos;
            if pos == text.len() {
                break;
            }
        }
        builder.add_no_break(inval_end - last_break_pos);
        let time_ms = (time::now() - start_time).num_nanoseconds().unwrap() as f64 * 1e-6;
        eprintln!("time to wrap {} bytes: {:.2}ms (not counting build+edit)",
            inval_end - inval_start, time_ms);
        (Interval::new_open_closed(inval_start, inval_end + (end - start) - newsize), builder.build())
    };
    breaks.edit(edit_iv, new_breaks);
}

/// A potential opportunity to insert a break. In this representation, the widths
/// have been requested (in a batch request) but are not necessarily known until
/// the request is issued.
struct PotentialBreak {
    pos: usize,  // offset within text of the end of the word
    tok: Token,
    hard: bool,
}

// State for a rewrap in progress
struct RewrapCtx<'a> {
    text: &'a Rope,
    lb_cursor: LineBreakCursor<'a>,
    lb_cursor_pos: usize,
    width_cache: &'a mut WidthCache,
    doc_ctx: &'a DocumentCtx,
    pot_breaks: Vec<PotentialBreak>,
    pot_break_ix: usize,  // index within pot_breaks
    max_offset: usize, // offset of maximum break (ie hard break following edit)
    max_width: f64,
}

// This constant should be tuned so that the RPC takes about 1ms. Less than that,
// RPC overhead becomes significant. More than that, interactivity suffers.
const MAX_POT_BREAKS: usize = 10_000;

impl<'a> RewrapCtx<'a> {
    fn new(text: &'a Rope, /* _style_spans: &Spans<Style>, */ doc_ctx: &'a DocumentCtx,
        max_width: f64, width_cache: &'a mut WidthCache, start: usize, end: usize) -> RewrapCtx<'a>
    {
        let lb_cursor_pos = start;
        let lb_cursor = LineBreakCursor::new(text, start);
        RewrapCtx {
            text,
            lb_cursor,
            lb_cursor_pos,
            width_cache,
            doc_ctx,
            pot_breaks: Vec::new(),
            pot_break_ix: 0,
            max_offset: end,
            max_width,
        }
    }

    fn refill_pot_breaks(&mut self) {
        let style_id = 2;  // TODO: derive from style spans rather than assuming.

        let mut req = self.width_cache.batch_req();

        self.pot_breaks.clear();
        self.pot_break_ix = 0;
        let mut pos = self.lb_cursor_pos;
        while pos < self.max_offset && self.pot_breaks.len() < MAX_POT_BREAKS {
            let (next, hard) = self.lb_cursor.next();
            // TODO: avoid allocating string
            let word = self.text.slice_to_string(pos, next);
            let tok = req.request(style_id, &word);
            pos = next;
            self.pot_breaks.push(PotentialBreak { pos, tok, hard });
        }
        req.issue(self.doc_ctx).unwrap();
        self.lb_cursor_pos = pos;
    }

    // Compute the next break, assuming `start` is a valid break.
    //
    // Invariant: start corresponds to the start of the word referenced by pot_break_ix.
    fn wrap_one_line(&mut self, start: usize) -> Option<usize> {
        let mut line_width = 0.0;
        let mut pos = start;
        while pos < self.max_offset {
            if self.pot_break_ix >= self.pot_breaks.len() {
                self.refill_pot_breaks();
            }
            let pot_break = &self.pot_breaks[self.pot_break_ix];
            if pot_break.hard {
                self.pot_break_ix += 1;
                return Some(pot_break.pos);
            }
            let width = self.width_cache.resolve(pot_break.tok);
            if line_width == 0.0 && width >= self.max_width {
                self.pot_break_ix += 1;
                return Some(pot_break.pos);
            }
            line_width += width;
            if line_width > self.max_width {
                return Some(pos);
            }
            self.pot_break_ix += 1;
            pos = pot_break.pos;
        }
        None
    }
}

/// Wrap the text (in batch mode) using width measurement.
pub fn linewrap_width(text: &Rope, _style_spans: &Spans<Style>, doc_ctx: &DocumentCtx,
        max_width: f64) -> Breaks
{
    // TODO: this should be scoped to the FE. However, it will work (just with
    // degraded performance).
    let mut width_cache = WidthCache::new();
    let mut ctx = RewrapCtx::new(text, /* style_spans, */ doc_ctx, max_width, &mut width_cache,
        0, text.len());
    let mut builder = BreakBuilder::new();
    let mut pos = 0;
    while let Some(next) = ctx.wrap_one_line(pos) {
        builder.add_break(next - pos);
        pos = next;
    }
    builder.add_no_break(text.len() - pos);
    builder.build()
}

/// Compute a new chunk of breaks after an edit. Returns new breaks to replace
/// the old ones. The interval [start..end] represents a frontier.
fn compute_rewrap_width(text: &Rope, /* style_spans: &Spans<Style>, */ doc_ctx: &DocumentCtx,
    max_width: f64, breaks: &Breaks, start: usize, end: usize) -> Breaks
{
    let mut width_cache = WidthCache::new();
    let mut line_cursor = Cursor::new(&text, end);
    let measure_end = if line_cursor.is_boundary::<LinesMetric>() {
        end
    } else {
        line_cursor.next::<LinesMetric>().unwrap_or(text.len())
    };
    let mut ctx = RewrapCtx::new(text, /* style_spans, */ doc_ctx, max_width, &mut width_cache,
        start, measure_end);
    let mut builder = BreakBuilder::new();
    let mut pos = start;
    let mut break_cursor = Cursor::new(&breaks, end);
    // TODO: factor this into `at_or_next` method on cursor.
    let mut next_break = if break_cursor.is_boundary::<BreaksBaseMetric>() {
        Some(end)
    } else {
        break_cursor.next::<BreaksBaseMetric>()
    };
    loop {
        // iterate newly computed breaks and existing breaks until they converge
        if let Some(new_next) = ctx.wrap_one_line(pos) {
            while let Some(old_next) = next_break {
                if old_next >= new_next {
                    break;
                }
                next_break = break_cursor.next::<BreaksBaseMetric>();
            }
            // TODO: we might be able to tighten the logic, avoiding this last break,
            // in some cases (resulting in a smaller delta).
            builder.add_break(new_next - pos);
            if let Some(old_next) = next_break {
                if new_next == old_next {
                    // Breaking process has converged.
                    break;
                }
            }
            pos = new_next;
        } else {
            // EOF
            builder.add_no_break(text.len() - pos);
            break;
        }
    }
    return builder.build();
}

pub fn rewrap_width(breaks: &mut Breaks, text: &Rope, /* style_spans: &Spans<Style>, */
    doc_ctx: &DocumentCtx, iv: Interval, newsize: usize, max_width: f64)
{
    // First, remove any breaks in edited section.
    let mut builder = BreakBuilder::new();
    builder.add_no_break(newsize);
    breaks.edit(iv, builder.build());
    // At this point, breaks is aligned with text.

    let mut start = iv.start();
    let end = start + newsize;
    // [start..end] is edited range in text

    // Find a point earlier than any possible breaks change. For simplicity, this is the
    // beginning of the paragraph, but going back two breaks would be better.
    let mut cursor = Cursor::new(&text, start);
    if !cursor.is_boundary::<LinesMetric>() {
        start = cursor.prev::<LinesMetric>().unwrap_or(0);
    }

    let new_breaks = compute_rewrap_width(text, /* style_spans, */ doc_ctx, max_width, breaks,
        start, end);
    let edit_iv = Interval::new_open_closed(start, start + new_breaks.len());
    breaks.edit(edit_iv, new_breaks);
}
<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

extern crate clap;
#[macro_use] extern crate log;
extern crate semver;
#[macro_use] extern crate version;
extern crate itertools;

extern crate libimagnotes;
extern crate libimagrt;
extern crate libimagentryedit;
extern crate libimagentrytag;
extern crate libimagerror;
extern crate libimagutil;

use std::process::exit;

use itertools::Itertools;

use libimagentryedit::edit::Edit;
use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagnotes::note::Note;
use libimagerror::trace::{MapErrTrace, trace_error};
use libimagutil::info_result::*;
use libimagutil::warn_result::WarnResult;

mod ui;
use ui::build_ui;

fn main() {
    let rt = generate_runtime_setup("imag-notes",
                                    &version!()[..],
                                    "Note taking helper",
                                    build_ui);

    rt.cli()
        .subcommand_name()
        .map(|name| {
            debug!("Call: {}", name);
            match name {
                "create" => create(&rt),
                "delete" => delete(&rt),
                "edit"   => edit(&rt),
                "list"   => list(&rt),
                _        => {
                    debug!("Unknown command"); // More error handling
                },
            };
        });
}

fn name_from_cli(rt: &Runtime, subcmd: &str) -> String {
    rt.cli().subcommand_matches(subcmd).unwrap().value_of("name").map(String::from).unwrap()
}

fn create(rt: &Runtime) {
    let name = name_from_cli(rt, "create");
    Note::new(rt.store(), name.clone(), String::new()).map_err_trace().ok();

    if rt.cli().subcommand_matches("create").unwrap().is_present("edit") &&
            !edit_entry(rt, name) {
        exit(1);
    }
}

fn delete(rt: &Runtime) {
    Note::delete(rt.store(), String::from(name_from_cli(rt, "delete")))
        .map_err_trace()
        .map_info_str("Ok")
        .ok();
}

fn edit(rt: &Runtime) {
    edit_entry(rt, name_from_cli(rt, "edit"));
}

fn edit_entry(rt: &Runtime, name: String) -> bool {
    let mut note = match Note::get(rt.store(), name) {
        Ok(Some(note)) => note,
        Ok(None) => {
            warn!("Cannot edit nonexistent Note");
            return false
        },
        Err(e) => {
            trace_error(&e);
            warn!("Cannot edit nonexistent Note");
            return false
        },
    };

    note.edit_content(rt).map_err_trace().map_warn_err_str("Editing failed").is_ok()
}

fn list(rt: &Runtime) {
    use std::cmp::Ordering;

    Note::all_notes(rt.store())
        .map_err_trace_exit(1)
        .map(|iter| {
            let notes = iter.filter_map(|note| note.map_err_trace().ok())
                .sort_by(|note_a, note_b| {
                    if let (Ok(a), Ok(b)) = (note_a.get_name(), note_b.get_name()) {
                        return a.cmp(&b)
                    } else {
                        return Ordering::Greater;
                    }
                });

            for note in notes.iter() {
                note.get_name()
                    .map(|name| println!("{}", name))
                    .map_err_trace()
                    .ok();
            }
        })
        .ok();
}

<commit_msg>Fix for itertools update: sort_by -> sorted_by<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

extern crate clap;
#[macro_use] extern crate log;
extern crate semver;
#[macro_use] extern crate version;
extern crate itertools;

extern crate libimagnotes;
extern crate libimagrt;
extern crate libimagentryedit;
extern crate libimagentrytag;
extern crate libimagerror;
extern crate libimagutil;

use std::process::exit;

use itertools::Itertools;

use libimagentryedit::edit::Edit;
use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagnotes::note::Note;
use libimagerror::trace::{MapErrTrace, trace_error};
use libimagutil::info_result::*;
use libimagutil::warn_result::WarnResult;

mod ui;
use ui::build_ui;

fn main() {
    let rt = generate_runtime_setup("imag-notes",
                                    &version!()[..],
                                    "Note taking helper",
                                    build_ui);

    rt.cli()
        .subcommand_name()
        .map(|name| {
            debug!("Call: {}", name);
            match name {
                "create" => create(&rt),
                "delete" => delete(&rt),
                "edit"   => edit(&rt),
                "list"   => list(&rt),
                _        => {
                    debug!("Unknown command"); // More error handling
                },
            };
        });
}

fn name_from_cli(rt: &Runtime, subcmd: &str) -> String {
    rt.cli().subcommand_matches(subcmd).unwrap().value_of("name").map(String::from).unwrap()
}

fn create(rt: &Runtime) {
    let name = name_from_cli(rt, "create");
    Note::new(rt.store(), name.clone(), String::new()).map_err_trace().ok();

    if rt.cli().subcommand_matches("create").unwrap().is_present("edit") &&
            !edit_entry(rt, name) {
        exit(1);
    }
}

fn delete(rt: &Runtime) {
    Note::delete(rt.store(), String::from(name_from_cli(rt, "delete")))
        .map_err_trace()
        .map_info_str("Ok")
        .ok();
}

fn edit(rt: &Runtime) {
    edit_entry(rt, name_from_cli(rt, "edit"));
}

fn edit_entry(rt: &Runtime, name: String) -> bool {
    let mut note = match Note::get(rt.store(), name) {
        Ok(Some(note)) => note,
        Ok(None) => {
            warn!("Cannot edit nonexistent Note");
            return false
        },
        Err(e) => {
            trace_error(&e);
            warn!("Cannot edit nonexistent Note");
            return false
        },
    };

    note.edit_content(rt).map_err_trace().map_warn_err_str("Editing failed").is_ok()
}

fn list(rt: &Runtime) {
    use std::cmp::Ordering;

    Note::all_notes(rt.store())
        .map_err_trace_exit(1)
        .map(|iter| {
            let notes = iter.filter_map(|note| note.map_err_trace().ok())
                .sorted_by(|note_a, note_b| {
                    if let (Ok(a), Ok(b)) = (note_a.get_name(), note_b.get_name()) {
                        return a.cmp(&b)
                    } else {
                        return Ordering::Greater;
                    }
                });

            for note in notes.iter() {
                note.get_name()
                    .map(|name| println!("{}", name))
                    .map_err_trace()
                    .ok();
            }
        })
        .ok();
}

<|endoftext|>


<commit_before>use std;
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::fmt;
use std::fmt::{Debug, Formatter};
use std::mem;
use std::ptr;
use std::rc::{Rc, Weak};

/**
 * A trait for all values that can be handled by Operation
 */
pub trait Value: Clone + Debug {}

pub trait EvaluationContext: Clone + Debug {
    type Value: Value + 'static;
}

/**
 * Responds to expressions becoming total
 */
pub trait EvaluationListener<C: EvaluationContext>: Debug {
    fn on_evaluated(&self, partial: &PartialExpression<C>, value: C::Value);
}

/**
 * A list of operands
 */
#[derive(Debug)]
pub enum OperandList<C: EvaluationContext> {
    Total(Vec<C::Value>),
    Partial(Vec<Expression<C>>, usize),
}

impl<C: EvaluationContext> OperandList<C> {
    pub fn new(operands: Vec<Expression<C>>) -> OperandList<C> {
        let num_partial = operands
            .iter()
            .filter(|operand| match **operand {
                Expression::Total(_) => false,
                Expression::Partial(_) => true,
            })
            .count();

        if num_partial == 0 {
            OperandList::Total(
                operands
                    .into_iter()
                    .map(|operand| match operand {
                        Expression::Total(ref value) => value.clone(),
                        Expression::Partial(_) => unreachable!(),
                    })
                    .collect::<Vec<_>>(),
            )
        } else {
            // Set children's indices
            for (index, operand) in operands.iter().enumerate() {
                if let Expression::Partial(ref partial) = *operand {
                    partial.index.set(index);
                }
            }

            OperandList::Partial(operands, num_partial)
        }
    }
}

/**
 * Represents the "meat" of a partially-evaluated expression
 */
pub struct PartialExpression<C: EvaluationContext> {
    operation: Operation<C>,
    context: C,
    operands: RefCell<OperandList<C>>,
    listener: Cell<Option<Weak<EvaluationListener<C>>>>,
    self_weak: Weak<PartialExpression<C>>,
    /**
     * The operand's index in the parent expression
     *
     * Will be set when the PartialExpression is placed in an OperandList
     */
    index: Cell<usize>,
}

impl<C: EvaluationContext + 'static> PartialExpression<C> {
    pub fn new(op: Operation<C>, context: C, operands: OperandList<C>) -> Rc<PartialExpression<C>> {
        let mut exp = Rc::new(PartialExpression {
            operation: op,
            context,
            operands: RefCell::new(operands),
            listener: Cell::new(None),
            self_weak: unsafe { mem::uninitialized() },
            index: Cell::new(0),
        });
        unsafe {
            ptr::write(
                &mut Rc::get_mut(&mut exp).unwrap().self_weak,
                Rc::<PartialExpression<C>>::downgrade(&exp),
            );
        }

        // Make exp listen for changes in its operands.
        {
            let operands = exp.operands.borrow();
            if let OperandList::Partial(ref operands, _) = *operands {
                for operand in operands.iter() {
                    if let Expression::Partial(ref oi) = *operand {
                        oi.listener
                            .set(Some(Rc::<PartialExpression<C>>::downgrade(&exp)));
                    }
                }
            }
        }

        exp
    }

    fn try_evaluate_self(&self) {
        let mut operands = self.operands.borrow_mut();

        let new_operands: Option<Vec<C::Value>> = match *operands {
            OperandList::Total(_) => None,
            OperandList::Partial(ref ops, num_partial) => {
                if num_partial == 0 {
                    Some(
                        ops.iter()
                            .map(|o| match *o {
                                Expression::Total(ref value) => value.clone(),
                                _ => unreachable!(),
                            })
                            .collect::<Vec<_>>(),
                    )
                } else {
                    None
                }
            }
        };

        if let Some(total_ops) = new_operands {
            *operands = OperandList::Total(total_ops);
        }

        if let OperandList::Total(ref total_ops) = *operands {
            let eval_result = self.operation.evaluate(&self.context, total_ops);
            match eval_result {
                EvaluationResult::Total(value) => self.on_self_evaluated(value),
                EvaluationResult::Pending => {
                    self.operation
                        .register(&self.context, &self.self_weak, total_ops);
                }
            }
        }
    }

    fn on_self_evaluated(&self, value: C::Value) {
        // We know that there won't be a mutable borrow of the listener here
        // because no one else will be setting it.
        let maybe_listener = unsafe { &*self.listener.as_ptr() };
        if let Some(ref weak_listener) = *maybe_listener {
            println!("listener"); // DEBUG
            if let Some(ref listener) = weak_listener.upgrade() {
                listener.on_evaluated(self, value);
            }
        }
    }

    fn on_operand_evaluated(&self, operand: &PartialExpression<C>, value: C::Value) {
        println!("Operand {} evaluated", operand.index.get()); // DEBUG
        {
            let mut operand_list = self.operands.borrow_mut();

            // Update the operand list.
            if let OperandList::Partial(ref mut operands, ref mut num_partial) = *operand_list {
                if let Expression::Partial(_) = operands[operand.index.get()] {
                    operands[operand.index.get()] = Expression::Total(value);
                    *num_partial -= 1;
                }
            }
        }

        self.try_evaluate_self();
    }
}

// Since we can't derive Debug for Cell<Option<Weak<...>>>, we have to implement it manually.
impl<C: EvaluationContext> Debug for PartialExpression<C> {
    fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
        // TODO: implement properly.
        write!(
            f,
            "PartialExpression({:?}, {:?})",
            self.operation,
            self.operands.borrow()
        )
    }
}

impl<C: EvaluationContext + 'static> EvaluationListener<C> for PartialExpression<C> {
    fn on_evaluated(&self, partial: &PartialExpression<C>, value: C::Value) {
        // Use hairy pointer comparisons to see how the recently evaluated partial is
        // related to self.
        let self_ptr = self as *const PartialExpression<C> as *const ();
        if partial as *const PartialExpression<C> as *const () == self_ptr {
            self.on_self_evaluated(value);
        } else {
            // This should be effectively safe because we don't mutate the Cell
            // while the pointer is still in use.
            let listener_ptr = unsafe {
                (*partial.listener.as_ptr())
                    .as_ref()
                    .and_then({ |p| p.upgrade() })
                    .map({ |p| Rc::into_raw(p) as *const () })
                    .unwrap_or(ptr::null())
            };
            if listener_ptr == self_ptr {
                self.on_operand_evaluated(partial, value);
            }
        }
    }
}

/**
 * An expression is either a value or an operation applied to a list of expressions.
 */
#[derive(Debug)]
pub enum Expression<C: EvaluationContext> {
    /// An expression that has been totally evaluated
    Total(C::Value),
    /// An expression which has not been totally evaluated
    Partial(Rc<PartialExpression<C>>),
}

impl<C: EvaluationContext + 'static> Expression<C> {
    /**
     * Builds an Expression from an Operation and a list of Operands
     */
    pub fn from_op(op: Operation<C>, context: &C, operands: Vec<Expression<C>>) -> Expression<C> {
        let op_list = OperandList::new(operands);

        if let OperandList::Total(operand_values) = op_list {
            let eval_result = op.evaluate(&context, &operand_values);

            return match eval_result {
                EvaluationResult::Total(val) => Expression::Total(val),
                EvaluationResult::Pending => {
                    let partial = PartialExpression::new(
                        op,
                        context.clone(),
                        OperandList::Total(operand_values.clone()),
                    );
                    // TODO: reorganize somehow so we don't need the clone above.
                    let listener = Rc::downgrade(&partial);
                    op.register(context, &listener, &operand_values);
                    Expression::Partial(partial)
                }
            };
        }

        Expression::Partial(PartialExpression::new(op, context.clone(), op_list))
    }

    /**
     * Builds a total Expression from a value
     */
    pub fn from_value(value: C::Value) -> Expression<C> {
        Expression::Total(value)
    }
}

pub enum EvaluationResult<V: Value + 'static> {
    Total(V),
    Pending,
}

/**
 * A function that uses an EvaluationContext, an array of operands, and an
 * evaluation listener to handle the evaluation of an Operation
 */
pub type Evaluator<C> =
    fn(&C, &[<C as EvaluationContext>::Value]) -> EvaluationResult<<C as EvaluationContext>::Value>;

pub type Registrar<C> = fn(&C, &Weak<PartialExpression<C>>, &[<C as EvaluationContext>::Value]);

// TODO: handle "lazy"/quoted ops?
#[derive(Clone)]
pub struct Operation<C: EvaluationContext> {
    name: &'static str,
    evaluator: Evaluator<C>,
    registrar: Registrar<C>,
}

impl<C: EvaluationContext> Operation<C> {
    pub const fn new(
        name: &'static str,
        evaluator: Evaluator<C>,
        registrar: Registrar<C>,
    ) -> Operation<C> {
        Operation {
            name,
            evaluator,
            registrar,
        }
    }

    pub fn evaluate(&self, context: &C, operands: &[C::Value]) -> EvaluationResult<C::Value> {
        (self.evaluator)(context, operands)
    }

    pub fn register(
        &self,
        context: &C,
        listener: &Weak<PartialExpression<C>>,
        operands: &[C::Value],
    ) {
        (self.registrar)(context, listener, operands)
    }
}

// For some reason I don't understand, #[derive(Copy)] seems to fail silently, so we have
// to mark this type manually.
impl<C: EvaluationContext> Copy for Operation<C> {}

impl<C: EvaluationContext> Debug for Operation<C> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Operation {{ name: {} }}", self.name)
    }
}

pub struct OperationGroup<C: EvaluationContext> {
    map: HashMap<Box<str>, Operation<C>>,
}

impl<C: EvaluationContext> OperationGroup<C> {
    pub const fn new(map: HashMap<Box<str>, Operation<C>>) -> OperationGroup<C> {
        OperationGroup { map }
    }

    pub fn get(&self, name: &str) -> Option<&Operation<C>> {
        self.map.get(name)
    }
}
<commit_msg>add Expression::try_evaluate() and do some minor cleanup<commit_after>use std;
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::fmt;
use std::fmt::{Debug, Formatter};
use std::ptr;
use std::rc::{Rc, Weak};

/**
 * A trait for all values that can be handled by Operation
 */
pub trait Value: Clone + Debug {}

pub trait EvaluationContext: Clone + Debug {
    type Value: Value + 'static;
}

/**
 * Responds to expressions becoming total
 */
pub trait EvaluationListener<C: EvaluationContext>: Debug {
    fn on_evaluated(&self, partial: &PartialExpression<C>, value: C::Value);
}

/**
 * A list of operands
 */
#[derive(Debug)]
pub enum OperandList<C: EvaluationContext> {
    Total(Vec<C::Value>),
    Partial(Vec<Expression<C>>, usize),
}

impl<C: EvaluationContext> OperandList<C> {
    pub fn new(operands: Vec<Expression<C>>) -> OperandList<C> {
        let num_partial = operands
            .iter()
            .filter(|operand| match **operand {
                Expression::Total(_) => false,
                Expression::Partial(_) => true,
            })
            .count();

        if num_partial == 0 {
            OperandList::Total(
                operands
                    .into_iter()
                    .map(|operand| match operand {
                        Expression::Total(ref value) => value.clone(),
                        Expression::Partial(_) => unreachable!(),
                    })
                    .collect::<Vec<_>>(),
            )
        } else {
            // Set children's indices
            for (index, operand) in operands.iter().enumerate() {
                if let Expression::Partial(ref partial) = *operand {
                    partial.index.set(index);
                }
            }

            OperandList::Partial(operands, num_partial)
        }
    }
}

/**
 * Represents the "meat" of a partially-evaluated expression
 */
pub struct PartialExpression<C: EvaluationContext> {
    operation: Operation<C>,
    context: C,
    operands: RefCell<OperandList<C>>,
    listener: Cell<Option<Weak<EvaluationListener<C>>>>,
    /*
     * Keeps a reference to this PartialExpression so we can call operation.register()
     * if necessary
     *
     * Will be set to None once registration has occurred so we don't double-register.
     */
    operation_listener: Cell<Option<Weak<PartialExpression<C>>>>,
    /*
     * The operand's index in the parent expression
     *
     * Will be set when the PartialExpression is placed in an OperandList
     */
    index: Cell<usize>,
}

impl<C: EvaluationContext + 'static> PartialExpression<C> {
    pub fn new(op: Operation<C>, context: C, operands: OperandList<C>) -> Rc<PartialExpression<C>> {
        let exp = Rc::new(PartialExpression {
            operation: op,
            context,
            operands: RefCell::new(operands),
            listener: Cell::new(None),
            operation_listener: Cell::new(None),
            index: Cell::new(0),
        });
        exp.operation_listener.set(Some(Rc::downgrade(&exp)));

        // Make exp listen for changes in its operands.
        {
            let operands = exp.operands.borrow();
            if let OperandList::Partial(ref operands, _) = *operands {
                for operand in operands.iter() {
                    if let Expression::Partial(ref oi) = *operand {
                        oi.listener
                            .set(Some(Rc::<PartialExpression<C>>::downgrade(&exp)));
                    }
                }
            }
        }

        exp
    }

    fn try_evaluate_self(&self) -> EvaluationResult<C::Value> {
        let mut operands = self.operands.borrow_mut();

        let new_operands: Option<Vec<C::Value>> = match *operands {
            OperandList::Total(_) => None,
            OperandList::Partial(ref ops, num_partial) => {
                if num_partial == 0 {
                    Some(
                        ops.iter()
                            .map(|o| match *o {
                                Expression::Total(ref value) => value.clone(),
                                _ => unreachable!(),
                            })
                            .collect::<Vec<_>>(),
                    )
                } else {
                    None
                }
            }
        };

        if let Some(total_ops) = new_operands {
            *operands = OperandList::Total(total_ops);
        }

        if let OperandList::Total(ref total_ops) = *operands {
            let eval_result = self.operation.evaluate(&self.context, total_ops);
            if let EvaluationResult::Pending = eval_result {
                if let Some(op_listener) = self.operation_listener.replace(None) {
                    self.operation
                        .register(&self.context, &op_listener, total_ops);
                }
            }
            eval_result
        } else {
            EvaluationResult::Pending
        }
    }

    fn on_self_evaluated(&self, value: C::Value) {
        // We know that no one else will be mutating self.EvaluationListener until we
        // call upgrade(), at which point we don't care whether mutation happens.
        let maybe_listener = unsafe { &*self.listener.as_ptr() };
        if let Some(ref weak_listener) = *maybe_listener {
            if let Some(ref listener) = weak_listener.upgrade() {
                listener.on_evaluated(self, value);
            }
        }
    }

    fn on_operand_evaluated(&self, operand: &PartialExpression<C>, value: C::Value) {
        {
            let mut operand_list = self.operands.borrow_mut();

            // Update the operand list.
            if let OperandList::Partial(ref mut operands, ref mut num_partial) = *operand_list {
                if let Expression::Partial(_) = operands[operand.index.get()] {
                    operands[operand.index.get()] = Expression::Total(value);
                    *num_partial -= 1;
                }
            }
        }

        if let EvaluationResult::Total(value) = self.try_evaluate_self() {
            self.on_self_evaluated(value)
        }
    }
}

// Since we can't derive Debug for Cell<Option<Weak<...>>>, we have to implement it manually.
impl<C: EvaluationContext> Debug for PartialExpression<C> {
    fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
        // TODO: implement properly.
        write!(
            f,
            "PartialExpression({:?}, {:?})",
            self.operation,
            self.operands.borrow()
        )
    }
}

impl<C: EvaluationContext + 'static> EvaluationListener<C> for PartialExpression<C> {
    fn on_evaluated(&self, partial: &PartialExpression<C>, value: C::Value) {
        // Use hairy pointer comparisons to see how the recently evaluated partial is
        // related to self.
        let self_ptr = self as *const PartialExpression<C> as *const ();
        if partial as *const PartialExpression<C> as *const () == self_ptr {
            self.on_self_evaluated(value);
        } else {
            // This should be effectively safe because we don't mutate the Cell
            // while the pointer is still in use.
            let listener_ptr = unsafe {
                (*partial.listener.as_ptr())
                    .as_ref()
                    .and_then({ |p| p.upgrade() })
                    .map({ |p| Rc::into_raw(p) as *const () })
                    .unwrap_or(ptr::null())
            };
            if listener_ptr == self_ptr {
                self.on_operand_evaluated(partial, value);
            }
        }
    }
}

/**
 * An expression is either a value or an operation applied to a list of expressions.
 */
#[derive(Debug)]
pub enum Expression<C: EvaluationContext> {
    /// An expression that has been totally evaluated
    Total(C::Value),
    /// An expression which has not been totally evaluated
    Partial(Rc<PartialExpression<C>>),
}

impl<C: EvaluationContext + 'static> Expression<C> {
    /**
     * Builds an Expression from an Operation and a list of Operands
     */
    pub fn from_op(op: Operation<C>, context: &C, operands: Vec<Expression<C>>) -> Expression<C> {
        let op_list = OperandList::new(operands);

        if let OperandList::Total(operand_values) = op_list {
            let eval_result = op.evaluate(&context, &operand_values);

            return match eval_result {
                EvaluationResult::Total(val) => Expression::Total(val),
                EvaluationResult::Pending => {
                    let partial = PartialExpression::new(
                        op,
                        context.clone(),
                        OperandList::Total(operand_values.clone()),
                    );
                    // TODO: reorganize somehow so we don't need the clone above.
                    let listener = Rc::downgrade(&partial);
                    op.register(context, &listener, &operand_values);
                    Expression::Partial(partial)
                }
            };
        }

        Expression::Partial(PartialExpression::new(op, context.clone(), op_list))
    }

    /**
     * Builds a total Expression from a value
     */
    pub fn from_value(value: C::Value) -> Expression<C> {
        Expression::Total(value)
    }

    /**
     * Attempts to evaluate the expression in-place
     */
    pub fn try_evaluate(&mut self) {
        let mut new_val: EvaluationResult<C::Value> = EvaluationResult::Pending;
        if let Expression::Partial(ref part) = self {
            new_val = part.try_evaluate_self();
        }

        if let EvaluationResult::Total(value) = new_val {
            *self = Expression::Total(value);
        }
    }
}

pub enum EvaluationResult<V: Value + 'static> {
    Total(V),
    Pending,
}

/**
 * A function that uses an EvaluationContext, an array of operands, and an
 * evaluation listener to handle the evaluation of an Operation
 */
pub type Evaluator<C> =
    fn(&C, &[<C as EvaluationContext>::Value]) -> EvaluationResult<<C as EvaluationContext>::Value>;

pub type Registrar<C> = fn(&C, &Weak<PartialExpression<C>>, &[<C as EvaluationContext>::Value]);

// TODO: handle "lazy"/quoted ops?
#[derive(Clone)]
pub struct Operation<C: EvaluationContext> {
    name: &'static str,
    evaluator: Evaluator<C>,
    registrar: Registrar<C>,
}

impl<C: EvaluationContext> Operation<C> {
    pub const fn new(
        name: &'static str,
        evaluator: Evaluator<C>,
        registrar: Registrar<C>,
    ) -> Operation<C> {
        Operation {
            name,
            evaluator,
            registrar,
        }
    }

    pub fn evaluate(&self, context: &C, operands: &[C::Value]) -> EvaluationResult<C::Value> {
        (self.evaluator)(context, operands)
    }

    pub fn register(
        &self,
        context: &C,
        listener: &Weak<PartialExpression<C>>,
        operands: &[C::Value],
    ) {
        (self.registrar)(context, listener, operands)
    }
}

// For some reason I don't understand, #[derive(Copy)] seems to fail silently, so we have
// to mark this type manually.
impl<C: EvaluationContext> Copy for Operation<C> {}

impl<C: EvaluationContext> Debug for Operation<C> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Operation {{ name: {} }}", self.name)
    }
}

pub struct OperationGroup<C: EvaluationContext> {
    map: HashMap<Box<str>, Operation<C>>,
}

impl<C: EvaluationContext> OperationGroup<C> {
    pub const fn new(map: HashMap<Box<str>, Operation<C>>) -> OperationGroup<C> {
        OperationGroup { map }
    }

    pub fn get(&self, name: &str) -> Option<&Operation<C>> {
        self.map.get(name)
    }
}
<|endoftext|>


<commit_before><commit_msg>Works! But slow<commit_after><|endoftext|>


<commit_before><commit_msg>Add unit test `build_url_doc()` in `gitlab` module<commit_after><|endoftext|>


<commit_before><commit_msg>documentation<commit_after><|endoftext|>


<commit_before>macro_rules! impl_shuffle_lane {
    { $name:ident, $fn:ident, $n:literal } => {
        impl $name<$n> {
            /// A const SIMD shuffle that takes 2 SIMD vectors and produces another vector, using
            /// the indices in the const parameter. The first or "self" vector will have its lanes
            /// indexed from 0, and the second vector will have its first lane indexed at $n.
            /// Indices must be in-bounds of either vector at compile time.
            ///
            /// Some SIMD shuffle instructions can be quite slow, so avoiding them by loading data
            /// into the desired patterns in advance is preferred, but shuffles are still faster
            /// than storing and reloading from memory.
            #[inline]
            pub fn shuffle<const IDX: [u32; $n]>(self, second: Self) -> Self {
                unsafe { crate::intrinsics::$fn(self, second, IDX) }
            }

            /// Reverse the order of the lanes in the vector.
            #[inline]
            pub fn reverse(self) -> Self {
                const fn idx() -> [u32; $n] {
                    let mut idx = [0u32; $n];
                    let mut i = 0;
                    while i < $n {
                        idx[i] = ($n - i - 1) as u32;
                        i += 1;
                    }
                    idx
                }
                self.shuffle::<{ idx() }>(self)
            }

            /// Interleave two vectors.
            ///
            /// The even lanes of the first result contain the lower half of `self`, and the odd
            /// lanes contain the lower half of `other`.
            ///
            /// The even lanes of the second result contain the upper half of `self`, and the odd
            /// lanes contain the upper half of `other`.
            #[inline]
            pub fn interleave(self, other: Self) -> (Self, Self) {
                const fn lo() -> [u32; $n] {
                    let mut idx = [0u32; $n];
                    let mut i = 0;
                    while i < $n {
                        let offset = i / 2;
                        idx[i] = if i % 2 == 0 {
                            offset
                        } else {
                            $n + offset
                        } as u32;
                        i += 1;
                    }
                    idx
                }
                const fn hi() -> [u32; $n] {
                    let mut idx = [0u32; $n];
                    let mut i = 0;
                    while i < $n {
                        let offset = ($n + i) / 2;
                        idx[i] = if i % 2 == 0 {
                            offset
                        } else {
                            $n + offset
                        } as u32;
                        i += 1;
                    }
                    idx
                }
                (self.shuffle::<{ lo() }>(other), self.shuffle::<{ hi() }>(other))
            }

            /// Deinterleave two vectors.
            ///
            /// The first result contains the even lanes of `self` and then `other`, concatenated.
            ///
            /// The second result contains the odd lanes of `self` and then `other`, concatenated.
            #[inline]
            pub fn deinterleave(self, other: Self) -> (Self, Self) {
                const fn even() -> [u32; $n] {
                    let mut idx = [0u32; $n];
                    let mut i = 0;
                    while i < $n {
                        idx[i] = 2 * i as u32;
                        i += 1;
                    }
                    idx
                }
                const fn odd() -> [u32; $n] {
                    let mut idx = [0u32; $n];
                    let mut i = 0;
                    while i < $n {
                        idx[i] = 1 + 2 * i as u32;
                        i += 1;
                    }
                    idx
                }
                (self.shuffle::<{ even() }>(other), self.shuffle::<{ odd() }>(other))
            }
        }
    }
}

macro_rules! impl_shuffle_2pow_lanes {
    { $name:ident } => {
        impl_shuffle_lane!{ $name, simd_shuffle2, 2 }
        impl_shuffle_lane!{ $name, simd_shuffle4, 4 }
        impl_shuffle_lane!{ $name, simd_shuffle8, 8 }
        impl_shuffle_lane!{ $name, simd_shuffle16, 16 }
        impl_shuffle_lane!{ $name, simd_shuffle32, 32 }
    }
}
<commit_msg>Attempt to clarify interleave/deinterleave<commit_after>macro_rules! impl_shuffle_lane {
    { $name:ident, $fn:ident, $n:literal } => {
        impl $name<$n> {
            /// A const SIMD shuffle that takes 2 SIMD vectors and produces another vector, using
            /// the indices in the const parameter. The first or "self" vector will have its lanes
            /// indexed from 0, and the second vector will have its first lane indexed at $n.
            /// Indices must be in-bounds of either vector at compile time.
            ///
            /// Some SIMD shuffle instructions can be quite slow, so avoiding them by loading data
            /// into the desired patterns in advance is preferred, but shuffles are still faster
            /// than storing and reloading from memory.
            #[inline]
            pub fn shuffle<const IDX: [u32; $n]>(self, second: Self) -> Self {
                unsafe { crate::intrinsics::$fn(self, second, IDX) }
            }

            /// Reverse the order of the lanes in the vector.
            #[inline]
            pub fn reverse(self) -> Self {
                const fn idx() -> [u32; $n] {
                    let mut idx = [0u32; $n];
                    let mut i = 0;
                    while i < $n {
                        idx[i] = ($n - i - 1) as u32;
                        i += 1;
                    }
                    idx
                }
                self.shuffle::<{ idx() }>(self)
            }

            /// Interleave two vectors.
            ///
            /// Produces two vectors with lanes taken alternately from `self` and `other`.
            ///
            /// The first result contains the first `LANES / 2` lanes from `self` and `other`,
            /// alternating, starting with the first lane of `self`.
            ///
            /// The second result contains the last `LANES / 2` lanes from `self` and `other`,
            /// alternating, starting with the lane `LANES / 2` from the start of `self`.
            ///
            /// This particular permutation is efficient on many architectures.
            ///
            /// ```
            /// # use core_simd::SimdU32;
            /// let a = SimdU32::from_array([0, 1, 2, 3]);
            /// let b = SimdU32::from_array([4, 5, 6, 7]);
            /// let (x, y) = a.interleave(b);
            /// assert_eq!(x.to_array(), [0, 4, 1, 5]);
            /// assert_eq!(y.to_array(), [2, 6, 3, 7]);
            /// ```
            #[inline]
            pub fn interleave(self, other: Self) -> (Self, Self) {
                const fn lo() -> [u32; $n] {
                    let mut idx = [0u32; $n];
                    let mut i = 0;
                    while i < $n {
                        let offset = i / 2;
                        idx[i] = if i % 2 == 0 {
                            offset
                        } else {
                            $n + offset
                        } as u32;
                        i += 1;
                    }
                    idx
                }
                const fn hi() -> [u32; $n] {
                    let mut idx = [0u32; $n];
                    let mut i = 0;
                    while i < $n {
                        let offset = ($n + i) / 2;
                        idx[i] = if i % 2 == 0 {
                            offset
                        } else {
                            $n + offset
                        } as u32;
                        i += 1;
                    }
                    idx
                }
                (self.shuffle::<{ lo() }>(other), self.shuffle::<{ hi() }>(other))
            }

            /// Deinterleave two vectors.
            ///
            /// The first result takes every other lane of `self` and then `other`, starting with
            /// the first lane.
            ///
            /// The second result takes every other lane of `self` and then `other`, starting with
            /// the second lane.
            ///
            /// This particular permutation is efficient on many architectures.
            ///
            /// ```
            /// # use core_simd::SimdU32;
            /// let a = SimdU32::from_array([0, 4, 1, 5]);
            /// let b = SimdU32::from_array([2, 6, 3, 7]);
            /// let (x, y) = a.deinterleave(b);
            /// assert_eq!(x.to_array(), [0, 1, 2, 3]);
            /// assert_eq!(y.to_array(), [4, 5, 6, 7]);
            /// ```
            #[inline]
            pub fn deinterleave(self, other: Self) -> (Self, Self) {
                const fn even() -> [u32; $n] {
                    let mut idx = [0u32; $n];
                    let mut i = 0;
                    while i < $n {
                        idx[i] = 2 * i as u32;
                        i += 1;
                    }
                    idx
                }
                const fn odd() -> [u32; $n] {
                    let mut idx = [0u32; $n];
                    let mut i = 0;
                    while i < $n {
                        idx[i] = 1 + 2 * i as u32;
                        i += 1;
                    }
                    idx
                }
                (self.shuffle::<{ even() }>(other), self.shuffle::<{ odd() }>(other))
            }
        }
    }
}

macro_rules! impl_shuffle_2pow_lanes {
    { $name:ident } => {
        impl_shuffle_lane!{ $name, simd_shuffle2, 2 }
        impl_shuffle_lane!{ $name, simd_shuffle4, 4 }
        impl_shuffle_lane!{ $name, simd_shuffle8, 8 }
        impl_shuffle_lane!{ $name, simd_shuffle16, 16 }
        impl_shuffle_lane!{ $name, simd_shuffle32, 32 }
    }
}
<|endoftext|>


<commit_before><commit_msg>Refactor the "search_post_value_indices" function<commit_after><|endoftext|>


<commit_before><commit_msg>When resizing, reset *both* components of screen.offsets<commit_after><|endoftext|>


<commit_before><commit_msg>Test functor laws of streams<commit_after><|endoftext|>


<commit_before><commit_msg>header_path is always path<commit_after><|endoftext|>


<commit_before>use std::rand::random;
use std::os;
use std::io::File;

fn main() {
    let args: ~[~str] = os::args();
    if args.len() != 3 {
        println!("Usage: {:s} <splitFile1> <splitFile2>", args[0]);
    } else {
        let f1name = args[1];
        let f2name = args[2];
        let path1 = Path::new(f1name.clone());
        let path2 = Path::new(f2name.clone());
        let msg_file1 = File::open(&path1);
        let msg_file2 = File::open(&path2);

        match (msg_file1, msg_file2) {
            Some(mut msg1, mut msg2) => {
                let msg1_bytes: ~[u8] = msg1.read_to_end();
                let msg2_bytes: ~[u8] = msg2.read_to_end();
                let output_file
                       = File::create(&Path::new("originalMsg"));

                match (output_file) {
                    (Some(output)) => {
                        split(msg1_bytes, msg2_bytes, output_file);
                        } ,
                    (_, _) => fail!("Error opening output files!"),
                }
            } ,
            None => fail!("Error opening message file: {:s}", fname)
        }
    }
}

fn xor(a: &[u8], b: &[u8]) -> ~[u8] {
    let mut ret = ~[];
    for i in range(0, a.len()) {
	ret.push(a[i] ^ b[i]);
    }
    ret
}

fn split(msg1_bytes: &[u8], mut msg2_bytes: &[u8], mut output_file: File) {
    let mut original_bytes: ~[u8] = xor(msg1_bytes, msg2_bytes);
    output_file.write(original_bytes);
}
<commit_msg>Fixed some bugs. Now it works<commit_after>use std::os;
use std::io::File;
use std::str;

fn main() {
    let args: ~[~str] = os::args();
    let argsClone = args.clone();
    if args.len() != 3 {
        println!("Usage: {:s} <splitFile1> <splitFile2>", args[0]);
    } else {
        let f1name = args[1];
        let f2name = argsClone[2];
        let path1 = Path::new(f1name.clone());
        let path2 = Path::new(f2name.clone());
        let msg_file1 = File::open(&path1);
        let msg_file2 = File::open(&path2);

        match (msg_file1, msg_file2) {
            (Some(mut msg1), Some(mut msg2)) => {
                let msg1_bytes: ~[u8] = msg1.read_to_end();
                let msg2_bytes: ~[u8] = msg2.read_to_end();
                let output_file
                       = File::create(&Path::new("originalMsg"));

                match (output_file) {
                    Some(output) => {
                        split(msg1_bytes, msg2_bytes, output);
                        } ,
                    _ => fail!("Error opening output files!"),
                }
            } ,
            (_, _) => fail!("Error opening split files: {:s}, {:s}", f1name, f2name)
        }
    }
}

fn xor(a: &[u8], b: &[u8]) -> ~[u8] {
    let mut ret = ~[];
    for i in range(0, a.len()) {
	ret.push(a[i] ^ b[i]);
    }
    ret
}

fn split(msg1_bytes: &[u8], msg2_bytes: &[u8], mut output_file: File) {
    let original_bytes: ~[u8] = xor(msg1_bytes, msg2_bytes);
    output_file.write(original_bytes);
    print(str::from_utf8(original_bytes));
}
<|endoftext|>