<commit_before>//
extern crate termion;

//
use self::termion::screen::AlternateScreen;
use self::termion::event::{Event, Key, MouseEvent};
use self::termion::input::{TermRead, MouseTerminal};
use self::termion::raw::IntoRawMode;
use self::termion::terminal_size;

use std::io::{self, Write, stdin, Stdout};

//
use core::view::View;
use core::screen::Screen;
use core::codepointinfo::CodepointInfo;

use core::editor::Editor;

//
struct UiState {
    keys: Vec<Event>,
    quit: bool,
    status: String,
    display_status: bool,
    display_view: bool,
    vid: u64,
    nb_view: usize,
}

impl UiState {
    fn new() -> UiState {
        UiState {
            keys: Vec::new(),
            quit: false,
            status: String::new(),
            display_status: true,
            display_view: true,
            vid: 0,
            nb_view: 0,
        }
    }
}

pub fn main_loop(mut editor: &mut Editor) {

    let mut view_state = UiState::new();

    let (width, height) = terminal_size().unwrap();

    setup_views(editor, width as usize, height as usize);

    //
    let stdout = MouseTerminal::from(io::stdout().into_raw_mode().unwrap());
    let mut stdout = AlternateScreen::from(stdout);

    write!(stdout, "{}{}", termion::cursor::Hide, termion::clear::All).unwrap();
    stdout.flush().unwrap();

    while !view_state.quit {

        view_state.nb_view = editor.view_map.len();
        let mut view = editor.view_map.get_mut(&view_state.vid);

        let status_line_y = height;

        if view_state.display_view == true {
            draw_view(&mut view.as_mut().unwrap(), &mut stdout);
        }

        if view_state.display_status == true {
            display_status_line(&mut view.as_mut().unwrap(),
                                &view_state.status,
                                status_line_y,
                                width,
                                &mut stdout);
        }

        process_input_events(&mut view_state);
    }

    // quit
    // clear, restore cursor
    write!(stdout, "{}{}", termion::clear::All, termion::cursor::Show).unwrap();
    stdout.flush().unwrap();
}

fn setup_views(editor: &mut Editor, width: usize, height: usize) {

    let mut views = Vec::new();

    let mut vid = 0;

    for (_, b) in &editor.document_map {

        let view = View::new(vid,
                             0 as u64,
                             width as usize,
                             height as usize,
                             Some(b.clone()));
        views.push(view);
        vid += 1;
    }

    for view in views {
        &editor.view_map.insert(view.id, Box::new(view));
    }
}

fn fill_screen(view: &mut View) {

    match view.document {

        Some(ref buf) => {

            let data = &buf.borrow().buffer.data;

            view.screen.clear();

            view.start_offset = 0;

            let mut offset = 0;
            for c in data {

                let mut displayed_cp: char = *c as char;
                if *c as char == '\n' as char {
                    displayed_cp = ' ';
                }

                let cpi = CodepointInfo {
                    cp: *c as char,
                    displayed_cp: displayed_cp,
                    offset: offset,
                    is_selected: false,
                };

                let (ok, _) = view.screen.push(cpi);
                offset += 1;
                if ok == false {
                    break;
                }
            }
            view.end_offset = offset;

    // brute force for now
            let mut screen = &mut view.screen;

            for m in &buf.borrow().moving_marks {

    // TODO: screen.find_line_by_offset(m.offset) -> Option<&mut Line>
                if m.offset >= view.start_offset && m.offset <= view.end_offset {
                    for l in 0..screen.height {
                        let line = screen.get_mut_line(l).unwrap();
                        for c in 0..line.used {
                            let mut cpi = line.get_mut_cpi(c).unwrap();
                            if cpi.offset == m.offset {
                                cpi.is_selected = true;
                            }
                        }
                    }
                }
            }
        }
        None => {}
    }
}

fn draw_screen(screen: &mut Screen, mut stdout: &mut Stdout) {

    write!(stdout, "{}", termion::cursor::Goto(1, 1)).unwrap();

    for l in 0..screen.height {

        terminal_cursor_to(&mut stdout, 1, (l + 1) as u16);

        let line = screen.get_line(l).unwrap();

        for c in 0..line.used {

            let cpi = line.get_cpi(c).unwrap();

            if cpi.is_selected == true {
                write!(stdout, "{}", termion::style::Invert).unwrap();
            }

            write!(stdout, "{}", cpi.displayed_cp).unwrap();
            write!(stdout, "{}", termion::style::Reset).unwrap();
        }

        for _ in line.used..line.width {
            write!(stdout, " ").unwrap();
        }
    }

    stdout.flush().unwrap();
}

/*
    TODO:
    1 : be explicit
    2 : create editor internal result type Result<>
    3 : use idomatic    func()? style
*/
fn draw_view(mut view: &mut View, mut stdout: &mut Stdout) {

    fill_screen(&mut view);
    draw_screen(&mut view.screen, &mut stdout);
}

fn terminal_clear_current_line(mut stdout: &mut Stdout, line_width: u16) {
    for _ in 0..line_width {
        write!(stdout, " ").unwrap();
    }
}

fn terminal_cursor_to(mut stdout: &mut Stdout, x: u16, y: u16) {
    write!(stdout, "{}", termion::cursor::Goto(x, y)).unwrap();
}

fn process_input_events(view_state: &mut UiState) {
    for evt in stdin().events() {

        view_state.keys.push(evt.unwrap());
        let evt = view_state.keys[view_state.keys.len() - 1].clone();

    // Print recieved Events...
        match evt {

            Event::Key(k) => {
                match k {
    // Exit.
                    Key::Ctrl('r') => {
                        view_state.keys.clear();
                    }

                    Key::Ctrl('c') => {
                        if view_state.keys.len() > 1 {
                            if let Event::Key(prev_event) =
                                view_state.keys[view_state.keys.len() - 2] {
                                if let Key::Ctrl(prev_char) = prev_event {
                                    if prev_char == 'x' {
                                        view_state.quit = true;
                                        break;
                                    }
                                }
                            }
                        }
                    }

                    Key::Char(c) => {
                        if c == '\n' {
                            view_state.status = format!("'{}'", "<newline>");
                        } else {
                            view_state.status = format!("'{}'", c);
                        }
                    }
                    Key::Alt(c) => view_state.status = format!("Alt-{}", c),
                    Key::Ctrl(c) => view_state.status = format!("Ctrl-{}", c),

                    Key::F(1) => {
                        if view_state.vid > 0 {
                            view_state.vid -= 1;
                        }
                        break;
                    }

                    Key::F(2) => {
                        view_state.vid = ::std::cmp::min(view_state.vid + 1,
                                                         (view_state.nb_view - 1) as u64);
                        break;
                    }

                    Key::F(f) => view_state.status = format!("F{:?}", f),
                    Key::Left => view_state.status = format!("<left>"),
                    Key::Right => view_state.status = format!("<right>"),
                    Key::Up => view_state.status = format!("<up>"),
                    Key::Down => view_state.status = format!("<down>"),
                    Key::Backspace => view_state.status = format!("<backspace>"),
                    Key::Home => view_state.status = format!("<Home>"),
                    Key::End => view_state.status = format!("<End>"),
                    Key::PageUp => view_state.status = format!("<PageUp>"),
                    Key::PageDown => view_state.status = format!("<PageDown>"),
                    Key::Delete => view_state.status = format!("<Delete>"),
                    Key::Insert => view_state.status = format!("<Insert>"),
                    Key::Esc => view_state.status = format!("<Esc>"),
                    _ => view_state.status = format!("Other"),
                }
            }

            Event::Mouse(m) => {
                match m {
                    MouseEvent::Press(mb, x, y) => {
                        view_state.status =
                            format!("MouseEvent::Press => MouseButton {:?} @ ({}, {})", mb, x, y);
                    }

                    MouseEvent::Release(x, y) => {
                        view_state.status = format!("MouseEvent::Release => @ ({}, {})", x, y);
                    }

                    MouseEvent::Hold(x, y) => {
                        view_state.status = format!("MouseEvent::Hold => @ ({}, {})", x, y);
                    }
                };
            }

            Event::Unsupported(e) => {
                view_state.status = format!("Event::Unsupported {:?}", e);
            }
        }

        break;
    }
}

fn display_status_line(view: &View, status: &str, line: u16, width: u16, mut stdout: &mut Stdout) {

    let doc = match view.document {
        Some(ref d) => d.borrow(),
        None => return,
    };

    let name = doc.name.as_str();
    let file_name = doc.buffer.file_name.as_str();

    // select/clear last line
    terminal_cursor_to(&mut stdout, 1, line);
    terminal_clear_current_line(&mut stdout, width);
    terminal_cursor_to(&mut stdout, 1, line);

    let status_str = format!("line {} document_name '{}', file: '{}', event '{}'",
                             line,
                             name,
                             file_name,
                             status);

    print!("{}", status_str);
    stdout.flush().unwrap();
}
<commit_msg>move cursor to left/right<commit_after>//
extern crate termion;

