<commit_before>//! Simple daemon
//!
//! This daemon will start web server, track new packages and build them

use std::{env, thread};
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::time::Duration;
use std::path::PathBuf;
use time;
use crate::{
    docbuilder::RustwideBuilder,
    DocBuilderOptions,
    DocBuilder,
    utils::{update_release_activity, github_updater, pubsubhubbub},
    db::{connect_db, update_search_index},
};

#[cfg(not(target_os = "windows"))]
use ::{
    libc::fork,
    std::process::exit,
    std::fs::File,
    std::io::Write
};

pub fn start_daemon(background: bool) {
    // first check required environment variables
    for v in ["CRATESFYI_PREFIX",
              "CRATESFYI_PREFIX",
              "CRATESFYI_GITHUB_USERNAME",
              "CRATESFYI_GITHUB_ACCESSTOKEN"]
        .iter() {
        env::var(v).expect(&format!("Environment variable {} not found", v));
    }

    let dbopts = opts();

    // check paths once
    dbopts.check_paths().unwrap();

    if background {
        #[cfg(target_os = "windows")]
        {
            panic!("running in background not supported on windows");
        }
        #[cfg(not(target_os = "windows"))]
        {
            // fork the process
            let pid = unsafe { fork() };
            if pid > 0 {
                let mut file = File::create(dbopts.prefix.join("cratesfyi.pid"))
                    .expect("Failed to create pid file");
                writeln!(&mut file, "{}", pid).expect("Failed to write pid");

                info!("cratesfyi {} daemon started on: {}", crate::BUILD_VERSION, pid);
                exit(0);
            }
        }
    }

    // check new crates every minute
    thread::Builder::new().name("crates.io reader".to_string()).spawn(move || {
        // space this out to prevent it from clashing against the queue-builder thread on launch
        thread::sleep(Duration::from_secs(30));
        loop {
            let opts = opts();
            let mut doc_builder = DocBuilder::new(opts);

            if doc_builder.is_locked() {
                debug!("Lock file exists, skipping checking new crates");
            } else {
                debug!("Checking new crates");
                match doc_builder.get_new_crates() {
                    Ok(n) => debug!("{} crates added to queue", n),
                    Err(e) => error!("Failed to get new crates: {}", e),
                }
            }

            thread::sleep(Duration::from_secs(60));
        }
    }).unwrap();

    // build new crates every minute
    thread::Builder::new().name("build queue reader".to_string()).spawn(move || {
        let opts = opts();
        let mut doc_builder = DocBuilder::new(opts);

        /// Represents the current state of the builder thread.
        enum BuilderState {
            /// The builder thread has just started, and hasn't built any crates yet.
            Fresh,
            /// The builder has just seen an empty build queue.
            EmptyQueue,
            /// The builder has just seen the lock file.
            Locked,
            /// The builder has just finished building a crate. The enclosed count is the number of
            /// crates built since the caches have been refreshed.
            QueueInProgress(usize),
        }

        let mut builder = RustwideBuilder::init().unwrap();

        let mut status = BuilderState::Fresh;

        loop {
            if !status.is_in_progress() {
                thread::sleep(Duration::from_secs(60));
            }

            // check lock file
            if doc_builder.is_locked() {
                warn!("Lock file exits, skipping building new crates");
                status = BuilderState::Locked;
                continue;
            }

            if status.count() >= 10 {
                // periodically, we need to flush our caches and ping the hubs
                debug!("10 builds in a row; flushing caches");
                status = BuilderState::QueueInProgress(0);

                match pubsubhubbub::ping_hubs() {
                    Err(e) => error!("Failed to ping hub: {}", e),
                    Ok(n) => debug!("Succesfully pinged {} hubs", n)
                }

                if let Err(e) = doc_builder.load_cache() {
                    error!("Failed to load cache: {}", e);
                }

                if let Err(e) = doc_builder.save_cache() {
                    error!("Failed to save cache: {}", e);
                }
            }

            // Only build crates if there are any to build
            debug!("Checking build queue");
            match doc_builder.get_queue_count() {
                Err(e) => {
                    error!("Failed to read the number of crates in the queue: {}", e);
                    continue;
                }
                Ok(0) => {
                    if status.count() > 0 {
                        // ping the hubs before continuing
                        match pubsubhubbub::ping_hubs() {
                            Err(e) => error!("Failed to ping hub: {}", e),
                            Ok(n) => debug!("Succesfully pinged {} hubs", n)
                        }

                        if let Err(e) = doc_builder.save_cache() {
                            error!("Failed to save cache: {}", e);
                        }
                    }
                    debug!("Queue is empty, going back to sleep");
                    status = BuilderState::EmptyQueue;
                    continue;
                }
                Ok(queue_count) => {
                    info!("Starting build with {} crates in queue (currently on a {} crate streak)",
                          queue_count, status.count());
                }
            }

            // if we're starting a new batch, reload our caches and sources
            if !status.is_in_progress() {
                if let Err(e) = doc_builder.load_cache() {
                    error!("Failed to load cache: {}", e);
                    continue;
                }
            }

            // Run build_packages_queue under `catch_unwind` to catch panics
            // This only panicked twice in the last 6 months but its just a better
            // idea to do this.
            let res = catch_unwind(AssertUnwindSafe(|| {
                match doc_builder.build_next_queue_package(&mut builder) {
                    Err(e) => error!("Failed to build crate from queue: {}", e),
                    Ok(crate_built) => if crate_built {
                        status.increment();
                    }

                }
            }));

            if let Err(e) = res {
                error!("GRAVE ERROR Building new crates panicked: {:?}", e);
            }
        }

        impl BuilderState {
            fn count(&self) -> usize {
                match *self {
                    BuilderState::QueueInProgress(n) => n,
                    _ => 0,
                }
            }

            fn is_in_progress(&self) -> bool {
                match *self {
                    BuilderState::QueueInProgress(_) => true,
                    _ => false,
                }
            }

            fn increment(&mut self) {
                *self = BuilderState::QueueInProgress(self.count() + 1);
            }
        }
    }).unwrap();

    // update release activity everyday at 23:55
    thread::Builder::new().name("release activity updater".to_string()).spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(60));
            let now = time::now();
            if now.tm_hour == 23 && now.tm_min == 55 {
                info!("Updating release activity");
                if let Err(e) = update_release_activity() {
                    error!("Failed to update release activity: {}", e);
                }
            }
        }
    }).unwrap();

    // update search index every 3 hours
    thread::Builder::new().name("search index updater".to_string()).spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(60 * 60 * 3));
            let conn = connect_db().expect("Failed to connect database");
            if let Err(e) = update_search_index(&conn) {
                error!("Failed to update search index: {}", e);
            }
        }
    }).unwrap();

    // update github stats every 6 hours
    thread::Builder::new().name("github stat updater".to_string()).spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(60 * 60 * 6));
            if let Err(e) = github_updater() {
                error!("Failed to update github fields: {}", e);
            }
        }
    }).unwrap();

    // TODO: update ssl certificate every 3 months

    // at least start web server
    info!("Starting web server");
    crate::Server::start(None);
}

fn opts() -> DocBuilderOptions {
    let prefix = PathBuf::from(env::var("CRATESFYI_PREFIX")
        .expect("CRATESFYI_PREFIX environment variable not found"));
    DocBuilderOptions::from_prefix(prefix)
}
<commit_msg>Clean up checking for env variables<commit_after>//! Simple daemon
//!
//! This daemon will start web server, track new packages and build them

use std::{env, thread};
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::time::Duration;
use std::path::PathBuf;
use time;
use crate::{
    docbuilder::RustwideBuilder,
    DocBuilderOptions,
    DocBuilder,
    utils::{update_release_activity, github_updater, pubsubhubbub},
    db::{connect_db, update_search_index},
};

#[cfg(not(target_os = "windows"))]
use ::{
    libc::fork,
    std::process::exit,
    std::fs::File,
    std::io::Write
};