//
use self::termion::screen::AlternateScreen;
use self::termion::event::{Event, Key, MouseEvent};
use self::termion::input::{TermRead, MouseTerminal};
use self::termion::raw::IntoRawMode;
use self::termion::terminal_size;

use std::io::{self, Write, stdin, Stdout};

//
use core::view::View;
use core::screen::Screen;
use core::codepointinfo::CodepointInfo;

use core::editor::Editor;

//
struct UiState {
    keys: Vec<Event>,
    quit: bool,
    status: String,
    display_status: bool,
    display_view: bool,
    vid: u64,
    nb_view: usize,
}

impl UiState {
    fn new() -> UiState {
        UiState {
            keys: Vec::new(),
            quit: false,
            status: String::new(),
            display_status: true,
            display_view: true,
            vid: 0,
            nb_view: 0,
        }
    }
}

pub fn main_loop(mut editor: &mut Editor) {

    let mut ui_state = UiState::new();

    let (width, height) = terminal_size().unwrap();

    setup_views(editor, width as usize, height as usize);

    //
    let stdout = MouseTerminal::from(io::stdout().into_raw_mode().unwrap());
    let mut stdout = AlternateScreen::from(stdout);

    write!(stdout, "{}{}", termion::cursor::Hide, termion::clear::All).unwrap();
    stdout.flush().unwrap();

    while !ui_state.quit {

        ui_state.nb_view = editor.view_map.len();
        let mut view = editor.view_map.get_mut(&ui_state.vid);

        let status_line_y = height;

        if ui_state.display_view == true {
            draw_view(&mut view.as_mut().unwrap(), &mut stdout);
        }

        if ui_state.display_status == true {
            display_status_line(&mut view.as_mut().unwrap(),
                                &ui_state.status,
                                status_line_y,
                                width,
                                &mut stdout);
        }

        process_input_events(&mut ui_state, &mut view.as_mut().unwrap());
    }

    // quit
    // clear, restore cursor
    write!(stdout, "{}{}", termion::clear::All, termion::cursor::Show).unwrap();
    stdout.flush().unwrap();
}

fn setup_views(editor: &mut Editor, width: usize, height: usize) {

    let mut views = Vec::new();

    let mut vid = 0;

    for (_, b) in &editor.document_map {

        let view = View::new(vid,
                             0 as u64,
                             width as usize,
                             height as usize,
                             Some(b.clone()));
        views.push(view);
        vid += 1;
    }

    for view in views {
        &editor.view_map.insert(view.id, Box::new(view));
    }
}

fn fill_screen(view: &mut View) {

    match view.document {

        Some(ref buf) => {

            view.screen.clear();

            let mut offset = view.start_offset;

            let data = &buf.borrow().buffer.data;
            for c in data {

                let mut displayed_cp: char = *c as char;
                if *c as char == '\n' as char {
                    displayed_cp = ' ';
                }

                let cpi = CodepointInfo {
                    cp: *c as char,
                    displayed_cp: displayed_cp,
                    offset: offset,
                    is_selected: false,
                };

                let (ok, _) = view.screen.push(cpi);
                offset += 1;
                if ok == false {
                    break;
                }
            }
            view.end_offset = offset;

            // brute force for now
            let mut screen = &mut view.screen;

            for m in &buf.borrow().moving_marks {

                // TODO: screen.find_line_by_offset(m.offset) -> Option<&mut Line>
                if m.offset >= view.start_offset && m.offset <= view.end_offset {
                    for l in 0..screen.height {
                        let line = screen.get_mut_line(l).unwrap();
                        for c in 0..line.used {
                            let mut cpi = line.get_mut_cpi(c).unwrap();
                            if cpi.offset == m.offset {
                                cpi.is_selected = true;
                            }
                        }
                    }
                }
            }
        }
        None => {}
    }
}

fn draw_screen(screen: &mut Screen, mut stdout: &mut Stdout) {

    write!(stdout, "{}", termion::cursor::Goto(1, 1)).unwrap();

    for l in 0..screen.height {

        terminal_cursor_to(&mut stdout, 1, (l + 1) as u16);

        let line = screen.get_line(l).unwrap();

        for c in 0..line.used {

            let cpi = line.get_cpi(c).unwrap();

            if cpi.is_selected == true {
                write!(stdout, "{}", termion::style::Invert).unwrap();
            }

            write!(stdout, "{}", cpi.displayed_cp).unwrap();
            write!(stdout, "{}", termion::style::Reset).unwrap();
        }

        for _ in line.used..line.width {
            write!(stdout, " ").unwrap();
        }
    }

    stdout.flush().unwrap();
}

/*
    TODO:
    1 : be explicit
    2 : create editor internal result type Result<>
    3 : use idomatic    func()? style
*/
fn draw_view(mut view: &mut View, mut stdout: &mut Stdout) {

    fill_screen(&mut view);
    draw_screen(&mut view.screen, &mut stdout);
}

fn terminal_clear_current_line(mut stdout: &mut Stdout, line_width: u16) {
    for _ in 0..line_width {
        write!(stdout, " ").unwrap();
    }
}

fn terminal_cursor_to(mut stdout: &mut Stdout, x: u16, y: u16) {
    write!(stdout, "{}", termion::cursor::Goto(x, y)).unwrap();
}

fn process_input_events(ui_state: &mut UiState, view: &mut View) {
    for evt in stdin().events() {

        ui_state.keys.push(evt.unwrap());
        let evt = ui_state.keys[ui_state.keys.len() - 1].clone();

        // Print recieved Events...
        match evt {

            Event::Key(k) => {
                match k {
                    // Exit.
                    Key::Ctrl('r') => {
                        ui_state.keys.clear();
                    }

                    Key::Ctrl('c') => {
                        if ui_state.keys.len() > 1 {
                            if let Event::Key(prev_event) = ui_state.keys[ui_state.keys.len() - 2] {
                                if let Key::Ctrl(prev_char) = prev_event {
                                    if prev_char == 'x' {
                                        ui_state.quit = true;
                                        break;
                                    }
                                }
                            }
                        }
                    }

                    Key::Char(c) => {
                        if c == '\n' {
                            ui_state.status = format!("'{}'", "<newline>");
                        } else {
                            ui_state.status = format!("'{}'", c);
                        }
                    }
                    Key::Alt(c) => ui_state.status = format!("Alt-{}", c),
                    Key::Ctrl(c) => ui_state.status = format!("Ctrl-{}", c),

                    Key::F(1) => {
                        if ui_state.vid > 0 {
                            ui_state.vid -= 1;
                        }
                        break;
                    }

                    Key::F(2) => {
                        ui_state.vid = ::std::cmp::min(ui_state.vid + 1,
                                                       (ui_state.nb_view - 1) as u64);
                        break;
                    }

                    Key::F(f) => ui_state.status = format!("F{:?}", f),
                    Key::Left => {
                        ui_state.status = {
                            let mut doc = view.document.as_mut().unwrap().borrow_mut();
                            for m in &mut doc.moving_marks {
                                if m.offset > 0 {
                                    m.offset -= 1;
                                }
                            }
                            format!("<left>")
                        }
                    }
                    Key::Right => {
                        ui_state.status = {
                            let mut doc = view.document.as_mut().unwrap().borrow_mut();
                            let buffer_size = doc.buffer.size as u64;

                            for m in &mut doc.moving_marks {
                                m.offset += 1;
                                if m.offset > buffer_size {
                                    m.offset = buffer_size
                                }
                            }

                            format!("<right>")
                        }
                    }
                    Key::Up => ui_state.status = format!("<up>"),
                    Key::Down => ui_state.status = format!("<down>"),
                    Key::Backspace => ui_state.status = format!("<backspace>"),
                    Key::Home => ui_state.status = format!("<Home>"),
                    Key::End => ui_state.status = format!("<End>"),
                    Key::PageUp => ui_state.status = format!("<PageUp>"),
                    Key::PageDown => ui_state.status = format!("<PageDown>"),
                    Key::Delete => ui_state.status = format!("<Delete>"),
                    Key::Insert => ui_state.status = format!("<Insert>"),
                    Key::Esc => ui_state.status = format!("<Esc>"),
                    _ => ui_state.status = format!("Other"),
                }
            }

            Event::Mouse(m) => {
                match m {
                    MouseEvent::Press(mb, x, y) => {
                        ui_state.status =
                            format!("MouseEvent::Press => MouseButton {:?} @ ({}, {})", mb, x, y);
                    }

                    MouseEvent::Release(x, y) => {
                        ui_state.status = format!("MouseEvent::Release => @ ({}, {})", x, y);
                    }

                    MouseEvent::Hold(x, y) => {
                        ui_state.status = format!("MouseEvent::Hold => @ ({}, {})", x, y);
                    }
                };
            }

            Event::Unsupported(e) => {
                ui_state.status = format!("Event::Unsupported {:?}", e);
            }
        }

        break;
    }
}

fn display_status_line(view: &View, status: &str, line: u16, width: u16, mut stdout: &mut Stdout) {

    let doc = match view.document {
        Some(ref d) => d.borrow(),
        None => return,
    };

    let name = doc.name.as_str();
    let file_name = doc.buffer.file_name.as_str();

    // select/clear last line
    terminal_cursor_to(&mut stdout, 1, line);
    terminal_clear_current_line(&mut stdout, width);
    terminal_cursor_to(&mut stdout, 1, line);

    let status_str = format!("line {} document_name '{}', file: '{}', event '{}'",
                             line,
                             name,
                             file_name,
                             status);

    print!("{}", status_str);
    stdout.flush().unwrap();
}
<|endoftext|>


<commit_before>use event::Event;
use top_level_event::TopLevelEvent;
use sass::rule::SassRule;
use sass::variable::SassVariable;
use std::collections::HashMap;

pub struct VariableMapper<I> {
    tokenizer: I,
    variables: HashMap<String, String>,
}

impl<I> VariableMapper<I> {
    pub fn new(tokenizer: I) -> VariableMapper<I> {
        VariableMapper {
            tokenizer: tokenizer,
            variables: HashMap::new(),
        }
    }

    fn substitute_variables(&self, value: &str) -> String {
        value.split(' ').map(|value_part|
            match self.variables.get(value_part) {
                Some(v) => &v[..],
                None => value_part,
            }
        ).collect::<Vec<_>>().connect(" ")
    }

    fn replace_children_in_scope<'b>(&self, children: Vec<Event<'b>>, starting_variables: &HashMap<String, String>) -> Vec<Event<'b>> {
        let mut local_variables = starting_variables.clone();

        children.into_iter().filter_map(|c|
            match c {
                Event::Variable(SassVariable { name, value }) => {
                    let val = self.substitute_variables_using_locals(&value, &local_variables);
                    local_variables.insert((*name).to_string(), val);
                    None
                },
                Event::Property(name, value) => {
                    Some(Event::Property(name, self.substitute_variables_using_locals(&value, &local_variables).into()))
                },
                Event::ChildRule(rule) => {
                    Some(Event::ChildRule(SassRule {
                        children: self.replace_children_in_scope(rule.children, &local_variables), ..rule
                    }))
                },
                other => Some(other)
            }
        ).collect::<Vec<_>>()
    }

    fn substitute_variables_using_locals(&self, value: &str, local_variables: &HashMap<String, String>) -> String {
        value.split(' ').map(|value_part|
            match (*local_variables).get(value_part) {
                Some(v) => &v[..],
                None => value_part,
            }
        ).collect::<Vec<_>>().connect(" ")
    }
}

impl<'a, I> Iterator for VariableMapper<I>
    where I: Iterator<Item = TopLevelEvent<'a>>
{
    type Item = TopLevelEvent<'a>;

    fn next(&mut self) -> Option<TopLevelEvent<'a>> {
        match self.tokenizer.next() {
            Some(TopLevelEvent::Variable(SassVariable { name, value })) => {
                let val = self.substitute_variables(&value);
                self.variables.insert((*name).to_string(), val);
                self.next()
            },
            Some(TopLevelEvent::Rule(sass_rule)) => {
                Some(TopLevelEvent::Rule(SassRule {
                    children: self.replace_children_in_scope(sass_rule.children, &self.variables), ..sass_rule
                }))
            },
            other => other,
        }
    }
}
<commit_msg>Who duplicated these functions they're exactly the same<commit_after>use event::Event;
use top_level_event::TopLevelEvent;
use sass::rule::SassRule;
use sass::variable::SassVariable;
use std::collections::HashMap;

pub struct VariableMapper<I> {
    tokenizer: I,
    variables: HashMap<String, String>,
}

impl<I> VariableMapper<I> {
    pub fn new(tokenizer: I) -> VariableMapper<I> {
        VariableMapper {
            tokenizer: tokenizer,
            variables: HashMap::new(),
        }
    }

    fn replace_children_in_scope<'b>(&self, children: Vec<Event<'b>>, starting_variables: &HashMap<String, String>) -> Vec<Event<'b>> {
        let mut local_variables = starting_variables.clone();

        children.into_iter().filter_map(|c|
            match c {
                Event::Variable(SassVariable { name, value }) => {
                    let val = self.substitute_variables_using_locals(&value, &local_variables);
                    local_variables.insert((*name).to_string(), val);
                    None
                },
                Event::Property(name, value) => {
                    Some(Event::Property(name, self.substitute_variables_using_locals(&value, &local_variables).into()))
                },
                Event::ChildRule(rule) => {
                    Some(Event::ChildRule(SassRule {
                        children: self.replace_children_in_scope(rule.children, &local_variables), ..rule
                    }))
                },
                other => Some(other)
            }
        ).collect::<Vec<_>>()
    }

    fn substitute_variables_using_locals(&self, value: &str, local_variables: &HashMap<String, String>) -> String {
        value.split(' ').map(|value_part|
            match (*local_variables).get(value_part) {
                Some(v) => &v[..],
                None => value_part,
            }
        ).collect::<Vec<_>>().connect(" ")
    }
}

impl<'a, I> Iterator for VariableMapper<I>
    where I: Iterator<Item = TopLevelEvent<'a>>
{
    type Item = TopLevelEvent<'a>;

    fn next(&mut self) -> Option<TopLevelEvent<'a>> {
        match self.tokenizer.next() {
            Some(TopLevelEvent::Variable(SassVariable { name, value })) => {
                let val = self.substitute_variables_using_locals(&value, &self.variables);
                self.variables.insert((*name).to_string(), val);
                self.next()
            },
            Some(TopLevelEvent::Rule(sass_rule)) => {
                Some(TopLevelEvent::Rule(SassRule {
                    children: self.replace_children_in_scope(sass_rule.children, &self.variables), ..sass_rule
                }))
            },
            other => other,
        }
    }
}
<|endoftext|>


<commit_before>use std::io::Read;
use std::fs::File;
use std::path::Path;

use shaman::sha2::Sha256;
use shaman::digest::Digest as ShamanDigest;

use config::{Config, Container};
use super::error::Error;
use build_step::{Step, BuildStep, Digest};

fn all(setup: &[Step], cfg: &Config)
    -> Result<Sha256, (String, Error)>
{
    debug!("Versioning items: {}", setup.len());

    let mut hash = Digest::new();

    let mut buf = Vec::with_capacity(1000);
    File::open(&Path::new("/proc/self/uid_map"))
               .and_then(|mut f| f.read_to_end(&mut buf))
               .ok().expect("Can't read uid_map");
    hash.field("uid_map", &buf);

    let mut buf = Vec::with_capacity(1000);
    File::open(&Path::new("/proc/self/gid_map"))
               .and_then(|mut f| f.read_to_end(&mut buf))
               .ok().expect("Can't read gid_map");
    hash.field("gid_map", &buf);

    for b in setup.iter() {
        debug!("Versioning setup: {:?}", b);
        try!(b.hash(&cfg, &mut hash).map_err(|e| (format!("{:?}", b), e)));
    }

    Ok(hash.unwrap())
}

pub fn short_version(container: &Container, cfg: &Config)
    -> Result<String, (String, Error)>
{
    let mut hash = try!(all(&container.setup, cfg));
    Ok(hash.result_str()[..8].to_string())
}

pub fn long_version(container: &Container, cfg: &Config)
    -> Result<String, (String, Error)>
{
    let mut hash = try!(all(&container.setup, cfg));
    Ok(hash.result_str())
}
<commit_msg>WIP: Do not use uid/gid mappings in build, only configured range<commit_after>use shaman::sha2::Sha256;
use shaman::digest::Digest as ShamanDigest;

use config::{Config, Container};
use super::error::Error;
use build_step::{BuildStep, Digest};

fn all(container: &Container, cfg: &Config)
    -> Result<Sha256, (String, Error)>
{
    debug!("Versioning items: {}", container.setup.len());

    let mut hash = Digest::new();

    hash.item("uids");
    for i in &container.uids {
        hash.item(&format!("{}-{}", i.start(), i.end()));
    }
    hash.item("gids");
    for i in &container.gids {
        hash.item(&format!("{}-{}", i.start(), i.end()));
    }

    for b in container.setup.iter() {
        debug!("Versioning setup: {:?}", b);
        try!(b.hash(&cfg, &mut hash).map_err(|e| (format!("{:?}", b), e)));
    }

    Ok(hash.unwrap())
}

pub fn short_version(container: &Container, cfg: &Config)
    -> Result<String, (String, Error)>
{
    let mut hash = try!(all(container, cfg));
    Ok(hash.result_str()[..8].to_string())
}

pub fn long_version(container: &Container, cfg: &Config)
    -> Result<String, (String, Error)>
{
    let mut hash = try!(all(&container, cfg));
    Ok(hash.result_str())
}
<|endoftext|>


<commit_before>use error::Result;
use postgres::Connection;
use std::collections::BTreeMap;
use std::time::Duration;

pub(crate) struct Limits {
    memory: usize,
    targets: usize,
    timeout: Duration,
    networking: bool,
    max_log_size: usize,
}

impl Default for Limits {
    fn default() -> Self {
        Self {
            memory: 3 * 1024 * 1024 * 1024,  // 3 GB
            timeout: Duration::from_secs(15 * 60),  // 15 minutes
            targets: 10,
            networking: false,
            max_log_size: 100 * 1024, // 100 KB
        }
    }
}