pub fn start_daemon(background: bool) {
    // first check required environment variables
    for v in ["CRATESFYI_PREFIX",
              "CRATESFYI_GITHUB_USERNAME",
              "CRATESFYI_GITHUB_ACCESSTOKEN"]
        .iter() {
        if env::var(v).is_err() {
            panic!("Environment variable {} not found", v);
        }
    }

    let dbopts = opts();

    // check paths once
    dbopts.check_paths().unwrap();

    if background {
        #[cfg(target_os = "windows")]
        {
            panic!("running in background not supported on windows");
        }
        #[cfg(not(target_os = "windows"))]
        {
            // fork the process
            let pid = unsafe { fork() };
            if pid > 0 {
                let mut file = File::create(dbopts.prefix.join("cratesfyi.pid"))
                    .expect("Failed to create pid file");
                writeln!(&mut file, "{}", pid).expect("Failed to write pid");

                info!("cratesfyi {} daemon started on: {}", crate::BUILD_VERSION, pid);
                exit(0);
            }
        }
    }

    // check new crates every minute
    thread::Builder::new().name("crates.io reader".to_string()).spawn(move || {
        // space this out to prevent it from clashing against the queue-builder thread on launch
        thread::sleep(Duration::from_secs(30));
        loop {
            let opts = opts();
            let mut doc_builder = DocBuilder::new(opts);

            if doc_builder.is_locked() {
                debug!("Lock file exists, skipping checking new crates");
            } else {
                debug!("Checking new crates");
                match doc_builder.get_new_crates() {
                    Ok(n) => debug!("{} crates added to queue", n),
                    Err(e) => error!("Failed to get new crates: {}", e),
                }
            }

            thread::sleep(Duration::from_secs(60));
        }
    }).unwrap();

    // build new crates every minute
    thread::Builder::new().name("build queue reader".to_string()).spawn(move || {
        let opts = opts();
        let mut doc_builder = DocBuilder::new(opts);

        /// Represents the current state of the builder thread.
        enum BuilderState {
            /// The builder thread has just started, and hasn't built any crates yet.
            Fresh,
            /// The builder has just seen an empty build queue.
            EmptyQueue,
            /// The builder has just seen the lock file.
            Locked,
            /// The builder has just finished building a crate. The enclosed count is the number of
            /// crates built since the caches have been refreshed.
            QueueInProgress(usize),
        }

        let mut builder = RustwideBuilder::init().unwrap();

        let mut status = BuilderState::Fresh;

        loop {
            if !status.is_in_progress() {
                thread::sleep(Duration::from_secs(60));
            }

            // check lock file
            if doc_builder.is_locked() {
                warn!("Lock file exits, skipping building new crates");
                status = BuilderState::Locked;
                continue;
            }

            if status.count() >= 10 {
                // periodically, we need to flush our caches and ping the hubs
                debug!("10 builds in a row; flushing caches");
                status = BuilderState::QueueInProgress(0);

                match pubsubhubbub::ping_hubs() {
                    Err(e) => error!("Failed to ping hub: {}", e),
                    Ok(n) => debug!("Succesfully pinged {} hubs", n)
                }

                if let Err(e) = doc_builder.load_cache() {
                    error!("Failed to load cache: {}", e);
                }

                if let Err(e) = doc_builder.save_cache() {
                    error!("Failed to save cache: {}", e);
                }
            }

            // Only build crates if there are any to build
            debug!("Checking build queue");
            match doc_builder.get_queue_count() {
                Err(e) => {
                    error!("Failed to read the number of crates in the queue: {}", e);
                    continue;
                }
                Ok(0) => {
                    if status.count() > 0 {
                        // ping the hubs before continuing
                        match pubsubhubbub::ping_hubs() {
                            Err(e) => error!("Failed to ping hub: {}", e),
                            Ok(n) => debug!("Succesfully pinged {} hubs", n)
                        }

                        if let Err(e) = doc_builder.save_cache() {
                            error!("Failed to save cache: {}", e);
                        }
                    }
                    debug!("Queue is empty, going back to sleep");
                    status = BuilderState::EmptyQueue;
                    continue;
                }
                Ok(queue_count) => {
                    info!("Starting build with {} crates in queue (currently on a {} crate streak)",
                          queue_count, status.count());
                }
            }

            // if we're starting a new batch, reload our caches and sources
            if !status.is_in_progress() {
                if let Err(e) = doc_builder.load_cache() {
                    error!("Failed to load cache: {}", e);
                    continue;
                }
            }

            // Run build_packages_queue under `catch_unwind` to catch panics
            // This only panicked twice in the last 6 months but its just a better
            // idea to do this.
            let res = catch_unwind(AssertUnwindSafe(|| {
                match doc_builder.build_next_queue_package(&mut builder) {
                    Err(e) => error!("Failed to build crate from queue: {}", e),
                    Ok(crate_built) => if crate_built {
                        status.increment();
                    }

                }
            }));

            if let Err(e) = res {
                error!("GRAVE ERROR Building new crates panicked: {:?}", e);
            }
        }

        impl BuilderState {
            fn count(&self) -> usize {
                match *self {
                    BuilderState::QueueInProgress(n) => n,
                    _ => 0,
                }
            }

            fn is_in_progress(&self) -> bool {
                match *self {
                    BuilderState::QueueInProgress(_) => true,
                    _ => false,
                }
            }

            fn increment(&mut self) {
                *self = BuilderState::QueueInProgress(self.count() + 1);
            }
        }
    }).unwrap();

    // update release activity everyday at 23:55
    thread::Builder::new().name("release activity updater".to_string()).spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(60));
            let now = time::now();
            if now.tm_hour == 23 && now.tm_min == 55 {
                info!("Updating release activity");
                if let Err(e) = update_release_activity() {
                    error!("Failed to update release activity: {}", e);
                }
            }
        }
    }).unwrap();

    // update search index every 3 hours
    thread::Builder::new().name("search index updater".to_string()).spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(60 * 60 * 3));
            let conn = connect_db().expect("Failed to connect database");
            if let Err(e) = update_search_index(&conn) {
                error!("Failed to update search index: {}", e);
            }
        }
    }).unwrap();

    // update github stats every 6 hours
    thread::Builder::new().name("github stat updater".to_string()).spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(60 * 60 * 6));
            if let Err(e) = github_updater() {
                error!("Failed to update github fields: {}", e);
            }
        }
    }).unwrap();

    // TODO: update ssl certificate every 3 months

    // at least start web server
    info!("Starting web server");
    crate::Server::start(None);
}

fn opts() -> DocBuilderOptions {
    let prefix = PathBuf::from(env::var("CRATESFYI_PREFIX")
        .expect("CRATESFYI_PREFIX environment variable not found"));
    DocBuilderOptions::from_prefix(prefix)
}
<|endoftext|>


<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Michael Gehring <mg@ebfe.org>
//  * (c) kwantam <kwantam@gmail.com>
//  *     * 2015-04-28 ~ created `expand` module to eliminate most allocs during setup
//  * (c) Sergey "Shnatsel" Davidoff <shnatsel@gmail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) allocs bset dflag cflag sflag tflag

#[macro_use]
extern crate uucore;

mod expand;

use bit_set::BitSet;
use clap::{App, Arg};
use fnv::FnvHashMap;
use std::io::{stdin, stdout, BufRead, BufWriter, Write};

use crate::expand::ExpandSet;
use uucore::InvalidEncodingHandling;

static VERSION: &str = env!("CARGO_PKG_VERSION");
static ABOUT: &str = "translate or delete characters";

const BUFFER_LEN: usize = 1024;

mod options {
    pub const COMPLEMENT: &str = "complement";
    pub const DELETE: &str = "delete";
    pub const SQUEEZE: &str = "squeeze-repeats";
    pub const TRUNCATE: &str = "truncate";
    pub const SETS: &str = "sets";
}

trait SymbolTranslator {
    fn translate(&self, c: char, prev_c: char) -> Option<char>;
}

struct DeleteOperation {
    bset: BitSet,
    complement: bool,
}

impl DeleteOperation {
    fn new(set: ExpandSet, complement: bool) -> DeleteOperation {
        DeleteOperation {
            bset: set.map(|c| c as usize).collect(),
            complement,
        }
    }
}

impl SymbolTranslator for DeleteOperation {
    fn translate(&self, c: char, _prev_c: char) -> Option<char> {
        let uc = c as usize;
        if self.complement == self.bset.contains(uc) {
            Some(c)
        } else {
            None
        }
    }
}

struct SqueezeOperation {
    squeeze_set: BitSet,
    complement: bool,
}

impl SqueezeOperation {
    fn new(squeeze_set: ExpandSet, complement: bool) -> SqueezeOperation {
        SqueezeOperation {
            squeeze_set: squeeze_set.map(|c| c as usize).collect(),
            complement,
        }
    }
}

impl SymbolTranslator for SqueezeOperation {
    fn translate(&self, c: char, prev_c: char) -> Option<char> {
        if prev_c == c && self.complement != self.squeeze_set.contains(c as usize) {
            None
        } else {
            Some(c)
        }
    }
}

struct DeleteAndSqueezeOperation {
    delete_set: BitSet,
    squeeze_set: BitSet,
    complement: bool,
}

impl DeleteAndSqueezeOperation {
    fn new(
        delete_set: ExpandSet,
        squeeze_set: ExpandSet,
        complement: bool,
    ) -> DeleteAndSqueezeOperation {
        DeleteAndSqueezeOperation {
            delete_set: delete_set.map(|c| c as usize).collect(),
            squeeze_set: squeeze_set.map(|c| c as usize).collect(),
            complement,
        }
    }
}

impl SymbolTranslator for DeleteAndSqueezeOperation {
    fn translate(&self, c: char, prev_c: char) -> Option<char> {
        if self.complement != self.delete_set.contains(c as usize)
            || prev_c == c && self.squeeze_set.contains(c as usize)
        {
            None
        } else {
            Some(c)
        }
    }
}

struct TranslateOperation {
    translate_map: FnvHashMap<usize, char>,
    complement: bool,
    s2_last: char,
}

impl TranslateOperation {
    fn new(
        set1: ExpandSet,
        set2: &mut ExpandSet,
        truncate: bool,
        complement: bool,
    ) -> TranslateOperation {
        let mut map = FnvHashMap::default();
        let mut s2_prev = '_';
        for i in set1 {
            let s2_next = set2.next();

            if s2_next.is_none() && truncate {
                map.insert(i as usize, i);
            } else {
                s2_prev = s2_next.unwrap_or(s2_prev);
                map.insert(i as usize, s2_prev);
            }
        }
        TranslateOperation {
            translate_map: map,
            complement,
            s2_last: set2.last().unwrap_or(s2_prev),
        }
    }
}

impl SymbolTranslator for TranslateOperation {
    fn translate(&self, c: char, _prev_c: char) -> Option<char> {
        if self.complement {
            Some(if self.translate_map.contains_key(&(c as usize)) {
                c
            } else {
                self.s2_last
            })
        } else {
            Some(*self.translate_map.get(&(c as usize)).unwrap_or(&c))
        }
    }
}

struct TranslateAndSqueezeOperation {
    translate: TranslateOperation,
    squeeze: SqueezeOperation,
}

impl TranslateAndSqueezeOperation {
    fn new(
        set1: ExpandSet,
        set2: &mut ExpandSet,
        set2_: ExpandSet,
        truncate: bool,
        complement: bool,
    ) -> TranslateAndSqueezeOperation {
        TranslateAndSqueezeOperation {
            translate: TranslateOperation::new(set1, set2, truncate),
            squeeze: SqueezeOperation::new(set2_, complement),
        }
    }
}

impl SymbolTranslator for TranslateAndSqueezeOperation {
    fn translate(&self, c: char, prev_c: char) -> Option<char> {
        // `unwrap()` will never panic because `Translate.translate()`
        // always returns `Some`.
        self.squeeze
            .translate(self.translate.translate(c, 0 as char).unwrap(), prev_c)
    }
}