impl Limits {
    pub(crate) fn for_crate(conn: &Connection, name: &str) -> Result<Self> {
        let mut limits = Self::default();

        let res = conn.query(
            "SELECT * FROM sandbox_overrides WHERE crate_name = $1;",
            &[&name],
        )?;
        if !res.is_empty() {
            let row = res.get(0);
            if let Some(memory) = row.get::<_, Option<i64>>("max_memory_bytes") {
                limits.memory = memory as usize;
            }
            if let Some(timeout) = row.get::<_, Option<i32>>("timeout_seconds") {
                limits.timeout = Duration::from_secs(timeout as u64);
            }
            if let Some(targets) = row.get::<_, Option<i32>>("max_targets") {
                limits.targets = targets as usize;
            }
        }

        Ok(limits)
    }

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

    pub(crate) fn timeout(&self) -> Duration {
        self.timeout
    }

    pub(crate) fn networking(&self) -> bool {
        self.networking
    }

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

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

    pub(crate) fn for_website(&self) -> BTreeMap<String, String> {
        let time_scale = |v| scale(v, 60, &["seconds", "minutes", "hours"]);
        let size_scale = |v| scale(v, 1024, &["bytes", "KB", "MB", "GB"]);

        let mut res = BTreeMap::new();
        res.insert("Available RAM".into(), size_scale(self.memory));
        res.insert(
            "Maximum rustdoc execution time".into(),
            time_scale(self.timeout.as_secs() as usize),
        );
        res.insert("Maximum size of a build log".into(), size_scale(self.max_log_size));
        if self.networking {
            res.insert("Network access".into(), "allowed".into());
        } else {
            res.insert("Network access".into(), "blocked".into());
        }
        res.insert("Maximum number of build targets".into(), self.targets.to_string());
        res
    }
}

fn scale(mut value: usize, interval: usize, labels: &[&str]) -> String {
    let mut chosen_label = &labels[0];
    for label in &labels[1..] {
        if (value as f64) / (interval as f64) >= 1.0 {
            chosen_label = label;
            value = value / interval;
        } else {
            break;
        }
    }
    format!("{} {}", value, chosen_label)
}
<commit_msg>Add test for Limits<commit_after>use error::Result;
use postgres::Connection;
use std::collections::BTreeMap;
use std::time::Duration;

#[derive(Debug, PartialEq)]
pub(crate) struct Limits {
    memory: usize,
    targets: usize,
    timeout: Duration,
    networking: bool,
    max_log_size: usize,
}

impl Default for Limits {
    fn default() -> Self {
        Self {
            memory: 3 * 1024 * 1024 * 1024,  // 3 GB
            timeout: Duration::from_secs(15 * 60),  // 15 minutes
            targets: 10,
            networking: false,
            max_log_size: 100 * 1024, // 100 KB
        }
    }
}

impl Limits {
    pub(crate) fn for_crate(conn: &Connection, name: &str) -> Result<Self> {
        let mut limits = Self::default();

        let res = conn.query(
            "SELECT * FROM sandbox_overrides WHERE crate_name = $1;",
            &[&name],
        )?;
        if !res.is_empty() {
            let row = res.get(0);
            if let Some(memory) = row.get::<_, Option<i64>>("max_memory_bytes") {
                limits.memory = memory as usize;
            }
            if let Some(timeout) = row.get::<_, Option<i32>>("timeout_seconds") {
                limits.timeout = Duration::from_secs(timeout as u64);
            }
            if let Some(targets) = row.get::<_, Option<i32>>("max_targets") {
                limits.targets = targets as usize;
            }
        }

        Ok(limits)
    }

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

    pub(crate) fn timeout(&self) -> Duration {
        self.timeout
    }

    pub(crate) fn networking(&self) -> bool {
        self.networking
    }

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

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

    pub(crate) fn for_website(&self) -> BTreeMap<String, String> {
        let time_scale = |v| scale(v, 60, &["seconds", "minutes", "hours"]);
        let size_scale = |v| scale(v, 1024, &["bytes", "KB", "MB", "GB"]);

        let mut res = BTreeMap::new();
        res.insert("Available RAM".into(), size_scale(self.memory));
        res.insert(
            "Maximum rustdoc execution time".into(),
            time_scale(self.timeout.as_secs() as usize),
        );
        res.insert("Maximum size of a build log".into(), size_scale(self.max_log_size));
        if self.networking {
            res.insert("Network access".into(), "allowed".into());
        } else {
            res.insert("Network access".into(), "blocked".into());
        }
        res.insert("Maximum number of build targets".into(), self.targets.to_string());
        res
    }
}

fn scale(mut value: usize, interval: usize, labels: &[&str]) -> String {
    let mut chosen_label = &labels[0];
    for label in &labels[1..] {
        if (value as f64) / (interval as f64) >= 1.0 {
            chosen_label = label;
            value = value / interval;
        } else {
            break;
        }
    }
    format!("{} {}", value, chosen_label)
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::test::*;
    #[test]
    fn retrieve_limits() {
        wrapper(|env| {
            let db = env.db();

            let krate = "hexponent";
            // limits work if no crate has limits set
            let hexponent = Limits::for_crate(&db.conn(), krate)?;
            assert_eq!(hexponent, Limits::default());

            db.conn().query("INSERT INTO sandbox_overrides (crate_name, max_targets) VALUES ($1, 15)", &[&krate])?;
            // limits work if crate has limits set
            let hexponent = Limits::for_crate(&db.conn(), krate)?;
            assert_eq!(hexponent, Limits { targets: 15, ..Limits::default() });

            // all limits work
            let krate = "regex";
            let limits = Limits {
                memory: 100_000,
                timeout: Duration::from_secs(300),
                targets: 1,
                ..Limits::default()
            };
            db.conn().query("INSERT INTO sandbox_overrides (crate_name, max_memory_bytes, timeout_seconds, max_targets)
                                    VALUES ($1, $2, $3, $4)",
                                    &[&krate, &(limits.memory as i64), &(limits.timeout.as_secs() as i32), &(limits.targets as i32)])?;
            assert_eq!(limits, Limits::for_crate(&db.conn(), krate)?);
            Ok(())
        });
    }
}
<|endoftext|>


<commit_before>use Printer;
use With;
use XY;
use align::*;
use direction::Direction;
use event::*;
use owning_ref::{ArcRef, OwningHandle};
use std::ops::Deref;
use std::sync::{Mutex, MutexGuard};
use std::sync::Arc;
use unicode_width::UnicodeWidthStr;
use utils::{LinesIterator, Row};
use vec::Vec2;
use view::{ScrollBase, ScrollStrategy, SizeCache, View};

/// Provides access to the content of a [`TextView`].
///
/// Cloning this object will still point to the same content.
///
/// [`TextView`]: struct.TextView.html
///
/// # Examples
///
/// ```rust
/// # use cursive::views::{TextView, TextContent};
/// let mut content = TextContent::new("content");
/// let view = TextView::new_with_content(content.clone());
///
/// // Later, possibly in a different thread
/// content.set_content("new content");
/// assert!(content.get_content().contains("new"));
/// ```
#[derive(Clone)]
pub struct TextContent {
    content: Arc<Mutex<TextContentInner>>,
}

impl TextContent {
    /// Creates a new text content around the given value.
    pub fn new<S: Into<String>>(content: S) -> Self {
        TextContent {
            content: Arc::new(Mutex::new(TextContentInner {
                content: content.into(),
                size_cache: None,
            })),
        }
    }
}

/// A reference to the text content.
///
/// It implements `Deref<Target=str>`.
///
/// This keeps the content locked. Do not store this!
pub struct TextContentRef {
    handle: OwningHandle<
        ArcRef<Mutex<TextContentInner>>,
        MutexGuard<'static, TextContentInner>,
    >,
}

impl Deref for TextContentRef {
    type Target = str;

    fn deref(&self) -> &str {
        &self.handle.content
    }
}

impl TextContent {
    /// Replaces the content with the given value.
    pub fn set_content<S: Into<String>>(&mut self, content: S) {
        self.with_content(|c| *c = content.into());
    }

    /// Append `content` to the end of a `TextView`.
    pub fn append_content(&mut self, content: &str) {
        self.with_content(|c| c.push_str(content));
    }

    /// Returns a reference to the content.
    ///
    /// This locks the data while the returned value is alive,
    /// so don't keep it too long.
    pub fn get_content(&self) -> TextContentRef {
        TextContentInner::get_content(&self.content)
    }

    fn with_content<F>(&mut self, f: F)
    where
        F: FnOnce(&mut String),
    {
        let mut lock = self.content.lock().unwrap();

        f(&mut lock.content);

        lock.size_cache = None;
    }
}

struct TextContentInner {
    content: String,
    size_cache: Option<XY<SizeCache>>,
}

impl TextContentInner {
    fn get_content(content: &Arc<Mutex<TextContentInner>>) -> TextContentRef {
        let arc_ref: ArcRef<Mutex<TextContentInner>> =
            ArcRef::new(Arc::clone(content));

        TextContentRef {
            handle: OwningHandle::new_with_fn(arc_ref, |mutex| unsafe {
                (*mutex).lock().unwrap()
            }),
        }
    }
}

/// A simple view showing a fixed text.
///
/// # Examples
///
/// ```rust
/// # use cursive::Cursive;
/// # use cursive::views::TextView;
/// let mut siv = Cursive::new();
///
/// siv.add_layer(TextView::new("Hello world!"));
/// ```
pub struct TextView {
    // content: String,
    content: Arc<Mutex<TextContentInner>>,
    rows: Vec<Row>,

    align: Align,

    // If `false`, disable scrolling.
    scrollable: bool,

    // ScrollBase make many scrolling-related things easier
    scrollbase: ScrollBase,
    scroll_strategy: ScrollStrategy,
    last_size: Vec2,
    width: Option<usize>,
}

// If the last character is a newline, strip it.
fn strip_last_newline(content: &str) -> &str {
    if content.ends_with('\n') {
        &content[..content.len() - 1]
    } else {
        content
    }
}

impl TextView {
    /// Creates a new TextView with the given content.
    pub fn new<S: Into<String>>(content: S) -> Self {
        TextView::new_with_content(TextContent::new(content))
    }

    /// Creates a new TextView using the given `Arc<Mutex<String>>`.
    ///
    /// If you kept a clone of the given content, you'll be able to update it
    /// remotely.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use cursive::views::{TextView, TextContent};
    /// let mut content = TextContent::new("content");
    /// let view = TextView::new_with_content(content.clone());
    ///
    /// // Later, possibly in a different thread
    /// content.set_content("new content");
    /// assert!(content.get_content().contains("new"));
    /// ```
    pub fn new_with_content(content: TextContent) -> Self {
        TextView {
            content: content.content,
            rows: Vec::new(),
            scrollable: true,
            scrollbase: ScrollBase::new(),
            scroll_strategy: ScrollStrategy::KeepRow,
            align: Align::top_left(),
            last_size: Vec2::zero(),
            width: None,
        }
    }

    /// Creates a new empty `TextView`.
    pub fn empty() -> Self {
        TextView::new("")
    }

    /// Enable or disable the view's scrolling capabilities.
    ///
    /// When disabled, the view will never attempt to scroll
    /// (and will always ask for the full height).
    pub fn set_scrollable(&mut self, scrollable: bool) {
        self.scrollable = scrollable;
    }

    /// Enable or disable the view's scrolling capabilities.
    ///
    /// When disabled, the view will never attempt to scroll
    /// (and will always ask for the full height).
    ///
    /// Chainable variant.
    pub fn scrollable(self, scrollable: bool) -> Self {
        self.with(|s| s.set_scrollable(scrollable))
    }

    /// Sets the horizontal alignment for this view.
    pub fn h_align(mut self, h: HAlign) -> Self {
        self.align.h = h;

        self
    }

    /// Sets the vertical alignment for this view.
    pub fn v_align(mut self, v: VAlign) -> Self {
        self.align.v = v;

        self
    }

    /// Sets the alignment for this view.
    pub fn align(mut self, a: Align) -> Self {
        self.align = a;

        self
    }

    /// Center the text horizontally and vertically inside the view.
    pub fn center(mut self) -> Self {
        self.align = Align::center();
        self
    }

    /// Replace the text in this view.
    ///
    /// Chainable variant.
    pub fn content<S: Into<String>>(self, content: S) -> Self {
        self.with(|s| s.set_content(content))
    }

    /// Replace the text in this view.
    pub fn set_content<S: Into<String>>(&mut self, content: S) {
        let content = content.into();
        self.content.lock().unwrap().content = content;
        self.invalidate();
    }

    /// Append `content` to the end of a `TextView`.
    pub fn append_content(&mut self, content: &str) {
        self.content.lock().unwrap().content.push_str(content);
        self.invalidate();
    }

    /// Returns the current text in this view.
    pub fn get_content(&self) -> TextContentRef {
        TextContentInner::get_content(&self.content)
    }

    /// Returns a shared reference to the content, allowing content mutation.
    pub fn get_shared_content(&mut self) -> TextContent {
        // We take &mut here without really needing it,
        // because it sort of "makes sense".

        TextContent {
            content: Arc::clone(&self.content),
        }
    }

    fn is_cache_valid(&self, size: Vec2) -> bool {
        let content = self.content.lock().unwrap();

        match content.size_cache {
            None => false,
            Some(ref last) => last.x.accept(size.x) && last.y.accept(size.y),
        }
    }

    /// Defines the way scrolling is adjusted on content or size change.
    ///
    /// The scroll strategy defines how the scrolling position is adjusted
    /// when the size of the view or the content change.
    ///
    /// It is reset to `ScrollStrategy::KeepRow` whenever the user scrolls
    /// manually.
    pub fn set_scroll_strategy(&mut self, strategy: ScrollStrategy) {
        self.scroll_strategy = strategy;
        self.adjust_scroll();
    }

    /// Defines the way scrolling is adjusted on content or size change.
    ///
    /// Chainable variant.
    pub fn scroll_strategy(self, strategy: ScrollStrategy) -> Self {
        self.with(|s| s.set_scroll_strategy(strategy))
    }

    // Apply the scrolling strategy to the current scroll position.
    //
    // Called when computing rows and when applying a new strategy.
    fn adjust_scroll(&mut self) {
        match self.scroll_strategy {
            ScrollStrategy::StickToTop => self.scrollbase.scroll_top(),
            ScrollStrategy::StickToBottom => self.scrollbase.scroll_bottom(),
            ScrollStrategy::KeepRow => (),
        };
    }

    // This must be non-destructive, as it may be called
    // multiple times during layout.
    fn compute_rows(&mut self, size: Vec2) {
        let mut content = self.content.lock().unwrap();
        if self.is_cache_valid(size) {
            return;
        }

        // Completely bust the cache
        // Just in case we fail, we don't want to leave a bad cache.
        content.size_cache = None;

        if size.x == 0 {
            // Nothing we can do at this point.
            return;
        }

        // First attempt: naively hope that we won't need a scrollbar_width
        // (This means we try to use the entire available width for text).
        self.rows =
            LinesIterator::new(strip_last_newline(&content.content), size.x)
                .collect();

        // Width taken by the scrollbar. Without a scrollbar, it's 0.
        let mut scrollbar_width = 0;

        if self.scrollable && self.rows.len() > size.y {
            // We take 1 column for the bar itself + 1 spacing column
            scrollbar_width = 2;

            // If we're too high, include a scrollbar_width
            let available = match size.x.checked_sub(scrollbar_width) {
                Some(s) => s,
                None => return,
            };

            self.rows =
                LinesIterator::new(&content.content, available).collect();

            if self.rows.is_empty() && !content.content.is_empty() {
                // We have some content, we we didn't find any row for it?
                // This probably means we couldn't even make a single row
                // (for instance we only have 1 column and we have a wide
                // character).
                return;
            }
        }

        // Desired width, including the scrollbar_width.
        self.width = self.rows
            .iter()
            .map(|row| row.width)
            .max()
            .map(|w| w + scrollbar_width);

        // The entire "virtual" size (includes all rows)
        let mut my_size = Vec2::new(self.width.unwrap_or(0), self.rows.len());

        // If we're scrolling, cap the the available size.
        if self.scrollable && my_size.y > size.y {
            my_size.y = size.y;
        }

        // Build a fresh cache.
        content.size_cache = Some(SizeCache::build(my_size, size));
    }

    // Invalidates the cache, so next call will recompute everything.
    fn invalidate(&mut self) {
        let mut content = self.content.lock().unwrap();
        content.size_cache = None;
    }
}

impl View for TextView {
    fn draw(&self, printer: &Printer) {
        let h = self.rows.len();
        // If the content is smaller than the view, align it somewhere.
        let offset = self.align.v.get_offset(h, printer.size.y);
        let printer = &printer.offset((0, offset), true);

        let content = self.content.lock().unwrap();

        self.scrollbase.draw(printer, |printer, i| {
            let row = &self.rows[i];
            let text = &content.content[row.start..row.end];
            let l = text.width();
            let x = self.align.h.get_offset(l, printer.size.x);
            printer.print((x, 0), text);
        });
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        if !self.scrollbase.scrollable() {
            return EventResult::Ignored;
        }

        // We have a scrollbar, otherwise the event would just be ignored.
        match event {
            Event::Key(Key::Home) => self.scrollbase.scroll_top(),
            Event::Key(Key::End) => self.scrollbase.scroll_bottom(),
            Event::Key(Key::Up) if self.scrollbase.can_scroll_up() => {
                self.scrollbase.scroll_up(1)
            }
            Event::Key(Key::Down) if self.scrollbase.can_scroll_down() => {
                self.scrollbase.scroll_down(1)
            }
            Event::Mouse {
                event: MouseEvent::WheelDown,
                ..
            } if self.scrollbase.can_scroll_down() =>
            {
                self.scrollbase.scroll_down(5);
            }
            Event::Mouse {
                event: MouseEvent::WheelUp,
                ..
            } if self.scrollbase.can_scroll_up() =>
            {
                self.scrollbase.scroll_up(5);
            }
            Event::Mouse {
                event: MouseEvent::Press(MouseButton::Left),
                position,
                offset,
            } if position
                .checked_sub(offset)
                .map(|position| {
                    self.scrollbase.start_drag(position, self.last_size.x)
                })
                .unwrap_or(false) =>
            {
                // Only consume the event if the mouse hits the scrollbar.
                // Start scroll drag at the given position.
            }
            Event::Mouse {
                event: MouseEvent::Hold(MouseButton::Left),
                position,
                offset,
            } => {
                // If the mouse is dragged, we always consume the event.
                let position = position.saturating_sub(offset);
                self.scrollbase.drag(position);
            }
            Event::Mouse {
                event: MouseEvent::Release(MouseButton::Left),
                ..
            } => {
                self.scrollbase.release_grab();
            }
            Event::Key(Key::PageDown) => self.scrollbase.scroll_down(10),
            Event::Key(Key::PageUp) => self.scrollbase.scroll_up(10),
            _ => return EventResult::Ignored,
        }

        // We just scrolled manually, so reset the scroll strategy.
        self.scroll_strategy = ScrollStrategy::KeepRow;
        EventResult::Consumed(None)
    }