fn translate_input<T: SymbolTranslator>(
    input: &mut dyn BufRead,
    output: &mut dyn Write,
    translator: T,
) {
    let mut buf = String::with_capacity(BUFFER_LEN + 4);
    let mut output_buf = String::with_capacity(BUFFER_LEN + 4);

    while let Ok(length) = input.read_line(&mut buf) {
        let mut prev_c = 0 as char;
        if length == 0 {
            break;
        }
        {
            // isolation to make borrow checker happy
            let filtered = buf.chars().filter_map(|c| {
                let res = translator.translate(c, prev_c);
                // Set `prev_c` to the post-translate character. This
                // allows the squeeze operation to correctly function
                // after the translate operation.
                if res.is_some() {
                    prev_c = res.unwrap();
                }
                res
            });

            output_buf.extend(filtered);
            output.write_all(output_buf.as_bytes()).unwrap();
        }
        buf.clear();
        output_buf.clear();
    }
}

fn get_usage() -> String {
    format!("{} [OPTION]... SET1 [SET2]", executable!())
}

fn get_long_usage() -> String {
    String::from(
        "Translate, squeeze, and/or delete characters from standard input,
writing to standard output.",
    )
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let args = args
        .collect_str(InvalidEncodingHandling::ConvertLossy)
        .accept_any();

    let usage = get_usage();
    let after_help = get_long_usage();

    let matches = App::new(executable!())
        .version(VERSION)
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(&after_help[..])
        .arg(
            Arg::with_name(options::COMPLEMENT)
                // .visible_short_alias('C')  // TODO: requires clap "3.0.0-beta.2"
                .short("c")
                .long(options::COMPLEMENT)
                .help("use the complement of SET1"),
        )
        .arg(
            Arg::with_name("C") // work around for `Arg::visible_short_alias`
                .short("C")
                .help("same as -c"),
        )
        .arg(
            Arg::with_name(options::DELETE)
                .short("d")
                .long(options::DELETE)
                .help("delete characters in SET1, do not translate"),
        )
        .arg(
            Arg::with_name(options::SQUEEZE)
                .long(options::SQUEEZE)
                .short("s")
                .help(
                    "replace each sequence  of  a  repeated  character  that  is
  listed  in the last specified SET, with a single occurrence
  of that character",
                ),
        )
        .arg(
            Arg::with_name(options::TRUNCATE)
                .long(options::TRUNCATE)
                .short("t")
                .help("first truncate SET1 to length of SET2"),
        )
        .arg(Arg::with_name(options::SETS).multiple(true))
        .get_matches_from(args);

    let delete_flag = matches.is_present(options::DELETE);
    let complement_flag = matches.is_present(options::COMPLEMENT) || matches.is_present("C");
    let squeeze_flag = matches.is_present(options::SQUEEZE);
    let truncate_flag = matches.is_present(options::TRUNCATE);

    let sets: Vec<String> = match matches.values_of(options::SETS) {
        Some(v) => v.map(|v| v.to_string()).collect(),
        None => vec![],
    };

    if sets.is_empty() {
        show_error!(
            "missing operand\nTry `{} --help` for more information.",
            executable!()
        );
        return 1;
    }

    if !(delete_flag || squeeze_flag) && sets.len() < 2 {
        show_error!(
            "missing operand after ‘{}’\nTry `{} --help` for more information.",
            sets[0],
            executable!()
        );
        return 1;
    }

    let stdin = stdin();
    let mut locked_stdin = stdin.lock();
    let stdout = stdout();
    let locked_stdout = stdout.lock();
    let mut buffered_stdout = BufWriter::new(locked_stdout);

    let set1 = ExpandSet::new(sets[0].as_ref());
    if delete_flag {
        if squeeze_flag {
            let set2 = ExpandSet::new(sets[1].as_ref());
            let op = DeleteAndSqueezeOperation::new(set1, set2, complement_flag);
            translate_input(&mut locked_stdin, &mut buffered_stdout, op);
        } else {
            let op = DeleteOperation::new(set1, complement_flag);
            translate_input(&mut locked_stdin, &mut buffered_stdout, op);
        }
    } else if squeeze_flag {
        if sets.len() < 2 {
            let op = SqueezeOperation::new(set1, complement_flag);
            translate_input(&mut locked_stdin, &mut buffered_stdout, op);
        } else {
            let mut set2 = ExpandSet::new(sets[1].as_ref());
            let set2_ = ExpandSet::new(sets[1].as_ref());
            let op = TranslateAndSqueezeOperation::new(
                set1,
                &mut set2,
                set2_,
                complement_flag,
                truncate_flag,
            );
            translate_input(&mut locked_stdin, &mut buffered_stdout, op);
        }
    } else {
        let mut set2 = ExpandSet::new(sets[1].as_ref());
        let op = TranslateOperation::new(set1, &mut set2, truncate_flag, complement_flag);
        translate_input(&mut locked_stdin, &mut buffered_stdout, op);
    }

    0
}
<commit_msg>tr: fix merge conflict<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Michael Gehring <mg@ebfe.org>
//  * (c) kwantam <kwantam@gmail.com>
//  *     * 2015-04-28 ~ created `expand` module to eliminate most allocs during setup
//  * (c) Sergey "Shnatsel" Davidoff <shnatsel@gmail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) allocs bset dflag cflag sflag tflag

#[macro_use]
extern crate uucore;

mod expand;

use bit_set::BitSet;
use clap::{App, Arg};
use fnv::FnvHashMap;
use std::io::{stdin, stdout, BufRead, BufWriter, Write};

use crate::expand::ExpandSet;
use uucore::InvalidEncodingHandling;

static VERSION: &str = env!("CARGO_PKG_VERSION");
static ABOUT: &str = "translate or delete characters";

const BUFFER_LEN: usize = 1024;

mod options {
    pub const COMPLEMENT: &str = "complement";
    pub const DELETE: &str = "delete";
    pub const SQUEEZE: &str = "squeeze-repeats";
    pub const TRUNCATE: &str = "truncate";
    pub const SETS: &str = "sets";
}

trait SymbolTranslator {
    fn translate(&self, c: char, prev_c: char) -> Option<char>;
}

struct DeleteOperation {
    bset: BitSet,
    complement: bool,
}

impl DeleteOperation {
    fn new(set: ExpandSet, complement: bool) -> DeleteOperation {
        DeleteOperation {
            bset: set.map(|c| c as usize).collect(),
            complement,
        }
    }
}

impl SymbolTranslator for DeleteOperation {
    fn translate(&self, c: char, _prev_c: char) -> Option<char> {
        let uc = c as usize;
        if self.complement == self.bset.contains(uc) {
            Some(c)
        } else {
            None
        }
    }
}

struct SqueezeOperation {
    squeeze_set: BitSet,
    complement: bool,
}

impl SqueezeOperation {
    fn new(squeeze_set: ExpandSet, complement: bool) -> SqueezeOperation {
        SqueezeOperation {
            squeeze_set: squeeze_set.map(|c| c as usize).collect(),
            complement,
        }
    }
}

impl SymbolTranslator for SqueezeOperation {
    fn translate(&self, c: char, prev_c: char) -> Option<char> {
        if prev_c == c && self.complement != self.squeeze_set.contains(c as usize) {
            None
        } else {
            Some(c)
        }
    }
}

struct DeleteAndSqueezeOperation {
    delete_set: BitSet,
    squeeze_set: BitSet,
    complement: bool,
}

impl DeleteAndSqueezeOperation {
    fn new(
        delete_set: ExpandSet,
        squeeze_set: ExpandSet,
        complement: bool,
    ) -> DeleteAndSqueezeOperation {
        DeleteAndSqueezeOperation {
            delete_set: delete_set.map(|c| c as usize).collect(),
            squeeze_set: squeeze_set.map(|c| c as usize).collect(),
            complement,
        }
    }
}

impl SymbolTranslator for DeleteAndSqueezeOperation {
    fn translate(&self, c: char, prev_c: char) -> Option<char> {
        if self.complement != self.delete_set.contains(c as usize)
            || prev_c == c && self.squeeze_set.contains(c as usize)
        {
            None
        } else {
            Some(c)
        }
    }
}

struct TranslateOperation {
    translate_map: FnvHashMap<usize, char>,
    complement: bool,
    s2_last: char,
}

impl TranslateOperation {
    fn new(
        set1: ExpandSet,
        set2: &mut ExpandSet,
        truncate: bool,
        complement: bool,
    ) -> TranslateOperation {
        let mut map = FnvHashMap::default();
        let mut s2_prev = '_';
        for i in set1 {
            let s2_next = set2.next();

            if s2_next.is_none() && truncate {
                map.insert(i as usize, i);
            } else {
                s2_prev = s2_next.unwrap_or(s2_prev);
                map.insert(i as usize, s2_prev);
            }
        }
        TranslateOperation {
            translate_map: map,
            complement,
            s2_last: set2.last().unwrap_or(s2_prev),
        }
    }
}

impl SymbolTranslator for TranslateOperation {
    fn translate(&self, c: char, _prev_c: char) -> Option<char> {
        if self.complement {
            Some(if self.translate_map.contains_key(&(c as usize)) {
                c
            } else {
                self.s2_last
            })
        } else {
            Some(*self.translate_map.get(&(c as usize)).unwrap_or(&c))
        }
    }
}

struct TranslateAndSqueezeOperation {
    translate: TranslateOperation,
    squeeze: SqueezeOperation,
}

impl TranslateAndSqueezeOperation {
    fn new(
        set1: ExpandSet,
        set2: &mut ExpandSet,
        set2_: ExpandSet,
        truncate: bool,
        complement: bool,
    ) -> TranslateAndSqueezeOperation {
        TranslateAndSqueezeOperation {
            translate: TranslateOperation::new(set1, set2, truncate, complement),
            squeeze: SqueezeOperation::new(set2_, complement),
        }
    }
}

impl SymbolTranslator for TranslateAndSqueezeOperation {
    fn translate(&self, c: char, prev_c: char) -> Option<char> {
        // `unwrap()` will never panic because `Translate.translate()`
        // always returns `Some`.
        self.squeeze
            .translate(self.translate.translate(c, 0 as char).unwrap(), prev_c)
    }
}