    fn needs_relayout(&self) -> bool {
        let content = self.content.lock().unwrap();
        content.size_cache.is_none()
    }

    fn required_size(&mut self, size: Vec2) -> Vec2 {
        self.compute_rows(size);

        // This is what we'd like
        let mut ideal = Vec2::new(self.width.unwrap_or(0), self.rows.len());

        if self.scrollable && ideal.y > size.y {
            ideal.y = size.y;
        }

        ideal
    }

    fn take_focus(&mut self, _: Direction) -> bool {
        self.scrollbase.scrollable()
    }

    fn layout(&mut self, size: Vec2) {
        // Compute the text rows.
        self.last_size = size;
        self.compute_rows(size);
        // Adjust scrolling, in case we're sticking to the bottom for instance.
        self.scrollbase.set_heights(size.y, self.rows.len());
        self.adjust_scroll();
    }
}
<commit_msg>Don't run test examples when we call Cursive::new<commit_after>use Printer;
use With;
use XY;
use align::*;
use direction::Direction;
use event::*;
use owning_ref::{ArcRef, OwningHandle};
use std::ops::Deref;
use std::sync::{Mutex, MutexGuard};
use std::sync::Arc;
use unicode_width::UnicodeWidthStr;
use utils::{LinesIterator, Row};
use vec::Vec2;
use view::{ScrollBase, ScrollStrategy, SizeCache, View};

/// Provides access to the content of a [`TextView`].
///
/// Cloning this object will still point to the same content.
///
/// [`TextView`]: struct.TextView.html
///
/// # Examples
///
/// ```rust
/// # use cursive::views::{TextView, TextContent};
/// let mut content = TextContent::new("content");
/// let view = TextView::new_with_content(content.clone());
///
/// // Later, possibly in a different thread
/// content.set_content("new content");
/// assert!(content.get_content().contains("new"));
/// ```
#[derive(Clone)]
pub struct TextContent {
    content: Arc<Mutex<TextContentInner>>,
}

impl TextContent {
    /// Creates a new text content around the given value.
    pub fn new<S: Into<String>>(content: S) -> Self {
        TextContent {
            content: Arc::new(Mutex::new(TextContentInner {
                content: content.into(),
                size_cache: None,
            })),
        }
    }
}

/// A reference to the text content.
///
/// It implements `Deref<Target=str>`.
///
/// This keeps the content locked. Do not store this!
pub struct TextContentRef {
    handle: OwningHandle<
        ArcRef<Mutex<TextContentInner>>,
        MutexGuard<'static, TextContentInner>,
    >,
}

impl Deref for TextContentRef {
    type Target = str;

    fn deref(&self) -> &str {
        &self.handle.content
    }
}

impl TextContent {
    /// Replaces the content with the given value.
    pub fn set_content<S: Into<String>>(&mut self, content: S) {
        self.with_content(|c| *c = content.into());
    }

    /// Append `content` to the end of a `TextView`.
    pub fn append_content(&mut self, content: &str) {
        self.with_content(|c| c.push_str(content));
    }

    /// Returns a reference to the content.
    ///
    /// This locks the data while the returned value is alive,
    /// so don't keep it too long.
    pub fn get_content(&self) -> TextContentRef {
        TextContentInner::get_content(&self.content)
    }

    fn with_content<F>(&mut self, f: F)
    where
        F: FnOnce(&mut String),
    {
        let mut lock = self.content.lock().unwrap();

        f(&mut lock.content);

        lock.size_cache = None;
    }
}

struct TextContentInner {
    content: String,
    size_cache: Option<XY<SizeCache>>,
}

impl TextContentInner {
    fn get_content(content: &Arc<Mutex<TextContentInner>>) -> TextContentRef {
        let arc_ref: ArcRef<Mutex<TextContentInner>> =
            ArcRef::new(Arc::clone(content));

        TextContentRef {
            handle: OwningHandle::new_with_fn(arc_ref, |mutex| unsafe {
                (*mutex).lock().unwrap()
            }),
        }
    }
}

/// A simple view showing a fixed text.
///
/// # Examples
///
/// ```rust,no_run
/// # use cursive::Cursive;
/// # use cursive::views::TextView;
/// let mut siv = Cursive::new();
///
/// siv.add_layer(TextView::new("Hello world!"));
/// ```
pub struct TextView {
    // content: String,
    content: Arc<Mutex<TextContentInner>>,
    rows: Vec<Row>,

    align: Align,

    // If `false`, disable scrolling.
    scrollable: bool,

    // ScrollBase make many scrolling-related things easier
    scrollbase: ScrollBase,
    scroll_strategy: ScrollStrategy,
    last_size: Vec2,
    width: Option<usize>,
}

// If the last character is a newline, strip it.
fn strip_last_newline(content: &str) -> &str {
    if content.ends_with('\n') {
        &content[..content.len() - 1]
    } else {
        content
    }
}

impl TextView {
    /// Creates a new TextView with the given content.
    pub fn new<S: Into<String>>(content: S) -> Self {
        TextView::new_with_content(TextContent::new(content))
    }

    /// Creates a new TextView using the given `Arc<Mutex<String>>`.
    ///
    /// If you kept a clone of the given content, you'll be able to update it
    /// remotely.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use cursive::views::{TextView, TextContent};
    /// let mut content = TextContent::new("content");
    /// let view = TextView::new_with_content(content.clone());
    ///
    /// // Later, possibly in a different thread
    /// content.set_content("new content");
    /// assert!(content.get_content().contains("new"));
    /// ```
    pub fn new_with_content(content: TextContent) -> Self {
        TextView {
            content: content.content,
            rows: Vec::new(),
            scrollable: true,
            scrollbase: ScrollBase::new(),
            scroll_strategy: ScrollStrategy::KeepRow,
            align: Align::top_left(),
            last_size: Vec2::zero(),
            width: None,
        }
    }

    /// Creates a new empty `TextView`.
    pub fn empty() -> Self {
        TextView::new("")
    }

    /// Enable or disable the view's scrolling capabilities.
    ///
    /// When disabled, the view will never attempt to scroll
    /// (and will always ask for the full height).
    pub fn set_scrollable(&mut self, scrollable: bool) {
        self.scrollable = scrollable;
    }

    /// Enable or disable the view's scrolling capabilities.
    ///
    /// When disabled, the view will never attempt to scroll
    /// (and will always ask for the full height).
    ///
    /// Chainable variant.
    pub fn scrollable(self, scrollable: bool) -> Self {
        self.with(|s| s.set_scrollable(scrollable))
    }

    /// Sets the horizontal alignment for this view.
    pub fn h_align(mut self, h: HAlign) -> Self {
        self.align.h = h;

        self
    }

    /// Sets the vertical alignment for this view.
    pub fn v_align(mut self, v: VAlign) -> Self {
        self.align.v = v;

        self
    }

    /// Sets the alignment for this view.
    pub fn align(mut self, a: Align) -> Self {
        self.align = a;

        self
    }

    /// Center the text horizontally and vertically inside the view.
    pub fn center(mut self) -> Self {
        self.align = Align::center();
        self
    }

    /// Replace the text in this view.
    ///
    /// Chainable variant.
    pub fn content<S: Into<String>>(self, content: S) -> Self {
        self.with(|s| s.set_content(content))
    }

    /// Replace the text in this view.
    pub fn set_content<S: Into<String>>(&mut self, content: S) {
        let content = content.into();
        self.content.lock().unwrap().content = content;
        self.invalidate();
    }

    /// Append `content` to the end of a `TextView`.
    pub fn append_content(&mut self, content: &str) {
        self.content.lock().unwrap().content.push_str(content);
        self.invalidate();
    }

    /// Returns the current text in this view.
    pub fn get_content(&self) -> TextContentRef {
        TextContentInner::get_content(&self.content)
    }

    /// Returns a shared reference to the content, allowing content mutation.
    pub fn get_shared_content(&mut self) -> TextContent {
        // We take &mut here without really needing it,
        // because it sort of "makes sense".

        TextContent {
            content: Arc::clone(&self.content),
        }
    }

    fn is_cache_valid(&self, size: Vec2) -> bool {
        let content = self.content.lock().unwrap();

        match content.size_cache {
            None => false,
            Some(ref last) => last.x.accept(size.x) && last.y.accept(size.y),
        }
    }

    /// Defines the way scrolling is adjusted on content or size change.
    ///
    /// The scroll strategy defines how the scrolling position is adjusted
    /// when the size of the view or the content change.
    ///
    /// It is reset to `ScrollStrategy::KeepRow` whenever the user scrolls
    /// manually.
    pub fn set_scroll_strategy(&mut self, strategy: ScrollStrategy) {
        self.scroll_strategy = strategy;
        self.adjust_scroll();
    }

    /// Defines the way scrolling is adjusted on content or size change.
    ///
    /// Chainable variant.
    pub fn scroll_strategy(self, strategy: ScrollStrategy) -> Self {
        self.with(|s| s.set_scroll_strategy(strategy))
    }

    // Apply the scrolling strategy to the current scroll position.
    //
    // Called when computing rows and when applying a new strategy.
    fn adjust_scroll(&mut self) {
        match self.scroll_strategy {
            ScrollStrategy::StickToTop => self.scrollbase.scroll_top(),
            ScrollStrategy::StickToBottom => self.scrollbase.scroll_bottom(),
            ScrollStrategy::KeepRow => (),
        };
    }

    // This must be non-destructive, as it may be called
    // multiple times during layout.
    fn compute_rows(&mut self, size: Vec2) {
        let mut content = self.content.lock().unwrap();
        if self.is_cache_valid(size) {
            return;
        }

        // Completely bust the cache
        // Just in case we fail, we don't want to leave a bad cache.
        content.size_cache = None;

        if size.x == 0 {
            // Nothing we can do at this point.
            return;
        }

        // First attempt: naively hope that we won't need a scrollbar_width
        // (This means we try to use the entire available width for text).
        self.rows =
            LinesIterator::new(strip_last_newline(&content.content), size.x)
                .collect();

        // Width taken by the scrollbar. Without a scrollbar, it's 0.
        let mut scrollbar_width = 0;

        if self.scrollable && self.rows.len() > size.y {
            // We take 1 column for the bar itself + 1 spacing column
            scrollbar_width = 2;

            // If we're too high, include a scrollbar_width
            let available = match size.x.checked_sub(scrollbar_width) {
                Some(s) => s,
                None => return,
            };

            self.rows =
                LinesIterator::new(&content.content, available).collect();

            if self.rows.is_empty() && !content.content.is_empty() {
                // We have some content, we we didn't find any row for it?
                // This probably means we couldn't even make a single row
                // (for instance we only have 1 column and we have a wide
                // character).
                return;
            }
        }

        // Desired width, including the scrollbar_width.
        self.width = self.rows
            .iter()
            .map(|row| row.width)
            .max()
            .map(|w| w + scrollbar_width);

        // The entire "virtual" size (includes all rows)
        let mut my_size = Vec2::new(self.width.unwrap_or(0), self.rows.len());

        // If we're scrolling, cap the the available size.
        if self.scrollable && my_size.y > size.y {
            my_size.y = size.y;
        }

        // Build a fresh cache.
        content.size_cache = Some(SizeCache::build(my_size, size));
    }

    // Invalidates the cache, so next call will recompute everything.
    fn invalidate(&mut self) {
        let mut content = self.content.lock().unwrap();
        content.size_cache = None;
    }
}

impl View for TextView {
    fn draw(&self, printer: &Printer) {
        let h = self.rows.len();
        // If the content is smaller than the view, align it somewhere.
        let offset = self.align.v.get_offset(h, printer.size.y);
        let printer = &printer.offset((0, offset), true);

        let content = self.content.lock().unwrap();

        self.scrollbase.draw(printer, |printer, i| {
            let row = &self.rows[i];
            let text = &content.content[row.start..row.end];
            let l = text.width();
            let x = self.align.h.get_offset(l, printer.size.x);
            printer.print((x, 0), text);
        });
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        if !self.scrollbase.scrollable() {
            return EventResult::Ignored;
        }

        // We have a scrollbar, otherwise the event would just be ignored.
        match event {
            Event::Key(Key::Home) => self.scrollbase.scroll_top(),
            Event::Key(Key::End) => self.scrollbase.scroll_bottom(),
            Event::Key(Key::Up) if self.scrollbase.can_scroll_up() => {
                self.scrollbase.scroll_up(1)
            }
            Event::Key(Key::Down) if self.scrollbase.can_scroll_down() => {
                self.scrollbase.scroll_down(1)
            }
            Event::Mouse {
                event: MouseEvent::WheelDown,
                ..
            } if self.scrollbase.can_scroll_down() =>
            {
                self.scrollbase.scroll_down(5);
            }
            Event::Mouse {
                event: MouseEvent::WheelUp,
                ..
            } if self.scrollbase.can_scroll_up() =>
            {
                self.scrollbase.scroll_up(5);
            }
            Event::Mouse {
                event: MouseEvent::Press(MouseButton::Left),
                position,
                offset,
            } if position
                .checked_sub(offset)
                .map(|position| {
                    self.scrollbase.start_drag(position, self.last_size.x)
                })
                .unwrap_or(false) =>
            {
                // Only consume the event if the mouse hits the scrollbar.
                // Start scroll drag at the given position.
            }
            Event::Mouse {
                event: MouseEvent::Hold(MouseButton::Left),
                position,
                offset,
            } => {
                // If the mouse is dragged, we always consume the event.
                let position = position.saturating_sub(offset);
                self.scrollbase.drag(position);
            }
            Event::Mouse {
                event: MouseEvent::Release(MouseButton::Left),
                ..
            } => {
                self.scrollbase.release_grab();
            }
            Event::Key(Key::PageDown) => self.scrollbase.scroll_down(10),
            Event::Key(Key::PageUp) => self.scrollbase.scroll_up(10),
            _ => return EventResult::Ignored,
        }

        // We just scrolled manually, so reset the scroll strategy.
        self.scroll_strategy = ScrollStrategy::KeepRow;
        EventResult::Consumed(None)
    }

    fn needs_relayout(&self) -> bool {
        let content = self.content.lock().unwrap();
        content.size_cache.is_none()
    }

    fn required_size(&mut self, size: Vec2) -> Vec2 {
        self.compute_rows(size);

        // This is what we'd like
        let mut ideal = Vec2::new(self.width.unwrap_or(0), self.rows.len());

        if self.scrollable && ideal.y > size.y {
            ideal.y = size.y;
        }

        ideal
    }

    fn take_focus(&mut self, _: Direction) -> bool {
        self.scrollbase.scrollable()
    }

    fn layout(&mut self, size: Vec2) {
        // Compute the text rows.
        self.last_size = size;
        self.compute_rows(size);
        // Adjust scrolling, in case we're sticking to the bottom for instance.
        self.scrollbase.set_heights(size.y, self.rows.len());
        self.adjust_scroll();
    }
}
<|endoftext|>


<commit_before>
use color::{ Color, Colorable };
use dimensions::Dimensions;
use mouse::Mouse;
use point::Point;
use rectangle;
use ui_context::{ UIID, UiContext };
use widget::Widget;
use graphics::Graphics;
use graphics::character::CharacterCache;

/// Represents the state of the Button widget.
#[derive(PartialEq, Clone, Copy)]
pub enum State {
    Normal,
    Highlighted,
    Clicked,
}

impl State {
    /// Return the associated Rectangle state.
    fn as_rectangle_state(&self) -> rectangle::State {
        match self {
            &State::Normal => rectangle::State::Normal,
            &State::Highlighted => rectangle::State::Highlighted,
            &State::Clicked => rectangle::State::Clicked,
        }
    }
}

widget_fns!(Button, State, Widget::Button(State::Normal));

/// Check the current state of the button.
fn get_new_state(is_over: bool,
                 prev: State,
                 mouse: Mouse) -> State {
    use mouse::ButtonState::{Down, Up};
    use self::State::{Normal, Highlighted, Clicked};
    match (is_over, prev, mouse.left) {
        (true,  Normal,  Down) => Normal,
        (true,  _,       Down) => Clicked,
        (true,  _,       Up)   => Highlighted,
        (false, Clicked, Down) => Clicked,
        _                      => Normal,
    }
}

/// A context on which the builder pattern can be implemented.
pub struct Button<'a, F> {
    ui_id: UIID,
    pos: Point,
    dim: Dimensions,
    maybe_color: Option<Color>,
    maybe_frame: Option<f64>,
    maybe_frame_color: Option<Color>,
    maybe_label: Option<&'a str>,
    maybe_label_color: Option<Color>,
    maybe_label_font_size: Option<u32>,
    maybe_callback: Option<F>,
}