fn translate_input<T: SymbolTranslator>(
    input: &mut dyn BufRead,
    output: &mut dyn Write,
    translator: T,
) {
    let mut buf = String::with_capacity(BUFFER_LEN + 4);
    let mut output_buf = String::with_capacity(BUFFER_LEN + 4);

    while let Ok(length) = input.read_line(&mut buf) {
        let mut prev_c = 0 as char;
        if length == 0 {
            break;
        }
        {
            // isolation to make borrow checker happy
            let filtered = buf.chars().filter_map(|c| {
                let res = translator.translate(c, prev_c);
                // Set `prev_c` to the post-translate character. This
                // allows the squeeze operation to correctly function
                // after the translate operation.
                if res.is_some() {
                    prev_c = res.unwrap();
                }
                res
            });

            output_buf.extend(filtered);
            output.write_all(output_buf.as_bytes()).unwrap();
        }
        buf.clear();
        output_buf.clear();
    }
}

fn get_usage() -> String {
    format!("{} [OPTION]... SET1 [SET2]", executable!())
}

fn get_long_usage() -> String {
    String::from(
        "Translate, squeeze, and/or delete characters from standard input,
writing to standard output.",
    )
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let args = args
        .collect_str(InvalidEncodingHandling::ConvertLossy)
        .accept_any();

    let usage = get_usage();
    let after_help = get_long_usage();

    let matches = App::new(executable!())
        .version(VERSION)
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(&after_help[..])
        .arg(
            Arg::with_name(options::COMPLEMENT)
                // .visible_short_alias('C')  // TODO: requires clap "3.0.0-beta.2"
                .short("c")
                .long(options::COMPLEMENT)
                .help("use the complement of SET1"),
        )
        .arg(
            Arg::with_name("C") // work around for `Arg::visible_short_alias`
                .short("C")
                .help("same as -c"),
        )
        .arg(
            Arg::with_name(options::DELETE)
                .short("d")
                .long(options::DELETE)
                .help("delete characters in SET1, do not translate"),
        )
        .arg(
            Arg::with_name(options::SQUEEZE)
                .long(options::SQUEEZE)
                .short("s")
                .help(
                    "replace each sequence  of  a  repeated  character  that  is
  listed  in the last specified SET, with a single occurrence
  of that character",
                ),
        )
        .arg(
            Arg::with_name(options::TRUNCATE)
                .long(options::TRUNCATE)
                .short("t")
                .help("first truncate SET1 to length of SET2"),
        )
        .arg(Arg::with_name(options::SETS).multiple(true))
        .get_matches_from(args);

    let delete_flag = matches.is_present(options::DELETE);
    let complement_flag = matches.is_present(options::COMPLEMENT) || matches.is_present("C");
    let squeeze_flag = matches.is_present(options::SQUEEZE);
    let truncate_flag = matches.is_present(options::TRUNCATE);

    let sets: Vec<String> = match matches.values_of(options::SETS) {
        Some(v) => v.map(|v| v.to_string()).collect(),
        None => vec![],
    };

    if sets.is_empty() {
        show_error!(
            "missing operand\nTry `{} --help` for more information.",
            executable!()
        );
        return 1;
    }

    if !(delete_flag || squeeze_flag) && sets.len() < 2 {
        show_error!(
            "missing operand after ‘{}’\nTry `{} --help` for more information.",
            sets[0],
            executable!()
        );
        return 1;
    }

    let stdin = stdin();
    let mut locked_stdin = stdin.lock();
    let stdout = stdout();
    let locked_stdout = stdout.lock();
    let mut buffered_stdout = BufWriter::new(locked_stdout);

    let set1 = ExpandSet::new(sets[0].as_ref());
    if delete_flag {
        if squeeze_flag {
            let set2 = ExpandSet::new(sets[1].as_ref());
            let op = DeleteAndSqueezeOperation::new(set1, set2, complement_flag);
            translate_input(&mut locked_stdin, &mut buffered_stdout, op);
        } else {
            let op = DeleteOperation::new(set1, complement_flag);
            translate_input(&mut locked_stdin, &mut buffered_stdout, op);
        }
    } else if squeeze_flag {
        if sets.len() < 2 {
            let op = SqueezeOperation::new(set1, complement_flag);
            translate_input(&mut locked_stdin, &mut buffered_stdout, op);
        } else {
            let mut set2 = ExpandSet::new(sets[1].as_ref());
            let set2_ = ExpandSet::new(sets[1].as_ref());
            let op = TranslateAndSqueezeOperation::new(
                set1,
                &mut set2,
                set2_,
                complement_flag,
                truncate_flag,
            );
            translate_input(&mut locked_stdin, &mut buffered_stdout, op);
        }
    } else {
        let mut set2 = ExpandSet::new(sets[1].as_ref());
        let op = TranslateOperation::new(set1, &mut set2, truncate_flag, complement_flag);
        translate_input(&mut locked_stdin, &mut buffered_stdout, op);
    }

    0
}
<|endoftext|>


<commit_before>use regex::Regex;
use std::collections::hashmap::HashMap;

pub struct Params {
    captures: HashMap<String, String>
}

impl Params {
    pub fn new<I: Iterator<String>>(uri: &str, matcher: Regex, params: I) -> Params {
        let captures = matcher.captures(uri).unwrap();
        Params {
            captures: params.map(|p| (p.clone(), captures.name(p.as_slice()).to_string())).collect()
        }
    }

    pub fn get<'a>(&'a self, param: &'static str) -> Option<String> {
        self.captures.find(&param.to_string()).and_then(|p| Some(p.clone()))
    }
}

#[cfg(test)]
mod test {
    use test::{Bencher, black_box};
    use super::*;
    use super::super::glob::deglob;

    #[test]
    fn test_new() {
        let params = Params::new(
            "/users/7324/235",
            deglob("/users/:userid/:groupid".to_string()),
            vec!["userid".to_string(), "groupid".to_string()].move_iter());
        assert_eq!(params.get("userid").unwrap(), "7324".to_string());
        assert_eq!(params.get("groupid").unwrap(), "235".to_string());
    }

    #[bench]
    fn bench_get(b: &mut Bencher) {
        let params = Params::new("/users/7324", deglob("/users/:userid".to_string()), vec!["userid".to_string()].move_iter());
        b.iter(|| {
            black_box(params.get("userid").unwrap())
        })
    }
}

<commit_msg>(doc) Documented complex chaining in Params.<commit_after>use regex::Regex;
use std::collections::hashmap::HashMap;

pub struct Params {
    captures: HashMap<String, String>
}

impl Params {
    pub fn new<I: Iterator<String>>(uri: &str, matcher: Regex, params: I) -> Params {
        let captures = matcher.captures(uri).unwrap();
        Params {
            captures: params.map(
                          // Map captures into (String, String) pairs so they
                          // can be collected into a HashMap<String, String>
                          |p| (p.clone(), captures.name(p.as_slice()).to_string())
                      ).collect()
        }
    }

    pub fn get<'a>(&'a self, param: &'static str) -> Option<String> {
        self.captures.find(&param.to_string()).and_then(|p| Some(p.clone()))
    }
}

#[cfg(test)]
mod test {
    use test::{Bencher, black_box};
    use super::*;
    use super::super::glob::deglob;

    #[test]
    fn test_new() {
        let params = Params::new(
            "/users/7324/235",
            deglob("/users/:userid/:groupid".to_string()),
            vec!["userid".to_string(), "groupid".to_string()].move_iter());
        assert_eq!(params.get("userid").unwrap(), "7324".to_string());
        assert_eq!(params.get("groupid").unwrap(), "235".to_string());
    }

    #[bench]
    fn bench_get(b: &mut Bencher) {
        let params = Params::new("/users/7324", deglob("/users/:userid".to_string()), vec!["userid".to_string()].move_iter());
        b.iter(|| {
            black_box(params.get("userid").unwrap())
        })
    }
}

<|endoftext|>


<commit_before>use sass::value_part::ValuePart;
use sass::color_value::*;
use sass::number_value::NumberValue;
use error::{SassError, ErrorKind, Result};
use sass::parameters::*;

use std::borrow::Cow::{self, Owned, Borrowed};
use std::collections::HashMap;

#[derive(Clone, Debug, PartialEq)]
pub struct SassFunctionCall<'a> {
    pub name: Cow<'a, str>,
    pub arguments: Vec<SassArgument<'a>>,
}