impl<'a, F> Button<'a, F> {

    /// Create a button context to be built upon.
    pub fn new(ui_id: UIID) -> Button<'a, F> {
        Button {
            ui_id: ui_id,
            pos: [0.0, 0.0],
            dim: [64.0, 64.0],
            maybe_callback: None,
            maybe_color: None,
            maybe_frame: None,
            maybe_frame_color: None,
            maybe_label: None,
            maybe_label_color: None,
            maybe_label_font_size: None,
        }
    }

}

impl<'a, F> Colorable for Button<'a, F> {
    fn color(mut self, color: Color) -> Self {
        self.maybe_color = Some(color);
        self
    }
}

/*
quack! {
    button: Button['a, F]
    get:
        fn () -> Size [] { Size(button.dim) }
        fn () -> DefaultWidgetState [] {
            DefaultWidgetState(Widget::Button(State::Normal))
        }
        fn () -> Id [] { Id(button.ui_id) }
    set:
        fn (val: Callback<F>) [where F: FnMut() + 'a] {
            button.maybe_callback = Some(val.0)
        }
        fn (val: FrameColor) [] { button.maybe_frame_color = Some(val.0) }
        fn (val: FrameWidth) [] { button.maybe_frame = Some(val.0) }
        fn (val: LabelText<'a>) [] { button.maybe_label = Some(val.0) }
        fn (val: LabelColor) [] { button.maybe_label_color = Some(val.0) }
        fn (val: LabelFontSize) [] { button.maybe_label_font_size = Some(val.0) }
        fn (val: Position) [] { button.pos = val.0 }
        fn (val: Size) [] { button.dim = val.0 }
    action:
}
*/

impl<'a, F> ::draw::Drawable for Button<'a, F>
    where
        F: FnMut() + 'a
{

    fn draw<B, C>(&mut self, uic: &mut UiContext<C>, graphics: &mut B)
        where
            B: Graphics<Texture = <C as CharacterCache>::Texture>,
            C: CharacterCache
    {

        let state = *get_state(uic, self.ui_id);
        let mouse = uic.get_mouse_state();
        let is_over = rectangle::is_over(self.pos, mouse.pos, self.dim);
        let new_state = get_new_state(is_over, state, mouse);

        // Callback.
        match (is_over, state, new_state) {
            (true, State::Clicked, State::Highlighted) => match self.maybe_callback {
                Some(ref mut callback) => (*callback)(), None => (),
            }, _ => (),
        }

        // Draw.
        let rect_state = new_state.as_rectangle_state();
        let color = self.maybe_color.unwrap_or(uic.theme.shape_color);
        let frame_w = self.maybe_frame.unwrap_or(uic.theme.frame_width);
        let maybe_frame = match frame_w > 0.0 {
            true => Some((frame_w, self.maybe_frame_color.unwrap_or(uic.theme.frame_color))),
            false => None,
        };
        match self.maybe_label {
            None => {
                rectangle::draw(
                    uic.win_w, uic.win_h, graphics, rect_state, self.pos,
                    self.dim, maybe_frame, color
                )
            },
            Some(text) => {
                let text_color = self.maybe_label_color.unwrap_or(uic.theme.label_color);
                let size = self.maybe_label_font_size.unwrap_or(uic.theme.font_size_medium);
                rectangle::draw_with_centered_label(
                    uic.win_w, uic.win_h, graphics, uic, rect_state,
                    self.pos, self.dim, maybe_frame, color,
                    text, size, text_color
                )
            },
        }

        set_state(uic, self.ui_id, Widget::Button(new_state), self.pos, self.dim);

    }
}
<commit_msg>Implemented `Frameable` for `Button`<commit_after>
use frame::Frameable;
use color::{ Color, Colorable };
use dimensions::Dimensions;
use mouse::Mouse;
use point::Point;
use rectangle;
use ui_context::{ UIID, UiContext };
use widget::Widget;
use graphics::Graphics;
use graphics::character::CharacterCache;

/// Represents the state of the Button widget.
#[derive(PartialEq, Clone, Copy)]
pub enum State {
    Normal,
    Highlighted,
    Clicked,
}

impl State {
    /// Return the associated Rectangle state.
    fn as_rectangle_state(&self) -> rectangle::State {
        match self {
            &State::Normal => rectangle::State::Normal,
            &State::Highlighted => rectangle::State::Highlighted,
            &State::Clicked => rectangle::State::Clicked,
        }
    }
}

widget_fns!(Button, State, Widget::Button(State::Normal));

/// Check the current state of the button.
fn get_new_state(is_over: bool,
                 prev: State,
                 mouse: Mouse) -> State {
    use mouse::ButtonState::{Down, Up};
    use self::State::{Normal, Highlighted, Clicked};
    match (is_over, prev, mouse.left) {
        (true,  Normal,  Down) => Normal,
        (true,  _,       Down) => Clicked,
        (true,  _,       Up)   => Highlighted,
        (false, Clicked, Down) => Clicked,
        _                      => Normal,
    }
}

/// A context on which the builder pattern can be implemented.
pub struct Button<'a, F> {
    ui_id: UIID,
    pos: Point,
    dim: Dimensions,
    maybe_color: Option<Color>,
    maybe_frame: Option<f64>,
    maybe_frame_color: Option<Color>,
    maybe_label: Option<&'a str>,
    maybe_label_color: Option<Color>,
    maybe_label_font_size: Option<u32>,
    maybe_callback: Option<F>,
}

impl<'a, F> Button<'a, F> {

    /// Create a button context to be built upon.
    pub fn new(ui_id: UIID) -> Button<'a, F> {
        Button {
            ui_id: ui_id,
            pos: [0.0, 0.0],
            dim: [64.0, 64.0],
            maybe_callback: None,
            maybe_color: None,
            maybe_frame: None,
            maybe_frame_color: None,
            maybe_label: None,
            maybe_label_color: None,
            maybe_label_font_size: None,
        }
    }

}

impl<'a, F> Colorable for Button<'a, F> {
    fn color(mut self, color: Color) -> Self {
        self.maybe_color = Some(color);
        self
    }
}

impl<'a, F> Frameable for Button<'a, F> {
    fn frame(mut self, width: f64) -> Self {
        self.maybe_frame = Some(width);
        self
    }
    fn frame_color(mut self, color: Color) -> Self {
        self.maybe_frame_color = Some(color);
        self
    }
}

/*
quack! {
    button: Button['a, F]
    get:
        fn () -> Size [] { Size(button.dim) }
        fn () -> DefaultWidgetState [] {
            DefaultWidgetState(Widget::Button(State::Normal))
        }
        fn () -> Id [] { Id(button.ui_id) }
    set:
        fn (val: Callback<F>) [where F: FnMut() + 'a] {
            button.maybe_callback = Some(val.0)
        }
        fn (val: LabelText<'a>) [] { button.maybe_label = Some(val.0) }
        fn (val: LabelColor) [] { button.maybe_label_color = Some(val.0) }
        fn (val: LabelFontSize) [] { button.maybe_label_font_size = Some(val.0) }
        fn (val: Position) [] { button.pos = val.0 }
        fn (val: Size) [] { button.dim = val.0 }
    action:
}
*/

impl<'a, F> ::draw::Drawable for Button<'a, F>
    where
        F: FnMut() + 'a
{

    fn draw<B, C>(&mut self, uic: &mut UiContext<C>, graphics: &mut B)
        where
            B: Graphics<Texture = <C as CharacterCache>::Texture>,
            C: CharacterCache
    {

        let state = *get_state(uic, self.ui_id);
        let mouse = uic.get_mouse_state();
        let is_over = rectangle::is_over(self.pos, mouse.pos, self.dim);
        let new_state = get_new_state(is_over, state, mouse);

        // Callback.
        match (is_over, state, new_state) {
            (true, State::Clicked, State::Highlighted) => match self.maybe_callback {
                Some(ref mut callback) => (*callback)(), None => (),
            }, _ => (),
        }

        // Draw.
        let rect_state = new_state.as_rectangle_state();
        let color = self.maybe_color.unwrap_or(uic.theme.shape_color);
        let frame_w = self.maybe_frame.unwrap_or(uic.theme.frame_width);
        let maybe_frame = match frame_w > 0.0 {
            true => Some((frame_w, self.maybe_frame_color.unwrap_or(uic.theme.frame_color))),
            false => None,
        };
        match self.maybe_label {
            None => {
                rectangle::draw(
                    uic.win_w, uic.win_h, graphics, rect_state, self.pos,
                    self.dim, maybe_frame, color
                )
            },
            Some(text) => {
                let text_color = self.maybe_label_color.unwrap_or(uic.theme.label_color);
                let size = self.maybe_label_font_size.unwrap_or(uic.theme.font_size_medium);
                rectangle::draw_with_centered_label(
                    uic.win_w, uic.win_h, graphics, uic, rect_state,
                    self.pos, self.dim, maybe_frame, color,
                    text, size, text_color
                )
            },
        }

        set_state(uic, self.ui_id, Widget::Button(new_state), self.pos, self.dim);

    }
}
<|endoftext|>