impl<'a> SassFunctionCall<'a> {
    pub fn evaluate(&self, variables: &HashMap<String, ValuePart<'a>>) -> Result<ValuePart<'a>> {
        match self.name {
            Borrowed("rgb") => {
                let params = vec![
                    SassParameter { name: Owned("$red".into()), default: None},
                    SassParameter { name: Owned("$green".into()), default: None},
                    SassParameter { name: Owned("$blue".into()), default: None},
                ];

                let resolved = try!(collate_args_parameters(
                    &params,
                    &self.arguments,
                    variables,
                ));

                Ok(ValuePart::Color(
                    try!(ColorValue::from_variables(&resolved))
                ))
            },
            Borrowed("rgba") => {
                match self.arguments.len() {
                    4 => {
                        let params = vec![
                            SassParameter { name: Owned("$red".into()), default: None},
                            SassParameter { name: Owned("$green".into()), default: None},
                            SassParameter { name: Owned("$blue".into()), default: None},
                            SassParameter { name: Owned("$alpha".into()), default: None},
                        ];

                        let resolved = try!(collate_args_parameters(
                            &params,
                            &self.arguments,
                            variables,
                        ));

                        Ok(ValuePart::Color(
                            try!(ColorValue::from_variables(&resolved))
                        ))
                    },
                    2 => {
                        let params = vec![
                            SassParameter { name: Owned("$color".into()), default: None},
                            SassParameter { name: Owned("$alpha".into()), default: None},
                        ];

                        let resolved = try!(collate_args_parameters(
                            &params,
                            &self.arguments,
                            variables,
                        ));

                        let color = try!(match resolved.get("$color".into()) {
                            Some(&ValuePart::Color(ref cv)) => Ok(cv.clone()),
                            Some(&ValuePart::String(ref s)) => ColorValue::from_hex(s.clone()),
                            ref e @ Some(_) | ref e @ None => Err(SassError {
                                kind: ErrorKind::UnexpectedValuePartType,
                                message: format!(
                                    "Expected color argument to rgba to be a color-like ValuePart; instead got `{:?}`", e
                                )
                            }),
                        });
                        let alpha = try!(alpha_from_variables(&resolved));

                        Ok(ValuePart::Color(
                            ColorValue::from_color_and_alpha(
                                color, alpha
                            )
                        ))
                    },
                    _ => Err(SassError {
                        kind: ErrorKind::WrongNumberOfArguments,
                        message: format!(
                            "Expected 2 or 4 arguments to rgba; got {}: `{:?}`",
                            self.arguments.len(), self.arguments
                        ),
                    })
                }
            },
            Borrowed("mix") => {
                let params = vec![
                    SassParameter { name: Owned("$color1".into()), default: None},
                    SassParameter { name: Owned("$color2".into()), default: None},
                ];

                let resolved = try!(collate_args_parameters(
                    &params,
                    &self.arguments,
                    variables,
                ));

                match (resolved.get("$color1".into()), resolved.get("$color2".into())) {
                    (Some(&ValuePart::Color(ref cv1)), Some(&ValuePart::Color(ref cv2))) => {
                        Ok(ValuePart::Color(try!(cv1.mix(cv2))))
                    },
                    (other1, other2) => {
                        Err(SassError {
                            kind: ErrorKind::UnexpectedValuePartType,
                            message: format!(
                                "Expected arguments to mix to be Colors; instead got `{:?}` and `{:?}`", other1, other2
                            )
                        })
                    }
                }
            },
            Borrowed("red") => {
                self.extract_color_part("red", variables)
            },
            Borrowed("green") => {
                self.extract_color_part("green", variables)
            },
            Borrowed("blue") => {
                self.extract_color_part("blue", variables)
            },
            _ => {
                Err(SassError {
                    kind: ErrorKind::UnknownFunction,
                    message: format!(
                        "Don't know how to evaluate function `{}`",
                        self.name,
                    ),
                })
            }
        }
    }

    fn extract_color_part(&self, which_color: &str, variables: &HashMap<String, ValuePart<'a>>) -> Result<ValuePart<'a>> {
        let params = vec![
            SassParameter { name: Owned("$color".into()), default: None},
        ];
        let resolved = try!(collate_args_parameters(
            &params,
            &self.arguments,
            variables,
        ));

        match resolved.get("$color".into()) {
            Some(&ValuePart::Color(ref cv)) => {
                let extracted = match which_color {
                    "red" => cv.red,
                    "green" => cv.green,
                    "blue" => cv.blue,
                    _ => unreachable!(),
                };
                Ok(ValuePart::Number(NumberValue::from_scalar(extracted as f32)))
            },
            ref e => Err(SassError {
                kind: ErrorKind::UnexpectedValuePartType,
                message: format!(
                    "Expected color argument to {} to be a Color; instead got `{:?}`", which_color, e
                )
            }),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use sass::color_value::ColorValue;
    use sass::value_part::ValuePart;
    use sass::number_value::NumberValue;
    use sass::parameters::SassArgument;
    use std::borrow::Cow::Borrowed;
    use std::collections::HashMap;

    #[test]
    fn it_returns_color_for_rgb() {
        let sfc = SassFunctionCall {
            name: Borrowed("rgb"),
            arguments: vec![
                SassArgument { name: None, value: Borrowed("10") },
                SassArgument { name: None, value: Borrowed("100") },
                SassArgument { name: None, value: Borrowed("73") },
            ],
        };
        assert_eq!(
            Ok(ValuePart::Color(ColorValue {
                red: 10, green: 100, blue: 73,
                alpha: None,
                computed: true, original: Borrowed("rgb(10, 100, 73)"),
            })),
            sfc.evaluate(&HashMap::new())
        );
    }

    #[test]
    fn it_returns_color_with_alpha_for_rgba() {
        let sfc = SassFunctionCall {
            name: Borrowed("rgba"),
            arguments: vec![
                SassArgument { name: None, value: Borrowed("10") },
                SassArgument { name: None, value: Borrowed("100") },
                SassArgument { name: None, value: Borrowed("73") },
                SassArgument { name: None, value: Borrowed(".5") },
            ],
        };
        assert_eq!(
            Ok(ValuePart::Color(ColorValue {
                red: 10, green: 100, blue: 73, alpha: Some(0.5),
                computed: true, original: Borrowed("rgba(10, 100, 73, 0.5)"),
            })),
            sfc.evaluate(&HashMap::new())
        );
    }

    #[test]
    fn it_returns_color_with_alpha_from_color_argument_for_rgba() {
        let sfc = SassFunctionCall {
            name: Borrowed("rgba"),
            arguments: vec![
                SassArgument { name: None, value: Borrowed("#f0e") },
                SassArgument { name: Some(Borrowed("$alpha")), value: Borrowed(".6") },
            ],
        };
        assert_eq!(
            Ok(ValuePart::Color(ColorValue {
                red: 255, green: 0, blue: 238, alpha: Some(0.6),
                computed: true, original: Borrowed("rgba(255, 0, 238, 0.6)"),
            })),
            sfc.evaluate(&HashMap::new())
        );
    }

    #[test]
    fn it_returns_red_part_of_color_for_red() {
        let sfc = SassFunctionCall {
            name: Borrowed("red"),
            arguments: vec![
                SassArgument { name: None, value: Borrowed("#cba") },
            ],
        };
        assert_eq!(
            Ok(ValuePart::Number(NumberValue::from_scalar(204.0))),
            sfc.evaluate(&HashMap::new())
        );
    }
}
<commit_msg>Partially implement type-of<commit_after>use sass::value_part::ValuePart;
use sass::color_value::*;
use sass::number_value::NumberValue;
use error::{SassError, ErrorKind, Result};
use sass::parameters::*;

use std::borrow::Cow::{self, Owned, Borrowed};
use std::collections::HashMap;

#[derive(Clone, Debug, PartialEq)]
pub struct SassFunctionCall<'a> {
    pub name: Cow<'a, str>,
    pub arguments: Vec<SassArgument<'a>>,
}

impl<'a> SassFunctionCall<'a> {
    pub fn evaluate(&self, variables: &HashMap<String, ValuePart<'a>>) -> Result<ValuePart<'a>> {
        match self.name {
            Borrowed("rgb") => {
                let params = vec![
                    SassParameter { name: Owned("$red".into()), default: None},
                    SassParameter { name: Owned("$green".into()), default: None},
                    SassParameter { name: Owned("$blue".into()), default: None},
                ];

                let resolved = try!(collate_args_parameters(
                    &params,
                    &self.arguments,
                    variables,
                ));

                Ok(ValuePart::Color(
                    try!(ColorValue::from_variables(&resolved))
                ))
            },
            Borrowed("rgba") => {
                match self.arguments.len() {
                    4 => {
                        let params = vec![
                            SassParameter { name: Owned("$red".into()), default: None},
                            SassParameter { name: Owned("$green".into()), default: None},
                            SassParameter { name: Owned("$blue".into()), default: None},
                            SassParameter { name: Owned("$alpha".into()), default: None},
                        ];

                        let resolved = try!(collate_args_parameters(
                            &params,
                            &self.arguments,
                            variables,
                        ));

                        Ok(ValuePart::Color(
                            try!(ColorValue::from_variables(&resolved))
                        ))
                    },
                    2 => {
                        let params = vec![
                            SassParameter { name: Owned("$color".into()), default: None},
                            SassParameter { name: Owned("$alpha".into()), default: None},
                        ];

                        let resolved = try!(collate_args_parameters(
                            &params,
                            &self.arguments,
                            variables,
                        ));

                        let color = try!(match resolved.get("$color".into()) {
                            Some(&ValuePart::Color(ref cv)) => Ok(cv.clone()),
                            Some(&ValuePart::String(ref s)) => ColorValue::from_hex(s.clone()),
                            ref e @ Some(_) | ref e @ None => Err(SassError {
                                kind: ErrorKind::UnexpectedValuePartType,
                                message: format!(
                                    "Expected color argument to rgba to be a color-like ValuePart; instead got `{:?}`", e
                                )
                            }),
                        });
                        let alpha = try!(alpha_from_variables(&resolved));

                        Ok(ValuePart::Color(
                            ColorValue::from_color_and_alpha(
                                color, alpha
                            )
                        ))
                    },
                    _ => Err(SassError {
                        kind: ErrorKind::WrongNumberOfArguments,
                        message: format!(
                            "Expected 2 or 4 arguments to rgba; got {}: `{:?}`",
                            self.arguments.len(), self.arguments
                        ),
                    })
                }
            },
            Borrowed("mix") => {
                let params = vec![
                    SassParameter { name: Owned("$color1".into()), default: None},
                    SassParameter { name: Owned("$color2".into()), default: None},
                ];

                let resolved = try!(collate_args_parameters(
                    &params,
                    &self.arguments,
                    variables,
                ));

                match (resolved.get("$color1".into()), resolved.get("$color2".into())) {
                    (Some(&ValuePart::Color(ref cv1)), Some(&ValuePart::Color(ref cv2))) => {
                        Ok(ValuePart::Color(try!(cv1.mix(cv2))))
                    },
                    (other1, other2) => {
                        Err(SassError {
                            kind: ErrorKind::UnexpectedValuePartType,
                            message: format!(
                                "Expected arguments to mix to be Colors; instead got `{:?}` and `{:?}`", other1, other2
                            )
                        })
                    }
                }
            },
            Borrowed("type-of") => {
                let params = vec![
                    SassParameter { name: Owned("$value".into()), default: None},
                ];
                let resolved = try!(collate_args_parameters(
                    &params,
                    &self.arguments,
                    variables,
                ));
                match resolved.get("$value".into()) {
                    Some(&ValuePart::Color(..)) => Ok(ValuePart::String(Borrowed("color"))),
                    Some(&ValuePart::Number(..)) => Ok(ValuePart::String(Borrowed("number"))),
                    Some(&ValuePart::String(..)) => Ok(ValuePart::String(Borrowed("string"))),
                    other => Err(SassError {
                        kind: ErrorKind::UnexpectedValuePartType,
                        message: format!(
                            "Don't know type-of value `{:?}`", other
                        )
                    }),
                }
            },
            Borrowed("red") => {
                self.extract_color_part("red", variables)
            },
            Borrowed("green") => {
                self.extract_color_part("green", variables)
            },
            Borrowed("blue") => {
                self.extract_color_part("blue", variables)
            },
            _ => {
                Err(SassError {
                    kind: ErrorKind::UnknownFunction,
                    message: format!(
                        "Don't know how to evaluate function `{}`",
                        self.name,
                    ),
                })
            }
        }
    }

    fn extract_color_part(&self, which_color: &str, variables: &HashMap<String, ValuePart<'a>>) -> Result<ValuePart<'a>> {
        let params = vec![
            SassParameter { name: Owned("$color".into()), default: None},
        ];
        let resolved = try!(collate_args_parameters(
            &params,
            &self.arguments,
            variables,
        ));

        match resolved.get("$color".into()) {
            Some(&ValuePart::Color(ref cv)) => {
                let extracted = match which_color {
                    "red" => cv.red,
                    "green" => cv.green,
                    "blue" => cv.blue,
                    _ => unreachable!(),
                };
                Ok(ValuePart::Number(NumberValue::from_scalar(extracted as f32)))
            },
            ref e => Err(SassError {
                kind: ErrorKind::UnexpectedValuePartType,
                message: format!(
                    "Expected color argument to {} to be a Color; instead got `{:?}`", which_color, e
                )
            }),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use sass::color_value::ColorValue;
    use sass::value_part::ValuePart;
    use sass::number_value::NumberValue;
    use sass::parameters::SassArgument;
    use std::borrow::Cow::Borrowed;
    use std::collections::HashMap;

    #[test]
    fn it_returns_color_for_rgb() {
        let sfc = SassFunctionCall {
            name: Borrowed("rgb"),
            arguments: vec![
                SassArgument { name: None, value: Borrowed("10") },
                SassArgument { name: None, value: Borrowed("100") },
                SassArgument { name: None, value: Borrowed("73") },
            ],
        };
        assert_eq!(
            Ok(ValuePart::Color(ColorValue {
                red: 10, green: 100, blue: 73,
                alpha: None,
                computed: true, original: Borrowed("rgb(10, 100, 73)"),
            })),
            sfc.evaluate(&HashMap::new())
        );
    }

    #[test]
    fn it_returns_color_with_alpha_for_rgba() {
        let sfc = SassFunctionCall {
            name: Borrowed("rgba"),
            arguments: vec![
                SassArgument { name: None, value: Borrowed("10") },
                SassArgument { name: None, value: Borrowed("100") },
                SassArgument { name: None, value: Borrowed("73") },
                SassArgument { name: None, value: Borrowed(".5") },
            ],
        };
        assert_eq!(
            Ok(ValuePart::Color(ColorValue {
                red: 10, green: 100, blue: 73, alpha: Some(0.5),
                computed: true, original: Borrowed("rgba(10, 100, 73, 0.5)"),
            })),
            sfc.evaluate(&HashMap::new())
        );
    }

    #[test]
    fn it_returns_color_with_alpha_from_color_argument_for_rgba() {
        let sfc = SassFunctionCall {
            name: Borrowed("rgba"),
            arguments: vec![
                SassArgument { name: None, value: Borrowed("#f0e") },
                SassArgument { name: Some(Borrowed("$alpha")), value: Borrowed(".6") },
            ],
        };
        assert_eq!(
            Ok(ValuePart::Color(ColorValue {
                red: 255, green: 0, blue: 238, alpha: Some(0.6),
                computed: true, original: Borrowed("rgba(255, 0, 238, 0.6)"),
            })),
            sfc.evaluate(&HashMap::new())
        );
    }

    #[test]
    fn it_returns_red_part_of_color_for_red() {
        let sfc = SassFunctionCall {
            name: Borrowed("red"),
            arguments: vec![
                SassArgument { name: None, value: Borrowed("#cba") },
            ],
        };
        assert_eq!(
            Ok(ValuePart::Number(NumberValue::from_scalar(204.0))),
            sfc.evaluate(&HashMap::new())
        );
    }
}
<|endoftext|>


<commit_before>use rustc_serialize::base64::{self, ToBase64};

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

fn imgcat(buffer: Vec<u8>, size_key: &str, size_value: usize) -> String {
    let e = buffer.as_slice().to_base64(base64::STANDARD);
    return String::from(format!("\x1b]1337;File=inline=1;{size_key}={size_value};:{code}\x07", size_key = size_key, size_value = size_value, code = e));
}

pub fn imgcat_from_path(path: &str, width: usize) -> String {
    let mut f = match File::open(path) {
        Err(why) => panic!("couldn't open: {}", why),
        Ok(file) => file,
    };
    let mut buffer = Vec::new();
    f.read_to_end(&mut buffer).expect("fail to read image");

    return imgcat(buffer, &"width", width);
}

pub fn imgcat_from_url(url: &str, height: usize) -> String {
    let key = url.to_string().into_bytes().to_base64(base64::URL_SAFE);
    let path = format!("data/img/{file_name}", file_name = key);

    return match File::open(path) {
        // Err(why) => String::from(format!("[{url}]", url = url)),
        Err(why) => String::from(format!("[{code}]", code = why)),
        Ok(mut file) => {
            let mut buffer = Vec::new();
            file.read_to_end(&mut buffer).expect("fail to read image");
            return imgcat(buffer, &"height", height);
        },
    };
}

pub fn reset_screen() {
    print!("{}{}{}", ::termion::clear::All, ::termion::style::Reset, ::termion::cursor::Show);
}

pub fn clear_screen () {
    print!("{}", ::termion::clear::All);
}
<commit_msg>[FIX] can't print image<commit_after>use rustc_serialize::base64::{self, ToBase64};

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

fn imgcat(buffer: Vec<u8>, size_key: &str, size_value: usize) -> String {
    let e = buffer.as_slice().to_base64(base64::STANDARD);
    return String::from(format!("\x1b]1337;File=inline=1;{size_key}={size_value};:{code}\x07", size_key = size_key, size_value = size_value, code = e));
}

pub fn imgcat_from_path(path: &str, width: usize) -> String {
    let mut f = match File::open(path) {
        Err(why) => panic!("couldn't open: {}", why),
        Ok(file) => file,
    };
    let mut buffer = Vec::new();
    f.read_to_end(&mut buffer).expect("fail to read image");

    return imgcat(buffer, &"width", width);
}

pub fn imgcat_from_url(url: &str, height: usize) -> String {
    let key = url.to_string().into_bytes().to_base64(base64::URL_SAFE);
    let path = format!("data/cache/img/{file_name}", file_name = key);

    return match File::open(path) {
        // Err(why) => String::from(format!("[{url}]", url = url)),
        Err(why) => String::from(format!("[{code}]", code = why)),
        Ok(mut file) => {
            let mut buffer = Vec::new();
            file.read_to_end(&mut buffer).expect("fail to read image");
            return imgcat(buffer, &"height", height);
        },
    };
}

pub fn reset_screen() {
    print!("{}{}{}", ::termion::clear::All, ::termion::style::Reset, ::termion::cursor::Show);
}

pub fn clear_screen () {
    print!("{}", ::termion::clear::All);
}
<|endoftext|>


<commit_before>use piston_window;

pub enum InputResult {
    PushScreen(Box<Screen>),
    PopScreen,
}

pub type InputResults = Vec<InputResult>;

pub trait Screen {
    fn on_input(&mut self, input: &piston_window::Input, window: &piston_window::PistonWindow) -> InputResults;
    fn on_draw(&mut self, args: &piston_window::RenderArgs, window: &piston_window::PistonWindow);
    fn on_update(&mut self, args: &piston_window::UpdateArgs);
}
<commit_msg>Screen's context<commit_after>use piston_window;

pub enum InputResult {
    PushScreen(Box<Screen>),
    PopScreen,
}

pub type InputResults = Vec<InputResult>;

pub struct Context {
    pub assets: ::std::path::Path,
}

pub trait Screen {
    fn on_input(&mut self, input: &piston_window::Input, window: &piston_window::PistonWindow) -> InputResults;
    fn on_draw(&mut self, args: &piston_window::RenderArgs, window: &piston_window::PistonWindow);
    fn on_update(&mut self, args: &piston_window::UpdateArgs);
}
<|endoftext|>


<commit_before>extern crate libc;

pub const TB_DEFAULT   : u16 = 0x00;
pub const TB_BLACK     : u16 = 0x01;
pub const TB_RED       : u16 = 0x02;
pub const TB_GREEN     : u16 = 0x03;
pub const TB_YELLOW    : u16 = 0x04;
pub const TB_BLUE      : u16 = 0x05;
pub const TB_MAGENTA   : u16 = 0x06;
pub const TB_CYAN      : u16 = 0x07;
pub const TB_WHITE     : u16 = 0x08;

pub const TB_BOLD      : u16 = 0x0100;
pub const TB_UNDERLINE : u16 = 0x0200;
pub const TB_REVERSE   : u16 = 0x0400;

#[repr(C)]
pub struct Cell {
    pub ch: u32,
    pub fg: u16,
    pub bg: u16,
}

const TB_EUNSUPPORTED_TERMINAL : libc::c_int = -1;
const TB_EFAILED_TO_OPEN_TTY   : libc::c_int = -2;

const TB_HIDE_CURSOR      : libc::c_int = -1;

const TB_OUTPUT_CURRENT   : libc::c_int = 0;
const TB_OUTPUT_NORMAL    : libc::c_int = 1;
// These are not used, we just std::mem::transmute the value if it's in range
// const TB_OUTPUT_256       : libc::c_int = 2;
// const TB_OUTPUT_216       : libc::c_int = 3;
const TB_OUTPUT_GRAYSCALE : libc::c_int = 4;

extern {
    fn tb_init() -> libc::c_int;
    fn tb_resize();
    fn tb_shutdown();
    fn tb_width() -> libc::c_int;
    fn tb_height() -> libc::c_int;
    fn tb_clear() -> libc::c_int;
    fn tb_set_clear_attributes(fg: u16, bg: u16);
    fn tb_present();
    fn tb_set_cursor(cx: libc::c_int, cy: libc::c_int);
    fn tb_put_cell(x: libc::c_int, y: libc::c_int, cell: Cell);
    fn tb_change_cell(x: libc::c_int, y: libc::c_int, ch: u32, fg: u16, bg: u16);
    // fn tb_cell_buffer() -> *mut tb_cell;
    fn tb_select_output_mode(mode: libc::c_int) -> libc::c_int;
}

pub struct Termbox {}

#[derive(Debug)]
pub enum InitError { UnsupportedTerminal, FailedToOpenTty }

#[repr(C)]
pub enum OutputMode {
    OutputNormal = 1, Output256, Output216, OutputGrayscale
}

impl Termbox {
    pub fn init() -> Result<Termbox, InitError> {
        let ret = unsafe { tb_init() };
        if ret == TB_EUNSUPPORTED_TERMINAL {
            Err(InitError::UnsupportedTerminal)
        } else if ret == TB_EFAILED_TO_OPEN_TTY {
            Err(InitError::FailedToOpenTty)
        } else {
            Ok(Termbox {})
        }
    }

    pub fn resize(&mut self) {
        unsafe { tb_resize(); }
    }

    pub fn width(&self) -> i32 {
        unsafe { tb_width() as i32 }
    }

    pub fn height(&self) -> i32 {
        unsafe { tb_height() as i32 }
    }

    pub fn clear(&mut self) {
        unsafe { tb_clear(); }
    }

    pub fn set_clear_attributes(&mut self, fg: u16, bg: u16) {
        unsafe { tb_set_clear_attributes(fg, bg) }
    }

    pub fn present(&mut self) {
        unsafe { tb_present() }
    }

    pub fn hide_cursor(&mut self) {
        unsafe { tb_set_cursor(TB_HIDE_CURSOR, TB_HIDE_CURSOR); }
    }

    pub fn set_cursor(&mut self, cx: i32, cy: i32) {
        unsafe { tb_set_cursor(cx as libc::c_int, cy as libc::c_int) }
    }

    pub fn put_cell(&mut self, x: i32, y: i32, cell: Cell) {
        unsafe { tb_put_cell(x as libc::c_int, y as libc::c_int, cell) }
    }

    pub fn change_cell(&mut self, x: i32, y: i32, ch: char, fg: u16, bg: u16) {
        // FIXME: This is assuming that the char is represented as its utf-8 encoding!
        unsafe { tb_change_cell(x as libc::c_int, y as libc::c_int, ch as u32, fg, bg) }
    }

    pub fn get_output_mode(&self) -> OutputMode {
        let ret = unsafe { tb_select_output_mode(TB_OUTPUT_CURRENT) };
        if ret >= TB_OUTPUT_NORMAL && ret <= TB_OUTPUT_GRAYSCALE {
            unsafe { std::mem::transmute(ret) }
        } else {
            panic!("get_output_mode(): Invalid output mode: {}", ret)
        }
    }

    pub fn set_output_mode(&mut self, mode: OutputMode) {
        unsafe { tb_select_output_mode(std::mem::transmute(mode)); }
    }
}

impl Drop for Termbox {
    fn drop(&mut self) {
        unsafe { tb_shutdown(); }
    }
}
<commit_msg>Fix char to u32 conversion<commit_after>extern crate libc;

pub const TB_DEFAULT   : u16 = 0x00;
pub const TB_BLACK     : u16 = 0x01;
pub const TB_RED       : u16 = 0x02;
pub const TB_GREEN     : u16 = 0x03;
pub const TB_YELLOW    : u16 = 0x04;
pub const TB_BLUE      : u16 = 0x05;
pub const TB_MAGENTA   : u16 = 0x06;
pub const TB_CYAN      : u16 = 0x07;
pub const TB_WHITE     : u16 = 0x08;

pub const TB_BOLD      : u16 = 0x0100;
pub const TB_UNDERLINE : u16 = 0x0200;
pub const TB_REVERSE   : u16 = 0x0400;

#[repr(C)]
pub struct Cell {
    pub ch: u32,
    pub fg: u16,
    pub bg: u16,
}

const TB_EUNSUPPORTED_TERMINAL : libc::c_int = -1;
const TB_EFAILED_TO_OPEN_TTY   : libc::c_int = -2;

const TB_HIDE_CURSOR      : libc::c_int = -1;

const TB_OUTPUT_CURRENT   : libc::c_int = 0;
const TB_OUTPUT_NORMAL    : libc::c_int = 1;
// These are not used, we just std::mem::transmute the value if it's in range
// const TB_OUTPUT_256       : libc::c_int = 2;
// const TB_OUTPUT_216       : libc::c_int = 3;
const TB_OUTPUT_GRAYSCALE : libc::c_int = 4;

extern {
    fn tb_init() -> libc::c_int;
    fn tb_resize();
    fn tb_shutdown();
    fn tb_width() -> libc::c_int;
    fn tb_height() -> libc::c_int;
    fn tb_clear() -> libc::c_int;
    fn tb_set_clear_attributes(fg: u16, bg: u16);
    fn tb_present();
    fn tb_set_cursor(cx: libc::c_int, cy: libc::c_int);
    fn tb_put_cell(x: libc::c_int, y: libc::c_int, cell: Cell);
    fn tb_change_cell(x: libc::c_int, y: libc::c_int, ch: u32, fg: u16, bg: u16);
    // fn tb_cell_buffer() -> *mut tb_cell;
    fn tb_select_output_mode(mode: libc::c_int) -> libc::c_int;
}

pub struct Termbox {}

#[derive(Debug)]
pub enum InitError { UnsupportedTerminal, FailedToOpenTty }

#[repr(C)]
pub enum OutputMode {
    OutputNormal = 1, Output256, Output216, OutputGrayscale
}

impl Termbox {
    pub fn init() -> Result<Termbox, InitError> {
        let ret = unsafe { tb_init() };
        if ret == TB_EUNSUPPORTED_TERMINAL {
            Err(InitError::UnsupportedTerminal)
        } else if ret == TB_EFAILED_TO_OPEN_TTY {
            Err(InitError::FailedToOpenTty)
        } else {
            Ok(Termbox {})
        }
    }

    pub fn resize(&mut self) {
        unsafe { tb_resize(); }
    }

    pub fn width(&self) -> i32 {
        unsafe { tb_width() as i32 }
    }

    pub fn height(&self) -> i32 {
        unsafe { tb_height() as i32 }
    }

    pub fn clear(&mut self) {
        unsafe { tb_clear(); }
    }

    pub fn set_clear_attributes(&mut self, fg: u16, bg: u16) {
        unsafe { tb_set_clear_attributes(fg, bg) }
    }

    pub fn present(&mut self) {
        unsafe { tb_present() }
    }

    pub fn hide_cursor(&mut self) {
        unsafe { tb_set_cursor(TB_HIDE_CURSOR, TB_HIDE_CURSOR); }
    }

    pub fn set_cursor(&mut self, cx: i32, cy: i32) {
        unsafe { tb_set_cursor(cx as libc::c_int, cy as libc::c_int) }
    }

    pub fn put_cell(&mut self, x: i32, y: i32, cell: Cell) {
        unsafe { tb_put_cell(x as libc::c_int, y as libc::c_int, cell) }
    }

    pub fn change_cell(&mut self, x: i32, y: i32, ch: char, fg: u16, bg: u16) {
        unsafe { tb_change_cell(x as libc::c_int, y as libc::c_int, char_to_utf8(ch), fg, bg) }
    }

    pub fn get_output_mode(&self) -> OutputMode {
        let ret = unsafe { tb_select_output_mode(TB_OUTPUT_CURRENT) };
        if ret >= TB_OUTPUT_NORMAL && ret <= TB_OUTPUT_GRAYSCALE {
            unsafe { std::mem::transmute(ret) }
        } else {
            panic!("get_output_mode(): Invalid output mode: {}", ret)
        }
    }

    pub fn set_output_mode(&mut self, mode: OutputMode) {
        unsafe { tb_select_output_mode(std::mem::transmute(mode)); }
    }
}

impl Drop for Termbox {
    fn drop(&mut self) {
        unsafe { tb_shutdown(); }
    }
}

// https://github.com/rust-lang/rust/blob/03bed655142dd5e42ba4539de53b3663d8a123e0/src/libcore/char.rs#L424

const TAG_CONT:    u8  = 0b1000_0000;
const TAG_TWO_B:   u8  = 0b1100_0000;
const TAG_THREE_B: u8  = 0b1110_0000;
const TAG_FOUR_B:  u8  = 0b1111_0000;
const MAX_ONE_B:   u32 =     0x80;
const MAX_TWO_B:   u32 =    0x800;
const MAX_THREE_B: u32 =  0x10000;

fn char_to_utf8(c: char) -> u32 {
    let code = c as u32;
    if code < MAX_ONE_B {
        code as u32
    } else if code < MAX_TWO_B {
        ((((code >> 6 & 0x1F) as u8 | TAG_TWO_B) as u32) << 8) +
        (((code & 0x3F) as u8 | TAG_CONT) as u32)
    } else if code < MAX_THREE_B {
        ((((code >> 12 & 0x0F) as u8 | TAG_THREE_B) as u32) << 16) +
        ((((code >>  6 & 0x3F) as u8 | TAG_CONT) as u32) << 8) +
        (((code & 0x3F) as u8 | TAG_CONT) as u32)
    } else {
        ((((code >> 18 & 0x07) as u8 | TAG_FOUR_B) as u32) << 24) +
        ((((code >> 12 & 0x3F) as u8 | TAG_CONT) as u32) << 16) +
        ((((code >>  6 & 0x3F) as u8 | TAG_CONT) as u32) << 8) +
        (((code & 0x3F) as u8 | TAG_CONT) as u32)
    }
}
<|endoftext|>


<commit_before>use std::any::Any;

use super::Mode;

use crate::core::editor::InputStageActionMap;
use crate::core::modes::core_mode::split_with_direction;
use crate::core::Editor;
use crate::core::EditorEnv;

use crate::core::view::LayoutDirection;
use crate::core::view::LayoutOperation;

use crate::core::view::register_view_subscriber;

use crate::core::view::View;
use crate::core::view::ViewEventDestination;
use crate::core::view::ViewEventSource;

pub struct SimpleViewMode {
    // add common fields
}
pub struct SimpleViewModeContext {
    // add per view fields
}

impl<'a> Mode for SimpleViewMode {
    fn name(&self) -> &'static str {
        &"simple-view"
    }

    fn build_action_map(&self) -> InputStageActionMap<'static> {
        let mut map = InputStageActionMap::new();
        Self::register_input_stage_actions(&mut map);
        map
    }

    fn alloc_ctx(&self) -> Box<dyn Any> {
        dbg_println!("alloc SimpleViewMode-mode ctx");
        let ctx = SimpleViewModeContext {};
        Box::new(ctx)
    }

    fn configure_view(
        &mut self,
        mut editor: &mut Editor<'static>,
        mut env: &mut EditorEnv<'static>,
        mut v: &mut View<'static>,
    ) {
        let doc = v.document();
        //        let doc_sz = doc.as_ref().unwrap().read().size();
        let line_number_view_width = 13 + 2;

        // children_layout_and_modes
        let ops_modes = vec![
            // line numbers
            (
                LayoutOperation::Fixed {
                    size: line_number_view_width,
                }, // TODO(ceg): adjust size based on screen content
                doc.clone(),
                vec!["line-number-mode".to_owned()], // TODO(ceg): "line-number-mode" in screen overlay pass
            ),
            /*
                        // line changed
                        (
                            LayoutOperation::Fixed { size: 0 }, // TODO(ceg): adjust size based on screen content
                            doc.clone(),
                            vec!["vscrollbar-mode".to_owned()], // TODO(ceg): "line-change-mode" in screen overlay pass
                        ),
                        // fold
                        (
                            LayoutOperation::Fixed { size: 0 }, // TODO(ceg): adjust size based on screen content
                            doc.clone(),
                            vec!["vscrollbar-mode".to_owned()], // TODO(ceg): "fold-mode" in screen overlay pass
                        ),
            */
            // text
            (
                LayoutOperation::RemainMinus { minus: 1 },
                doc.clone(),
                vec![
                    "core-mode".to_owned(),
                    "text-mode".to_owned(),
                    "find-mode".to_owned(),
                    "goto-line-mode".to_owned(),
                    "open-doc-mode".to_owned(),
                ],
            ),
            // scrollbar
            (
                LayoutOperation::Fixed { size: 1 },
                doc.clone(),
                vec!["vscrollbar-mode".to_owned()],
            ),
        ];

        let mut layout_ops = vec![];
        let mut docs = vec![];
        let mut modes = vec![];

        for e in &ops_modes {
            layout_ops.push(e.0.clone());
            docs.push(e.1.clone());
            modes.push(e.2.clone());
        }

        let width = v.width;
        let height = v.height;

        v.layout_direction = LayoutDirection::Horizontal;

        split_with_direction(
            &mut editor,
            &mut env,
            &mut v,
            width,
            height,
            LayoutDirection::Horizontal,
            &layout_ops,
            &docs,
            &modes,
        );

        v.is_splittable = true; // Nb: do not remove , allow recursive splitting

        // TODO(ceg): set focus
        // set focus on text view
        let line_numbers_view_idx = 0;
        let text_view_idx = 1;
        let scroll_bar_idx = 2;

        v.focus_to = Some(v.children[text_view_idx].id); // TODO(ceg):

        dbg_println!("simple-view: children: {:?}", v.children);
        // register siblings view
        // text <--> scrollbar

        let vscrollbar_mode = editor.get_mode("vscrollbar-mode").unwrap();

        let text_view_src = ViewEventSource {
            id: v.children[text_view_idx].id,
        };

        let scrollbar_dst = ViewEventDestination {
            id: v.children[scroll_bar_idx].id,
        };

        // view events -> scrollbar
        register_view_subscriber(
            editor,
            env,
            vscrollbar_mode.clone(),
            text_view_src, // publisher
            scrollbar_dst, // subscriber
        );

        // view events -> line_number
        let line_number_dst = ViewEventDestination {
            id: v.children[line_numbers_view_idx].id,
        };

        let line_number_mode = editor.get_mode("line-number-mode").unwrap();
        register_view_subscriber(
            editor,
            env,
            line_number_mode.clone(),
            text_view_src,   // publisher
            line_number_dst, // subscriber
        );
    }
}

impl SimpleViewMode {
    pub fn new() -> Self {
        dbg_println!("SimpleViewMode");
        SimpleViewMode {}
    }

    pub fn register_input_stage_actions<'a>(_map: &'a mut InputStageActionMap<'a>) {}
}
<commit_msg>simple_view: use Rc::clone(..)<commit_after>use std::any::Any;
use std::rc::Rc;

use super::Mode;

use crate::core::editor::InputStageActionMap;
use crate::core::modes::core_mode::split_with_direction;
use crate::core::Editor;
use crate::core::EditorEnv;

use crate::core::view::LayoutDirection;
use crate::core::view::LayoutOperation;

use crate::core::view::register_view_subscriber;

use crate::core::view::View;
use crate::core::view::ViewEventDestination;
use crate::core::view::ViewEventSource;

pub struct SimpleViewMode {
    // add common fields
}
pub struct SimpleViewModeContext {
    // add per view fields
}

impl<'a> Mode for SimpleViewMode {
    fn name(&self) -> &'static str {
        &"simple-view"
    }

    fn build_action_map(&self) -> InputStageActionMap<'static> {
        let mut map = InputStageActionMap::new();
        Self::register_input_stage_actions(&mut map);
        map
    }

    fn alloc_ctx(&self) -> Box<dyn Any> {
        dbg_println!("alloc SimpleViewMode-mode ctx");
        let ctx = SimpleViewModeContext {};
        Box::new(ctx)
    }

    fn configure_view(
        &mut self,
        mut editor: &mut Editor<'static>,
        mut env: &mut EditorEnv<'static>,
        mut v: &mut View<'static>,
    ) {
        let doc = v.document();
        //        let doc_sz = doc.as_ref().unwrap().read().size();
        let line_number_view_width = 13 + 2;

        // children_layout_and_modes
        let ops_modes = vec![
            // line numbers
            (
                LayoutOperation::Fixed {
                    size: line_number_view_width,
                }, // TODO(ceg): adjust size based on screen content
                doc.clone(),
                vec!["line-number-mode".to_owned()], // TODO(ceg): "line-number-mode" in screen overlay pass
            ),
            /*
                        // line changed
                        (
                            LayoutOperation::Fixed { size: 0 }, // TODO(ceg): adjust size based on screen content
                            doc.clone(),
                            vec!["vscrollbar-mode".to_owned()], // TODO(ceg): "line-change-mode" in screen overlay pass
                        ),
                        // fold
                        (
                            LayoutOperation::Fixed { size: 0 }, // TODO(ceg): adjust size based on screen content
                            doc.clone(),
                            vec!["vscrollbar-mode".to_owned()], // TODO(ceg): "fold-mode" in screen overlay pass
                        ),
            */
            // text
            (
                LayoutOperation::RemainMinus { minus: 1 },
                doc.clone(),
                vec![
                    "core-mode".to_owned(),
                    "text-mode".to_owned(),
                    "find-mode".to_owned(),
                    "goto-line-mode".to_owned(),
                    "open-doc-mode".to_owned(),
                ],
            ),
            // scrollbar
            (
                LayoutOperation::Fixed { size: 1 },
                doc.clone(),
                vec!["vscrollbar-mode".to_owned()],
            ),
        ];

        let mut layout_ops = vec![];
        let mut docs = vec![];
        let mut modes = vec![];

        for e in &ops_modes {
            layout_ops.push(e.0.clone());
            docs.push(e.1.clone());
            modes.push(e.2.clone());
        }

        let width = v.width;
        let height = v.height;

        v.layout_direction = LayoutDirection::Horizontal;

        split_with_direction(
            &mut editor,
            &mut env,
            &mut v,
            width,
            height,
            LayoutDirection::Horizontal,
            &layout_ops,
            &docs,
            &modes,
        );

        v.is_splittable = true; // Nb: do not remove , allow recursive splitting

        // TODO(ceg): set focus
        // set focus on text view
        let line_numbers_view_idx = 0;
        let text_view_idx = 1;
        let scroll_bar_idx = 2;

        v.focus_to = Some(v.children[text_view_idx].id); // TODO(ceg):

        dbg_println!("simple-view: children: {:?}", v.children);
        // register siblings view
        // text <--> scrollbar

        let vscrollbar_mode = editor.get_mode("vscrollbar-mode").unwrap();

        let text_view_src = ViewEventSource {
            id: v.children[text_view_idx].id,
        };

        let scrollbar_dst = ViewEventDestination {
            id: v.children[scroll_bar_idx].id,
        };

        // view events -> scrollbar
        register_view_subscriber(
            editor,
            env,
            Rc::clone(&vscrollbar_mode),
            text_view_src, // publisher
            scrollbar_dst, // subscriber
        );

        // view events -> line_number
        let line_number_dst = ViewEventDestination {
            id: v.children[line_numbers_view_idx].id,
        };

        let line_number_mode = editor.get_mode("line-number-mode").unwrap();
        register_view_subscriber(
            editor,
            env,
            Rc::clone(&line_number_mode),
            text_view_src,   // publisher
            line_number_dst, // subscriber
        );
    }
}

impl SimpleViewMode {
    pub fn new() -> Self {
        dbg_println!("SimpleViewMode");
        SimpleViewMode {}
    }

    pub fn register_input_stage_actions<'a>(_map: &'a mut InputStageActionMap<'a>) {}
}
<|endoftext|>