<commit_before>use std::borrow::ToOwned;
use std::cell::{Cell, RefCell};
use std::cmp::{min, max, Ordering};
use std::default::Default;
use std::fs;
use std::io::Read;
use std::ops::Range;
use std::path::Path;
use clock_ticks;

use cfg::ColorScheme;
use gfx::MouseCursor;
use glyph::Metrics;
use graphics::character::CharacterCache;

use ui::{BB, Px};
use ui::layout::{RectBB, RectBounded, Layout, Where};
use ui::color::Scheme;
use ui::draw::{Draw, DrawCx};
use ui::event::*;
use ui::text::{self, FontFace};

use ide::highlight;

pub struct Editor {
    bb: RectBB,
    font: text::Mono,
    font_bold: text::MonoBold,
    font_metrics: Cell<Metrics>,
    over: Cell<bool>,
    down: Cell<bool>,
    blink_phase: Cell<bool>,
    scroll_start: Cell<usize>,

    selection_start: Cell<Caret>,
    caret: Cell<Caret>,

    lines: RefCell<Vec<Line>>
}

#[derive(Copy, Clone)]
struct Caret {
    row: usize,
    col: usize,
    offset: usize
}

impl PartialEq for Caret {
    fn eq(&self, other: &Caret) -> bool {
        self.row == other.row && self.col == other.col
    }
}

impl Eq for Caret {}

impl PartialOrd for Caret {
    fn partial_cmp(&self, other: &Caret) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Caret {
    fn cmp(&self, other: &Caret) -> Ordering {
        if self.row == other.row {
            self.col.cmp(&other.col)
        } else {
            self.row.cmp(&other.row)
        }
    }
}

#[derive(Debug)]
struct Line {
    data: String,
    hl_depth: usize,
    ranges: Vec<(usize, highlight::Style)>
}

impl Editor {
    pub fn open<P: AsRef<Path>>(path: P) -> Editor {
        let mut data = String::new();
        fs::File::open(path).unwrap().read_to_string(&mut data).unwrap();
        let lines = data.split('\n').map(|line| Line {
            data: line.to_owned(),
            hl_depth: 1,
            ranges: vec![]
        }).collect();

        let caret = Caret {
            row: 0,
            col: 0,
            offset: 0
        };

        let editor = Editor {
            bb: RectBB::default(),
            font: text::Mono,
            font_bold: text::MonoBold,
            font_metrics: Cell::new(Metrics::default()),
            over: Cell::new(false),
            down: Cell::new(false),
            blink_phase: Cell::new(false),
            scroll_start: Cell::new(0),

            selection_start: Cell::new(caret),
            caret: Cell::new(caret),

            lines: RefCell::new(lines)
        };

        let num_lines = editor.lines.borrow().len();
        editor.update_hl(0..num_lines);

        editor
    }

    fn pos_to_caret(&self, [x, y]: [Px; 2]) -> Option<Caret> {
        let metrics = self.font_metrics.get();
        if metrics.width == 0.0 {
            return None;
        }

        let bb = self.bb();
        if x > bb.x2 || y > bb.y2 {
            return None;
        }
        let [x, y] = [x - bb.x1, y - bb.y1];
        if x < 0.0 || y < 0.0 {
            return None;
        }
        let row = (y / metrics.height) as usize + self.scroll_start.get();

        let mut x2 = 0.0;
        let mut col = 0;
        let mut offset = 0;
        for c in self.lines.borrow()[row].data.chars() {
            let w = c.width(false).unwrap_or(1);
            x2 += w as Px * metrics.width;
            if x2 > x {
                break;
            }
            col += w;
            offset += c.len_utf8();
        }
        Some(Caret { row: row, col: col, offset: offset })
    }

    fn move_to(&self, caret: Caret) {
        self.selection_start.set(caret);
        self.caret.set(caret);
    }

    fn update_hl(&self, mut range: Range<usize>) {
        let mut lines = self.lines.borrow_mut();
        while lines[range.start].hl_depth > 0 && range.start > 0 {
            range.start -= 1;
        }
        while lines[range.end - 1].hl_depth > 0 && range.end < lines.len() {
            range.end += 1;
        }

        let (d, mut hl) = highlight::Rust::run(lines[range.clone()].iter().map(|line| &line.data[..]));

        // Fallback to re-highlight everything until the end.
        if d > 0 {
            hl = highlight::Rust::run(lines.iter().map(|line| &line.data[..])).1;
            range = 0..lines.len();
        }

        for (line, (hl_depth, ranges)) in lines[range].iter_mut().zip(hl.into_iter()) {
            line.hl_depth = hl_depth;
            line.ranges = ranges;
        }
    }

    fn insert(&self, data: &str) {
        let mut k = self.caret.get();

        {
            let mut lines = self.lines.borrow_mut();
            let line = &mut lines[k.row].data;

            for ch in data.chars() {
                line.insert(k.offset, ch);
                k.offset += ch.len_utf8();
                k.col += 1;
            }
        }

        self.update_hl(k.row..k.row+1);
        self.move_to(k);
    }
}

impl RectBounded for Editor {
    fn rect_bb(&self) -> &RectBB { &self.bb }
    fn name(&self) -> &'static str { "<editor>" }
}

impl Draw for Editor {
    fn draw(&self, cx: &mut DrawCx) {
        let mut metrics = self.font_metrics.get();
        if metrics.width == 0.0 {
            metrics = cx.fonts.metrics(self.font);
            self.font_metrics.set(metrics);
        }

        if self.over.get() {
            cx.cursor = MouseCursor::Text;
        }

        let bb = self.bb();
        let start = self.scroll_start.get();
        let end = start + (((bb.y2 - bb.y1) / metrics.height) as usize);
        let lines = self.lines.borrow();
        let lines = if end < lines.len() {
            &lines[start..end]
        } else {
            &lines[start..]
        };

        cx.rect(bb, ColorScheme.back_view());

        let s1 = self.selection_start.get();
        let s2 = self.caret.get();
        let k = s2;
        if start <= s2.row && s2.row <= end {
            let y = bb.y1 + ((s2.row - start) as Px * metrics.height);
            cx.rect(BB {
                x1: bb.x1, y1: y,
                x2: bb.x2, y2: y + metrics.height
            }, ColorScheme.back_view_alt());
        }

        // First line of the selection.
        let (s1, s2) = (min(s1, s2), max(s1, s2));
        if start <= s1.row && s1.row <= end {
            let y = bb.y1 + ((s1.row - start) as Px * metrics.height);
            let x1 = bb.x1 + (s1.col as Px) * metrics.width;
            let x2 = if s1.row == s2.row {
                bb.x1 + (s2.col as Px) * metrics.width
            } else {
                bb.x2
            };
            cx.rect(BB {
                x1: x1, y1: y,
                x2: x2, y2: y + metrics.height
            }, ColorScheme.focus());
        }
        // Subsequent lines.
        for row in (s1.row + 1)..s2.row {
            if start <= row && row <= end {
                let y = bb.y1 + ((row - start) as Px * metrics.height);
                cx.rect(BB {
                    x1: bb.x1, y1: y,
                    x2: bb.x2, y2: y + metrics.height
                }, ColorScheme.focus());
            }
        }
        // Last line (if selection has at least 2 lines).
        if start <= s2.row && s2.row <= end && s1.row < s2.row {
            let y = bb.y1 + ((s2.row - start) as Px * metrics.height);
            cx.rect(BB {
                x1: bb.x1, y1: y,
                x2: bb.x1 + (s2.col as Px) * metrics.width, y2: y + metrics.height
            }, ColorScheme.focus());
        }

        // The actual text in each line.
        for (i, line) in lines.iter().enumerate() {
            let y = bb.y1 + i as Px * metrics.height;
            let mut pos = 0;
            for &(len, style) in &line.ranges {
                let x = bb.x1 + (pos as Px) * metrics.width;
                if style.bold {
                    self.font_bold.draw(cx, [x, y], style.color, &line.data[pos..pos+len]);
                } else {
                    self.font.draw(cx, [x, y], style.color, &line.data[pos..pos+len]);
                }
                pos += len;
            }
        }

        // Caret on top of everything else.
        if self.blink_phase.get() && start <= k.row && k.row <= end {
            let y = bb.y1 + ((k.row - start) as Px * metrics.height);

            // TODO proper BB scissoring.
            let x = bb.x1 + (k.col as Px) * metrics.width;
            let w = 2.0;
            if bb.x1 <= x && x + w <= bb.x2 {
                cx.rect(BB {
                    x1: x, y1: y,
                    x2: x + w, y2: y + metrics.height
                }, ColorScheme.normal());
            }
        }
    }
}

impl Dispatch<MouseDown> for Editor {
    fn dispatch(&self, ev: &MouseDown) -> bool {
        self.down.set(true);

        if let Some(caret) = self.pos_to_caret(ev.pos()) {
            self.move_to(caret);
            true
        } else {
            false
        }
    }
}

impl Dispatch<MouseUp> for Editor {
    fn dispatch(&self, _: &MouseUp) -> bool {
        self.down.set(false);
        false
    }
}

impl Dispatch<MouseMove> for Editor {
    fn dispatch(&self, ev: &MouseMove) -> bool {
        let over = self.bb().contains(ev.pos());
        let mut dirty = false;
        if over != self.over.get() {
            self.over.set(over);
            dirty = true;
        }

        if let Some(caret) = self.pos_to_caret(ev.pos()) {
            if self.down.get() {
                self.caret.set(caret);
                dirty = true;
            }
        }

        dirty
    }
}

impl Dispatch<MouseScroll> for Editor {
    fn dispatch(&self, ev: &MouseScroll) -> bool {
        let metrics = self.font_metrics.get();
        if metrics.width == 0.0 {
            return false;
        }

        let [_, dy] = ev.delta();
        if dy == 0.0 {
            return false;
        }

        let dy = -dy;
        let bb = self.bb();
        let sy = self.scroll_start.get();
        let new_sy = if dy < 0.0 {
            let dy = -dy as usize;
            if sy < dy { sy } else { sy - dy }
        } else {
            let dy = dy as usize;
            if ((self.lines.borrow().len() as Px)  - ((sy + dy) as Px)) * metrics.height <= (bb.y2 - bb.y1) {
                sy
            } else {
                sy + dy
            }
        };
        self.scroll_start.set(new_sy);
        new_sy != sy
    }
}

impl Dispatch<Update> for Editor {
    fn dispatch(&self, _: &Update) -> bool {
        const SECOND: u64 = 1_000_000_000;
        let phase = clock_ticks::precise_time_ns() % SECOND > SECOND / 2;
        if phase != self.blink_phase.get() { self.blink_phase.set(phase); true } else { false }
    }
}

impl<'a> Dispatch<TextInput<'a>> for Editor {
    fn dispatch(&self, ev: &TextInput) -> bool {
        self.insert(ev.0);
        true
    }
}
<commit_msg>editor: insertion removes selection.<commit_after>use std::borrow::ToOwned;
use std::cell::{Cell, RefCell};
use std::cmp::{min, max, Ordering};
use std::default::Default;
use std::fs;
use std::io::Read;
use std::ops::Range;
use std::path::Path;
use clock_ticks;

use cfg::ColorScheme;
use gfx::MouseCursor;
use glyph::Metrics;
use graphics::character::CharacterCache;

use ui::{BB, Px};
use ui::layout::{RectBB, RectBounded, Layout, Where};
use ui::color::Scheme;
use ui::draw::{Draw, DrawCx};
use ui::event::*;
use ui::text::{self, FontFace};

use ide::highlight;

pub struct Editor {
    bb: RectBB,
    font: text::Mono,
    font_bold: text::MonoBold,
    font_metrics: Cell<Metrics>,
    over: Cell<bool>,
    down: Cell<bool>,
    blink_phase: Cell<bool>,
    scroll_start: Cell<usize>,

    selection_start: Cell<Caret>,
    caret: Cell<Caret>,

    lines: RefCell<Vec<Line>>
}

#[derive(Copy, Clone)]
struct Caret {
    row: usize,
    col: usize,
    offset: usize
}

impl PartialEq for Caret {
    fn eq(&self, other: &Caret) -> bool {
        self.row == other.row && self.col == other.col
    }
}

impl Eq for Caret {}

impl PartialOrd for Caret {
    fn partial_cmp(&self, other: &Caret) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Caret {
    fn cmp(&self, other: &Caret) -> Ordering {
        if self.row == other.row {
            self.col.cmp(&other.col)
        } else {
            self.row.cmp(&other.row)
        }
    }
}

#[derive(Debug)]
struct Line {
    data: String,
    hl_depth: usize,
    ranges: Vec<(usize, highlight::Style)>
}

impl Editor {
    pub fn open<P: AsRef<Path>>(path: P) -> Editor {
        let mut data = String::new();
        fs::File::open(path).unwrap().read_to_string(&mut data).unwrap();
        let lines = data.split('\n').map(|line| Line {
            data: line.to_owned(),
            hl_depth: 1,
            ranges: vec![]
        }).collect();

        let caret = Caret {
            row: 0,
            col: 0,
            offset: 0
        };

        let editor = Editor {
            bb: RectBB::default(),
            font: text::Mono,
            font_bold: text::MonoBold,
            font_metrics: Cell::new(Metrics::default()),
            over: Cell::new(false),
            down: Cell::new(false),
            blink_phase: Cell::new(false),
            scroll_start: Cell::new(0),

            selection_start: Cell::new(caret),
            caret: Cell::new(caret),

            lines: RefCell::new(lines)
        };

        let num_lines = editor.lines.borrow().len();
        editor.update_hl(0..num_lines);

        editor
    }

    fn pos_to_caret(&self, [x, y]: [Px; 2]) -> Option<Caret> {
        let metrics = self.font_metrics.get();
        if metrics.width == 0.0 {
            return None;
        }

        let bb = self.bb();
        if x > bb.x2 || y > bb.y2 {
            return None;
        }
        let [x, y] = [x - bb.x1, y - bb.y1];
        if x < 0.0 || y < 0.0 {
            return None;
        }
        let row = (y / metrics.height) as usize + self.scroll_start.get();

        let mut x2 = 0.0;
        let mut col = 0;
        let mut offset = 0;
        for c in self.lines.borrow()[row].data.chars() {
            let w = c.width(false).unwrap_or(1);
            x2 += w as Px * metrics.width;
            if x2 > x {
                break;
            }
            col += w;
            offset += c.len_utf8();
        }
        Some(Caret { row: row, col: col, offset: offset })
    }

    fn move_to(&self, caret: Caret) {
        self.selection_start.set(caret);
        self.caret.set(caret);
    }

    fn update_hl(&self, mut range: Range<usize>) {
        let mut lines = self.lines.borrow_mut();
        while lines[range.start].hl_depth > 0 && range.start > 0 {
            range.start -= 1;
        }
        while lines[range.end - 1].hl_depth > 0 && range.end < lines.len() {
            range.end += 1;
        }

        let (d, mut hl) = highlight::Rust::run(lines[range.clone()].iter().map(|line| &line.data[..]));

        // Fallback to re-highlight everything until the end.
        if d > 0 {
            hl = highlight::Rust::run(lines.iter().map(|line| &line.data[..])).1;
            range = 0..lines.len();
        }

        for (line, (hl_depth, ranges)) in lines[range].iter_mut().zip(hl.into_iter()) {
            line.hl_depth = hl_depth;
            line.ranges = ranges;
        }
    }

    fn remove(&self, range: Range<Caret>) {
        let (s1, s2) = (range.start, range.end);
        let mut lines = self.lines.borrow_mut();

        // Remove part of the first line.
        if s1.row == s2.row {
            let line = &mut lines[s1.row].data;

            let final_len = line.len() - (s2.offset - s1.offset);
            while line.len() > final_len {
                line.remove(s1.offset);
            }
        } else {
            lines[s1.row].data.truncate(s1.offset);
        }

        // Add part of last line to first line (if range has at least 2 lines).
        if s1.row < s2.row {
            let (dest, src) = lines[s1.row..].split_at_mut(1);
            let dest = &mut dest[0].data;
            let src = &mut src[s2.row - s1.row - 1].data;

            dest.push_str(&src[s2.offset..]);
        }

        // Remove all other lines.
        for _ in s1.row+1..s2.row+1 {
            lines.remove(s1.row + 1);
        }
    }

    fn insert(&self, data: &str) {
        let (s1, s2) = (self.selection_start.get(), self.caret.get());
        let (s1, s2) = (min(s1, s2), max(s1, s2));

        if s1 != s2 {
            self.remove(s1..s2);
        }

        let mut k = s1;
        {
            let mut lines = self.lines.borrow_mut();
            let line = &mut lines[k.row].data;
            for ch in data.chars() {
                line.insert(k.offset, ch);
                k.offset += ch.len_utf8();
                k.col += 1;
            }
        }

        self.update_hl(k.row..k.row+1);
        self.move_to(k);
    }
}

impl RectBounded for Editor {
    fn rect_bb(&self) -> &RectBB { &self.bb }
    fn name(&self) -> &'static str { "<editor>" }
}

impl Draw for Editor {
    fn draw(&self, cx: &mut DrawCx) {
        let mut metrics = self.font_metrics.get();
        if metrics.width == 0.0 {
            metrics = cx.fonts.metrics(self.font);
            self.font_metrics.set(metrics);
        }

        if self.over.get() {
            cx.cursor = MouseCursor::Text;
        }

        let bb = self.bb();
        let start = self.scroll_start.get();
        let end = start + (((bb.y2 - bb.y1) / metrics.height) as usize);
        let lines = self.lines.borrow();
        let lines = if end < lines.len() {
            &lines[start..end]
        } else {
            &lines[start..]
        };

        cx.rect(bb, ColorScheme.back_view());

        let s1 = self.selection_start.get();
        let s2 = self.caret.get();
        let k = s2;
        if start <= s2.row && s2.row <= end {
            let y = bb.y1 + ((s2.row - start) as Px * metrics.height);
            cx.rect(BB {
                x1: bb.x1, y1: y,
                x2: bb.x2, y2: y + metrics.height
            }, ColorScheme.back_view_alt());
        }

        // First line of the selection.
        let (s1, s2) = (min(s1, s2), max(s1, s2));
        if start <= s1.row && s1.row <= end {
            let y = bb.y1 + ((s1.row - start) as Px * metrics.height);
            let x1 = bb.x1 + (s1.col as Px) * metrics.width;
            let x2 = if s1.row == s2.row {
                bb.x1 + (s2.col as Px) * metrics.width
            } else {
                bb.x2
            };
            cx.rect(BB {
                x1: x1, y1: y,
                x2: x2, y2: y + metrics.height
            }, ColorScheme.focus());
        }
        // Subsequent lines.
        for row in (s1.row + 1)..s2.row {
            if start <= row && row <= end {
                let y = bb.y1 + ((row - start) as Px * metrics.height);
                cx.rect(BB {
                    x1: bb.x1, y1: y,
                    x2: bb.x2, y2: y + metrics.height
                }, ColorScheme.focus());
            }
        }
        // Last line (if selection has at least 2 lines).
        if start <= s2.row && s2.row <= end && s1.row < s2.row {
            let y = bb.y1 + ((s2.row - start) as Px * metrics.height);
            cx.rect(BB {
                x1: bb.x1, y1: y,
                x2: bb.x1 + (s2.col as Px) * metrics.width, y2: y + metrics.height
            }, ColorScheme.focus());
        }

        // The actual text in each line.
        for (i, line) in lines.iter().enumerate() {
            let y = bb.y1 + i as Px * metrics.height;
            let mut pos = 0;
            for &(len, style) in &line.ranges {
                let x = bb.x1 + (pos as Px) * metrics.width;
                if style.bold {
                    self.font_bold.draw(cx, [x, y], style.color, &line.data[pos..pos+len]);
                } else {
                    self.font.draw(cx, [x, y], style.color, &line.data[pos..pos+len]);
                }
                pos += len;
            }
        }

        // Caret on top of everything else.
        if self.blink_phase.get() && start <= k.row && k.row <= end {
            let y = bb.y1 + ((k.row - start) as Px * metrics.height);

            // TODO proper BB scissoring.
            let x = bb.x1 + (k.col as Px) * metrics.width;
            let w = 2.0;
            if bb.x1 <= x && x + w <= bb.x2 {
                cx.rect(BB {
                    x1: x, y1: y,
                    x2: x + w, y2: y + metrics.height
                }, ColorScheme.normal());
            }
        }
    }
}

impl Dispatch<MouseDown> for Editor {
    fn dispatch(&self, ev: &MouseDown) -> bool {
        self.down.set(true);

        if let Some(caret) = self.pos_to_caret(ev.pos()) {
            self.move_to(caret);
            true
        } else {
            false
        }
    }
}

impl Dispatch<MouseUp> for Editor {
    fn dispatch(&self, _: &MouseUp) -> bool {
        self.down.set(false);
        false
    }
}

impl Dispatch<MouseMove> for Editor {
    fn dispatch(&self, ev: &MouseMove) -> bool {
        let over = self.bb().contains(ev.pos());
        let mut dirty = false;
        if over != self.over.get() {
            self.over.set(over);
            dirty = true;
        }

        if let Some(caret) = self.pos_to_caret(ev.pos()) {
            if self.down.get() {
                self.caret.set(caret);
                dirty = true;
            }
        }

        dirty
    }
}

impl Dispatch<MouseScroll> for Editor {
    fn dispatch(&self, ev: &MouseScroll) -> bool {
        let metrics = self.font_metrics.get();
        if metrics.width == 0.0 {
            return false;
        }

        let [_, dy] = ev.delta();
        if dy == 0.0 {
            return false;
        }

        let dy = -dy;
        let bb = self.bb();
        let sy = self.scroll_start.get();
        let new_sy = if dy < 0.0 {
            let dy = -dy as usize;
            if sy < dy { sy } else { sy - dy }
        } else {
            let dy = dy as usize;
            if ((self.lines.borrow().len() as Px)  - ((sy + dy) as Px)) * metrics.height <= (bb.y2 - bb.y1) {
                sy
            } else {
                sy + dy
            }
        };
        self.scroll_start.set(new_sy);
        new_sy != sy
    }
}

impl Dispatch<Update> for Editor {
    fn dispatch(&self, _: &Update) -> bool {
        const SECOND: u64 = 1_000_000_000;
        let phase = clock_ticks::precise_time_ns() % SECOND > SECOND / 2;
        if phase != self.blink_phase.get() { self.blink_phase.set(phase); true } else { false }
    }
}

impl<'a> Dispatch<TextInput<'a>> for Editor {
    fn dispatch(&self, ev: &TextInput) -> bool {
        self.insert(ev.0);
        true
    }
}
<|endoftext|>


<commit_before>//! Traits for working with JavaScript objects.
//!
//! This module defines the [`Object`](Object) trait, which is implemented
//! by all object types in the [JavaScript type hierarchy][hierarchy]. This
//! trait provides key operations in the semantics of JavaScript objects,
//! such as getting and setting an object's properties.
//!
//! ## Property Keys
//!
//! Object properties are accessed by a _property key_, which in JavaScript
//! can be a string or [symbol][symbol]. (Neon does not yet have support for
//! symbols.) For convenience, the [`PropertyKey`](PropertyKey) trait allows
//! Neon programs to use various Rust string types, as well as numeric types,
//! as keys when accessing object properties, converting the keys to strings
//! as necessary:
//!
//! ```
//! # #[cfg(feature = "napi-1")] {
//! # use neon::prelude::*;
//! fn set_and_check<'a>(
//!     cx: &mut impl Context<'a>,
//!     obj: Handle<'a, JsObject>
//! ) -> JsResult<'a, JsValue> {
//!     let value = cx.string("hello!");
//!     // set property "17" with integer shorthand
//!     obj.set(cx, 17, value)?;
//!     // get property "17" with string shorthand
//!     // returns the same value ("hello!")
//!     obj.get(cx, "17")
//! }
//! # }
//! ```
//!
//! [hierarchy]: crate::types#the-javascript-type-hierarchy
//! [symbol]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol

#[cfg(feature = "legacy-runtime")]
pub(crate) mod class;

#[cfg(feature = "legacy-runtime")]
pub use self::class::{Class, ClassDescriptor};
pub use self::traits::*;

#[cfg(feature = "legacy-runtime")]
mod traits {
    use crate::context::Context;
    use crate::handle::{Handle, Managed};
    use crate::result::{JsResult, NeonResult, Throw};
    use crate::types::utf8::Utf8;
    use crate::types::{build, JsArray, JsValue, Value};
    use neon_runtime::raw;

    /// A property key in a JavaScript object.
    pub trait PropertyKey {
        unsafe fn get_from(self, out: &mut raw::Local, obj: raw::Local) -> bool;
        unsafe fn set_from(self, out: &mut bool, obj: raw::Local, val: raw::Local) -> bool;
    }

    impl PropertyKey for u32 {
        unsafe fn get_from(self, out: &mut raw::Local, obj: raw::Local) -> bool {
            neon_runtime::object::get_index(out, obj, self)
        }

        unsafe fn set_from(self, out: &mut bool, obj: raw::Local, val: raw::Local) -> bool {
            neon_runtime::object::set_index(out, obj, self, val)
        }
    }

    impl<'a, K: Value> PropertyKey for Handle<'a, K> {
        unsafe fn get_from(self, out: &mut raw::Local, obj: raw::Local) -> bool {
            neon_runtime::object::get(out, obj, self.to_raw())
        }

        unsafe fn set_from(self, out: &mut bool, obj: raw::Local, val: raw::Local) -> bool {
            neon_runtime::object::set(out, obj, self.to_raw(), val)
        }
    }

    impl<'a> PropertyKey for &'a str {
        unsafe fn get_from(self, out: &mut raw::Local, obj: raw::Local) -> bool {
            let (ptr, len) = Utf8::from(self).into_small_unwrap().lower();
            neon_runtime::object::get_string(out, obj, ptr, len)
        }

        unsafe fn set_from(self, out: &mut bool, obj: raw::Local, val: raw::Local) -> bool {
            let (ptr, len) = Utf8::from(self).into_small_unwrap().lower();
            neon_runtime::object::set_string(out, obj, ptr, len, val)
        }
    }

    /// The trait of all object types.
    pub trait Object: Value {
        fn get<'a, V: Value, C: Context<'a>, K: PropertyKey>(
            &self,
            cx: &mut C,
            key: K,
        ) -> NeonResult<Handle<'a, V>> {
            let v: Handle<JsValue> =
                build(cx.env(), |out| unsafe { key.get_from(out, self.to_raw()) })?;
            v.downcast_or_throw(cx)
        }

        fn get_own_property_names<'a, C: Context<'a>>(&self, cx: &mut C) -> JsResult<'a, JsArray> {
            let env = cx.env();
            build(env, |out| unsafe {
                neon_runtime::object::get_own_property_names(out, env.to_raw(), self.to_raw())
            })
        }

        fn set<'a, C: Context<'a>, K: PropertyKey, W: Value>(
            &self,
            _: &mut C,
            key: K,
            val: Handle<W>,
        ) -> NeonResult<bool> {
            let mut result = false;
            if unsafe { key.set_from(&mut result, self.to_raw(), val.to_raw()) } {
                Ok(result)
            } else {
                Err(Throw::new())
            }
        }
    }

    /// The trait of types that can be a function's `this` binding.
    pub unsafe trait This: Managed {
        #[allow(clippy::wrong_self_convention)]
        fn as_this(h: raw::Local) -> Self;
    }
}

#[cfg(feature = "napi-1")]
mod traits {
    use crate::context::internal::Env;
    use crate::context::Context;
    use crate::handle::{Handle, Managed, Root};
    use crate::result::{NeonResult, Throw};
    use crate::types::utf8::Utf8;
    use crate::types::{build, JsUndefined, JsValue, Value};
    use neon_runtime::raw;

    #[cfg(feature = "napi-6")]
    use crate::result::JsResult;
    #[cfg(feature = "napi-6")]
    use crate::types::JsArray;

    /// A property key in a JavaScript object.
    pub trait PropertyKey {
        unsafe fn get_from<'c, C: Context<'c>>(
            self,
            cx: &mut C,
            out: &mut raw::Local,
            obj: raw::Local,
        ) -> bool;

        unsafe fn set_from<'c, C: Context<'c>>(
            self,
            cx: &mut C,
            out: &mut bool,
            obj: raw::Local,
            val: raw::Local,
        ) -> bool;
    }

    impl PropertyKey for u32 {
        unsafe fn get_from<'c, C: Context<'c>>(
            self,
            cx: &mut C,
            out: &mut raw::Local,
            obj: raw::Local,
        ) -> bool {
            neon_runtime::object::get_index(out, cx.env().to_raw(), obj, self)
        }

        unsafe fn set_from<'c, C: Context<'c>>(
            self,
            cx: &mut C,
            out: &mut bool,
            obj: raw::Local,
            val: raw::Local,
        ) -> bool {
            neon_runtime::object::set_index(out, cx.env().to_raw(), obj, self, val)
        }
    }

    impl<'a, K: Value> PropertyKey for Handle<'a, K> {
        unsafe fn get_from<'c, C: Context<'c>>(
            self,
            cx: &mut C,
            out: &mut raw::Local,
            obj: raw::Local,
        ) -> bool {
            let env = cx.env().to_raw();

            neon_runtime::object::get(out, env, obj, self.to_raw())
        }

        unsafe fn set_from<'c, C: Context<'c>>(
            self,
            cx: &mut C,
            out: &mut bool,
            obj: raw::Local,
            val: raw::Local,
        ) -> bool {
            let env = cx.env().to_raw();

            neon_runtime::object::set(out, env, obj, self.to_raw(), val)
        }
    }

    impl<'a> PropertyKey for &'a str {
        unsafe fn get_from<'c, C: Context<'c>>(
            self,
            cx: &mut C,
            out: &mut raw::Local,
            obj: raw::Local,
        ) -> bool {
            let (ptr, len) = Utf8::from(self).into_small_unwrap().lower();
            let env = cx.env().to_raw();

            neon_runtime::object::get_string(env, out, obj, ptr, len)
        }

        unsafe fn set_from<'c, C: Context<'c>>(
            self,
            cx: &mut C,
            out: &mut bool,
            obj: raw::Local,
            val: raw::Local,
        ) -> bool {
            let (ptr, len) = Utf8::from(self).into_small_unwrap().lower();
            let env = cx.env().to_raw();

            neon_runtime::object::set_string(env, out, obj, ptr, len, val)
        }
    }

    /// The trait of all object types.
    pub trait Object: Value {
        /// Gets a property from a JavaScript object that may be `undefined` and
        /// attempts to downcast the value if it existed.
        fn get_opt<'a, V: Value, C: Context<'a>, K: PropertyKey>(
            &self,
            cx: &mut C,
            key: K,
        ) -> NeonResult<Option<Handle<'a, V>>> {
            let v = self.get_value(cx, key)?;

            if v.is_a::<JsUndefined, _>(cx) {
                return Ok(None);
            }

            v.downcast_or_throw(cx).map(Some)
        }

        /// Gets a property from a JavaScript object as a [`JsValue`].
        ///
        /// If a [`getter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get)
        /// is defined on the object, it will be called.
        fn get_value<'a, C: Context<'a>, K: PropertyKey>(
            &self,
            cx: &mut C,
            key: K,
        ) -> NeonResult<Handle<'a, JsValue>> {
            build(cx.env(), |out| unsafe {
                key.get_from(cx, out, self.to_raw())
            })
        }

        /// Gets a property from a JavaScript object and attempts to downcast as a specific type.
        /// Equivalent to calling `obj.get(&mut cx)?.downcast_or_throw(&mut cx)`.
        ///
        /// Throws an exception if the value is a different type.
        fn get<'a, V: Value, C: Context<'a>, K: PropertyKey>(
            &self,
            cx: &mut C,
            key: K,
        ) -> NeonResult<Handle<'a, V>> {
            self.get_value(cx, key)?.downcast_or_throw(cx)
        }

        #[cfg(feature = "napi-6")]
        #[cfg_attr(docsrs, doc(cfg(feature = "napi-6")))]
        fn get_own_property_names<'a, C: Context<'a>>(&self, cx: &mut C) -> JsResult<'a, JsArray> {
            let env = cx.env();

            build(cx.env(), |out| unsafe {
                neon_runtime::object::get_own_property_names(out, env.to_raw(), self.to_raw())
            })
        }

        fn set<'a, C: Context<'a>, K: PropertyKey, W: Value>(
            &self,
            cx: &mut C,
            key: K,
            val: Handle<W>,
        ) -> NeonResult<bool> {
            let mut result = false;
            if unsafe { key.set_from(cx, &mut result, self.to_raw(), val.to_raw()) } {
                Ok(result)
            } else {
                Err(Throw::new())
            }
        }

        fn root<'a, C: Context<'a>>(&self, cx: &mut C) -> Root<Self> {
            Root::new(cx, self)
        }
    }

    /// The trait of types that can be a function's `this` binding.
    pub unsafe trait This: Managed {
        #[allow(clippy::wrong_self_convention)]
        fn as_this(env: Env, h: raw::Local) -> Self;
    }
}
<commit_msg>Add `Object::call_with()` convenience method to call a method on an object.<commit_after>//! Traits for working with JavaScript objects.
//!
//! This module defines the [`Object`](Object) trait, which is implemented
//! by all object types in the [JavaScript type hierarchy][hierarchy]. This
//! trait provides key operations in the semantics of JavaScript objects,
//! such as getting and setting an object's properties.
//!
//! ## Property Keys
//!
//! Object properties are accessed by a _property key_, which in JavaScript
//! can be a string or [symbol][symbol]. (Neon does not yet have support for
//! symbols.) For convenience, the [`PropertyKey`](PropertyKey) trait allows
//! Neon programs to use various Rust string types, as well as numeric types,
//! as keys when accessing object properties, converting the keys to strings
//! as necessary:
//!
//! ```
//! # #[cfg(feature = "napi-1")] {
//! # use neon::prelude::*;
//! fn set_and_check<'a>(
//!     cx: &mut impl Context<'a>,
//!     obj: Handle<'a, JsObject>
//! ) -> JsResult<'a, JsValue> {
//!     let value = cx.string("hello!");
//!     // set property "17" with integer shorthand
//!     obj.set(cx, 17, value)?;
//!     // get property "17" with string shorthand
//!     // returns the same value ("hello!")
//!     obj.get(cx, "17")
//! }
//! # }
//! ```
//!
//! [hierarchy]: crate::types#the-javascript-type-hierarchy
//! [symbol]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol

#[cfg(feature = "legacy-runtime")]
pub(crate) mod class;

#[cfg(feature = "legacy-runtime")]
pub use self::class::{Class, ClassDescriptor};
pub use self::traits::*;

#[cfg(feature = "legacy-runtime")]
mod traits {
    use crate::context::Context;
    use crate::handle::{Handle, Managed};
    use crate::result::{JsResult, NeonResult, Throw};
    use crate::types::utf8::Utf8;
    use crate::types::{build, JsArray, JsValue, Value};
    use neon_runtime::raw;

    /// A property key in a JavaScript object.
    pub trait PropertyKey {
        unsafe fn get_from(self, out: &mut raw::Local, obj: raw::Local) -> bool;
        unsafe fn set_from(self, out: &mut bool, obj: raw::Local, val: raw::Local) -> bool;
    }

    impl PropertyKey for u32 {
        unsafe fn get_from(self, out: &mut raw::Local, obj: raw::Local) -> bool {
            neon_runtime::object::get_index(out, obj, self)
        }

        unsafe fn set_from(self, out: &mut bool, obj: raw::Local, val: raw::Local) -> bool {
            neon_runtime::object::set_index(out, obj, self, val)
        }
    }

    impl<'a, K: Value> PropertyKey for Handle<'a, K> {
        unsafe fn get_from(self, out: &mut raw::Local, obj: raw::Local) -> bool {
            neon_runtime::object::get(out, obj, self.to_raw())
        }

        unsafe fn set_from(self, out: &mut bool, obj: raw::Local, val: raw::Local) -> bool {
            neon_runtime::object::set(out, obj, self.to_raw(), val)
        }
    }

    impl<'a> PropertyKey for &'a str {
        unsafe fn get_from(self, out: &mut raw::Local, obj: raw::Local) -> bool {
            let (ptr, len) = Utf8::from(self).into_small_unwrap().lower();
            neon_runtime::object::get_string(out, obj, ptr, len)
        }

        unsafe fn set_from(self, out: &mut bool, obj: raw::Local, val: raw::Local) -> bool {
            let (ptr, len) = Utf8::from(self).into_small_unwrap().lower();
            neon_runtime::object::set_string(out, obj, ptr, len, val)
        }
    }

    /// The trait of all object types.
    pub trait Object: Value {
        fn get<'a, V: Value, C: Context<'a>, K: PropertyKey>(
            &self,
            cx: &mut C,
            key: K,
        ) -> NeonResult<Handle<'a, V>> {
            let v: Handle<JsValue> =
                build(cx.env(), |out| unsafe { key.get_from(out, self.to_raw()) })?;
            v.downcast_or_throw(cx)
        }

        fn get_own_property_names<'a, C: Context<'a>>(&self, cx: &mut C) -> JsResult<'a, JsArray> {
            let env = cx.env();
            build(env, |out| unsafe {
                neon_runtime::object::get_own_property_names(out, env.to_raw(), self.to_raw())
            })
        }

        fn set<'a, C: Context<'a>, K: PropertyKey, W: Value>(
            &self,
            _: &mut C,
            key: K,
            val: Handle<W>,
        ) -> NeonResult<bool> {
            let mut result = false;
            if unsafe { key.set_from(&mut result, self.to_raw(), val.to_raw()) } {
                Ok(result)
            } else {
                Err(Throw::new())
            }
        }
    }

    /// The trait of types that can be a function's `this` binding.
    pub unsafe trait This: Managed {
        #[allow(clippy::wrong_self_convention)]
        fn as_this(h: raw::Local) -> Self;
    }
}

#[cfg(feature = "napi-1")]
mod traits {
    use crate::context::internal::Env;
    use crate::context::Context;
    use crate::handle::{Handle, Managed, Root};
    use crate::result::{NeonResult, Throw};
    use crate::types::function::CallOptions;
    use crate::types::utf8::Utf8;
    use crate::types::{build, JsUndefined, JsValue, Value, JsFunction};
    use neon_runtime::raw;

    #[cfg(feature = "napi-6")]
    use crate::result::JsResult;
    #[cfg(feature = "napi-6")]
    use crate::types::JsArray;

    /// A property key in a JavaScript object.
    pub trait PropertyKey {
        unsafe fn get_from<'c, C: Context<'c>>(
            self,
            cx: &mut C,
            out: &mut raw::Local,
            obj: raw::Local,
        ) -> bool;

        unsafe fn set_from<'c, C: Context<'c>>(
            self,
            cx: &mut C,
            out: &mut bool,
            obj: raw::Local,
            val: raw::Local,
        ) -> bool;
    }

    impl PropertyKey for u32 {
        unsafe fn get_from<'c, C: Context<'c>>(
            self,
            cx: &mut C,
            out: &mut raw::Local,
            obj: raw::Local,
        ) -> bool {
            neon_runtime::object::get_index(out, cx.env().to_raw(), obj, self)
        }

        unsafe fn set_from<'c, C: Context<'c>>(
            self,
            cx: &mut C,
            out: &mut bool,
            obj: raw::Local,
            val: raw::Local,
        ) -> bool {
            neon_runtime::object::set_index(out, cx.env().to_raw(), obj, self, val)
        }
    }

    impl<'a, K: Value> PropertyKey for Handle<'a, K> {
        unsafe fn get_from<'c, C: Context<'c>>(
            self,
            cx: &mut C,
            out: &mut raw::Local,
            obj: raw::Local,
        ) -> bool {
            let env = cx.env().to_raw();

            neon_runtime::object::get(out, env, obj, self.to_raw())
        }

        unsafe fn set_from<'c, C: Context<'c>>(
            self,
            cx: &mut C,
            out: &mut bool,
            obj: raw::Local,
            val: raw::Local,
        ) -> bool {
            let env = cx.env().to_raw();

            neon_runtime::object::set(out, env, obj, self.to_raw(), val)
        }
    }

    impl<'a> PropertyKey for &'a str {
        unsafe fn get_from<'c, C: Context<'c>>(
            self,
            cx: &mut C,
            out: &mut raw::Local,
            obj: raw::Local,
        ) -> bool {
            let (ptr, len) = Utf8::from(self).into_small_unwrap().lower();
            let env = cx.env().to_raw();

            neon_runtime::object::get_string(env, out, obj, ptr, len)
        }

        unsafe fn set_from<'c, C: Context<'c>>(
            self,
            cx: &mut C,
            out: &mut bool,
            obj: raw::Local,
            val: raw::Local,
        ) -> bool {
            let (ptr, len) = Utf8::from(self).into_small_unwrap().lower();
            let env = cx.env().to_raw();

            neon_runtime::object::set_string(env, out, obj, ptr, len, val)
        }
    }

    /// The trait of all object types.
    pub trait Object: Value {
        /// Gets a property from a JavaScript object that may be `undefined` and
        /// attempts to downcast the value if it existed.
        fn get_opt<'a, V: Value, C: Context<'a>, K: PropertyKey>(
            &self,
            cx: &mut C,
            key: K,
        ) -> NeonResult<Option<Handle<'a, V>>> {
            let v = self.get_value(cx, key)?;

            if v.is_a::<JsUndefined, _>(cx) {
                return Ok(None);
            }

            v.downcast_or_throw(cx).map(Some)
        }

        /// Gets a property from a JavaScript object as a [`JsValue`].
        ///
        /// If a [`getter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get)
        /// is defined on the object, it will be called.
        fn get_value<'a, C: Context<'a>, K: PropertyKey>(
            &self,
            cx: &mut C,
            key: K,
        ) -> NeonResult<Handle<'a, JsValue>> {
            build(cx.env(), |out| unsafe {
                key.get_from(cx, out, self.to_raw())
            })
        }

        /// Gets a property from a JavaScript object and attempts to downcast as a specific type.
        /// Equivalent to calling `obj.get(&mut cx)?.downcast_or_throw(&mut cx)`.
        ///
        /// Throws an exception if the value is a different type.
        fn get<'a, V: Value, C: Context<'a>, K: PropertyKey>(
            &self,
            cx: &mut C,
            key: K,
        ) -> NeonResult<Handle<'a, V>> {
            self.get_value(cx, key)?.downcast_or_throw(cx)
        }

        #[cfg(feature = "napi-6")]
        #[cfg_attr(docsrs, doc(cfg(feature = "napi-6")))]
        fn get_own_property_names<'a, C: Context<'a>>(&self, cx: &mut C) -> JsResult<'a, JsArray> {
            let env = cx.env();

            build(cx.env(), |out| unsafe {
                neon_runtime::object::get_own_property_names(out, env.to_raw(), self.to_raw())
            })
        }

        fn set<'a, C: Context<'a>, K: PropertyKey, W: Value>(
            &self,
            cx: &mut C,
            key: K,
            val: Handle<W>,
        ) -> NeonResult<bool> {
            let mut result = false;
            if unsafe { key.set_from(cx, &mut result, self.to_raw(), val.to_raw()) } {
                Ok(result)
            } else {
                Err(Throw::new())
            }
        }

        fn root<'a, C: Context<'a>>(&self, cx: &mut C) -> Root<Self> {
            Root::new(cx, self)
        }

        fn call_with<'a, C, K>(&self, cx: &mut C, method: K) -> NeonResult<CallOptions<'a>>
        where
            C: Context<'a>,
            K: PropertyKey,
        {
            let mut options = self.get::<JsFunction, _, _>(cx, method)?.call_with(cx);
            options.this(Handle::new_internal(Self::from_raw(cx.env(), self.to_raw())));
            Ok(options)
        }
    }

    /// The trait of types that can be a function's `this` binding.
    pub unsafe trait This: Managed {
        #[allow(clippy::wrong_self_convention)]
        fn as_this(env: Env, h: raw::Local) -> Self;
    }
}
<|endoftext|>


<commit_before>enum List< T > {
	Empty,
	Cons{
		head : T,
		tail : Box< List< T > >
	},
}

struct ListIterator< 'a, T : 'a > {
	cur : &'a List< T >,
}

impl< 'a, T > IntoIterator for &'a List< T > {
	type Item = &'a T;
	type IntoIter = ListIterator< 'a, T >;
	fn into_iter( self ) -> Self::IntoIter {
		ListIterator{ cur : self }
	}
}

impl < 'a, T > Iterator for ListIterator< 'a, T > {
	type Item = &'a T;

	fn next( &mut self ) -> Option< &'a T > {
		match self.cur {
			&List::Empty => None,
			&List::Cons{ ref head, ref tail } => {
				self.cur = tail;
				Some( head )
			},
		}
	}
}

fn main() {
	let list = List::Cons{ head : 42, tail : Box::new(
		List::Cons{ head : 1337, tail : Box::new(
			List::Cons{ head : -1, tail : Box::new(
				List::Empty ) } ) } ) };
	for x in &list {
		println!( "{}", x );
	}
}
<commit_msg>helper functions<commit_after>enum List< T > {
	Empty,
	Cons{
		head : T,
		tail : Box< List< T > >
	},
}

impl< T > List< T > {
	fn mk_empty() -> List< T > {
		List::Empty
	}
	fn cons( value : T, list : List< T > ) -> List < T > {
		List::Cons{ head : value, tail : Box::new( list ) }
	}
	fn mk_one( value : T ) -> List< T > {
		List::cons( value, List::mk_empty() )
	}
}

struct ListIterator< 'a, T : 'a > {
	cur : &'a List< T >,
}

impl< 'a, T > IntoIterator for &'a List< T > {
	type Item = &'a T;
	type IntoIter = ListIterator< 'a, T >;
	fn into_iter( self ) -> Self::IntoIter {
		ListIterator{ cur : self }
	}
}

impl < 'a, T > Iterator for ListIterator< 'a, T > {
	type Item = &'a T;

	fn next( &mut self ) -> Option< &'a T > {
		match self.cur {
			&List::Empty => None,
			&List::Cons{ ref head, ref tail } => {
				self.cur = tail;
				Some( head )
			},
		}
	}
}

// see also: "Entirely Too Many Linked Lists" for alternatives

fn main() {
	let list = List::cons( 42, List::cons( 1337, List::mk_one( -1 ) ) );
	for x in &list {
		println!( "{}", x );
	}
}
<|endoftext|>


<commit_before>//
// gash.rs
//
// Starting code for PS2
// Running on Rust 0.9
//
// University of Virginia - cs4414 Spring 2014
// Weilin Xu, David Evans
// Version 0.4
//

extern mod extra;

use std::{io, run, os};
use std::io::buffered::BufferedReader;
use std::io::stdin;
use extra::getopts;

struct Shell {
    cmd_prompt: ~str,
}

impl Shell {
    fn new(prompt_str: &str) -> Shell {
        Shell {
            cmd_prompt: prompt_str.to_owned(),
        }
    }

    fn run(&mut self) {
        let mut stdin = BufferedReader::new(stdin());

        loop {
            print(self.cmd_prompt);
            io::stdio::flush();

            let line = stdin.read_line().unwrap();
            let cmd_line = line.trim().to_owned();
            let program = cmd_line.splitn(' ', 1).nth(0).expect("no program");

            match program {
                ""      =>  { continue; }
                "exit"  =>  { return; }
                _       =>  { self.run_cmdline(cmd_line); }
            }
        }
    }

    fn run_cmdline(&mut self, cmd_line: &str) {
        let mut argv: ~[~str] =
            cmd_line.split(' ').filter_map(|x| if x != "" { Some(x.to_owned()) } else { None }).to_owned_vec();

        if argv.len() > 0 {
            let program: ~str = argv.remove(0);
            self.run_cmd(program, argv);
        }
    }

    fn run_cmd(&mut self, program: &str, argv: &[~str]) {
        if self.cmd_exists(program) {
            run::process_status(program, argv);
        } else {
            println!("{:s}: command not found", program);
        }
    }

    fn cmd_exists(&mut self, cmd_path: &str) -> bool {
        let ret = run::process_output("which", [cmd_path.to_owned()]);
        return ret.expect("exit code error.").status.success();
    }
}

fn get_cmdline_from_args() -> Option<~str> {
    /* Begin processing program arguments and initiate the parameters. */
    let args = os::args();

    let opts = ~[
        getopts::optopt("c")
    ];

    let matches = match getopts::getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => { fail!(f.to_err_msg()) }
    };

    if matches.opt_present("c") {
        let cmd_str = match matches.opt_str("c") {
                                                Some(cmd_str) => {cmd_str.to_owned()},
                                                None => {~""}
                                              };
        return Some(cmd_str);
    } else {
        return None;
    }
}

fn main() {
    let opt_cmd_line = get_cmdline_from_args();

    match opt_cmd_line {
        Some(cmd_line) => Shell::new("").run_cmdline(cmd_line),
        None           => Shell::new("gash > ").run()
    }
}
<commit_msg>piping and redirection<commit_after>//
// gash.rs
//
// Starting code for PS2
// Running on Rust 0.9
//
// University of Virginia - cs4414 Spring 2014
// Weilin Xu, David Evans
// Version 0.4
//

extern mod extra;

use std::{io, run, os};
use std::io::buffered::BufferedReader;
use std::io::stdin;
use extra::getopts;
use std::io::File;
// use std::run::Process;
// use std::run::ProcessOptions;
// use std::libc;
// extra::future;
// use std::task::task;

struct Shell {
    cmd_prompt: ~str,
}

impl Shell {
    fn new(prompt_str: &str) -> Shell {
        Shell {
            cmd_prompt: prompt_str.to_owned(),
        }
    }

    fn run(&mut self) {
        let mut stdin = BufferedReader::new(stdin());
        let mut cmd_history = ~[];

        loop {
            print(self.cmd_prompt);
            io::stdio::flush();

            let line = stdin.read_line().unwrap();
            let cmd_line = line.trim().to_owned();
            let program = cmd_line.splitn(' ', 1).nth(0).expect("no program");
            let args = cmd_line.split(' ').skip(1);

            cmd_history.push(cmd_line.clone());

            match args.clone().last() {
                Some("&") => {
                    let arg_list: ~[~str] =
                        args.clone().filter_map(|x| if x != "&" { Some(x.to_owned()) } else { None }).to_owned_vec();
                    let prog = program.to_owned();
                    spawn(proc() { run::process_status(prog, arg_list); });
                    continue;
                }
                _ => {}
            }

            // fix needed: support for command ./zhttpto > zlog.txt &
            let mut iter = args.clone();
            match iter.find(|&x| x==">") {
                Some(">") => {
                    let mut arg_list: ~[~str] = ~[];
                    args.clone().advance(|x| if x != ">" { arg_list.push(x.to_owned()); true } else { false });
                    let buffer = run::process_output(program, arg_list);
                    let path = &Path::new(iter.next().unwrap());
                    let output = File::open_mode(path, io::Truncate, io::Write);
                    match output {
                        Some(mut file) => file.write(buffer.unwrap().output),
                        None => fail!("Error: Could not write to file.")
                    }
                    continue;
                }
                _ => {}
            }

            // note: ambiguity on stdin redirection
            let mut iter = args.clone();
            match iter.find(|&x| x=="<") {
                Some("<") => {
                    let mut arg_list: ~[~str] = ~[];
                    args.clone().advance(|x| if x != "<" { arg_list.push(x.to_owned()); true } else { false });
                    // let file = File::open(&Path::new(iter.next().unwrap()));
                    // match file {
                    //     Some(f) => arg_list.push(f),
                    //     _ => fail!("Error: File not found")
                    // }
                    arg_list.push(iter.next().unwrap().to_owned());
                    run::process_status(program, arg_list);
                    continue;
                }
                _ => {}
            }

            let mut iter = args.clone();
            match iter.find(|&x| x=="|") {
                Some("|") => {
                    let mut arg_list: ~[~str] = ~[];
                    args.clone().advance(|x| if x != "|" { arg_list.push(x.to_owned()); true } else { false });
                    let buffer = run::process_output(program, arg_list);
                    let target = iter.next().unwrap();
                    arg_list = ~[];
                    iter.advance(|x| if x != "" { arg_list.push(x.to_owned()); true } else { false });
                    arg_list.push(std::str::from_utf8_owned(buffer.unwrap().output));
                    run::process_status(target, arg_list);
                    continue;
                }
                _ => {}
            }

            match program {
                ""      =>  { continue; }
                "exit"  =>  { return; }
                "cd"    =>  { self.cd(cmd_line); }
                "history"    =>  { self.history(cmd_history.clone()); }
                _       =>  { self.run_cmdline(cmd_line); }
            }
        }
    }

    fn history(&mut self, cmd_history: ~[~str]) {
        for i in range(0, cmd_history.len()) {
            println!("   {}  {:s}", i+1, cmd_history[i]);
        }
    }

    fn cd(&mut self,  cmd_line: &str) {
        let argv: ~[~str] =
            cmd_line.split(' ').filter_map(|x| if x != "" { Some(x.to_owned()) } else { None }).to_owned_vec();

        if argv.len() > 1 {
            let path = &Path::new(argv[1]);
            //println!("path: {}", path.display());
            os::change_dir(path);
        }
    }

    fn run_cmdline(&mut self, cmd_line: &str) {
        let mut argv: ~[~str] =
            cmd_line.split(' ').filter_map(|x| if x != "" { Some(x.to_owned()) } else { None }).to_owned_vec();

        if argv.len() > 0 {
            let program: ~str = argv.remove(0);
            self.run_cmd(program, argv);
        }
    }

    fn run_cmd(&mut self, program: &str, argv: &[~str]) {
        if self.cmd_exists(program) {
            run::process_status(program, argv);
        } else {
            println!("{:s}: command not found", program);
        }
    }

    fn cmd_exists(&mut self, cmd_path: &str) -> bool {
        let ret = run::process_output("which", [cmd_path.to_owned()]);
        return ret.expect("exit code error.").status.success();
    }
}

fn get_cmdline_from_args() -> Option<~str> {
    /* Begin processing program arguments and initiate the parameters. */
    let args = os::args();

    let opts = ~[
        getopts::optopt("c")
    ];

    let matches = match getopts::getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => { fail!(f.to_err_msg()) }
    };

    if matches.opt_present("c") {
        let cmd_str = match matches.opt_str("c") {
            Some(cmd_str) => {cmd_str.to_owned()},
            None => {~""}
        };
        return Some(cmd_str);
    } else {
        return None;
    }
}

fn main() {
    let opt_cmd_line = get_cmdline_from_args();

    match opt_cmd_line {
        Some(cmd_line) => Shell::new("").run_cmdline(cmd_line),
        None           => Shell::new("gash > ").run()
    }
}
<|endoftext|>


<commit_before>//! Implementation of `Filter` for certain non-probabilistic data structures. This can be helpful
//! for debugging and performance comparisons.
use std::collections::HashSet;
use std::hash::{BuildHasher, Hash};

use void::Void;

use crate::filters::Filter;

impl<T, S> Filter<T> for HashSet<T, S>
where
    T: Clone + Eq + Hash,
    S: BuildHasher,
{
    type InsertErr = Void;

    fn clear(&mut self) {
        self.clear();
    }

    fn insert(&mut self, obj: &T) -> Result<bool, Self::InsertErr> {
        Ok(self.insert(obj.clone()))
    }

    fn is_empty(&self) -> bool {
        self.is_empty()
    }

    fn len(&self) -> usize {
        self.len()
    }

    fn query(&self, obj: &T) -> bool {
        self.contains(obj)
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashSet;

    use void::Void;

    use crate::filters::Filter;

    #[test]
    fn hashset() {
        let set: &mut Filter<u64, InsertErr = Void> = &mut HashSet::new();
        assert!(set.is_empty());
        assert_eq!(set.len(), 0);
        assert!(!set.query(&42));

        assert!(set.insert(&42).unwrap());
        assert!(!set.insert(&42).unwrap());
        assert!(!set.is_empty());
        assert_eq!(set.len(), 1);
        assert!(set.query(&42));
        assert!(!set.query(&13));
    }
}
<commit_msg>add 'dyn' suggested by RA<commit_after>//! Implementation of `Filter` for certain non-probabilistic data structures. This can be helpful
//! for debugging and performance comparisons.
use std::collections::HashSet;
use std::hash::{BuildHasher, Hash};

use void::Void;

use crate::filters::Filter;

impl<T, S> Filter<T> for HashSet<T, S>
where
    T: Clone + Eq + Hash,
    S: BuildHasher,
{
    type InsertErr = Void;

    fn clear(&mut self) {
        self.clear();
    }

    fn insert(&mut self, obj: &T) -> Result<bool, Self::InsertErr> {
        Ok(self.insert(obj.clone()))
    }

    fn is_empty(&self) -> bool {
        self.is_empty()
    }

    fn len(&self) -> usize {
        self.len()
    }

    fn query(&self, obj: &T) -> bool {
        self.contains(obj)
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashSet;

    use void::Void;

    use crate::filters::Filter;

    #[test]
    fn hashset() {
        let set: &mut dyn Filter<u64, InsertErr = Void> = &mut HashSet::new();
        assert!(set.is_empty());
        assert_eq!(set.len(), 0);
        assert!(!set.query(&42));

        assert!(set.insert(&42).unwrap());
        assert!(!set.insert(&42).unwrap());
        assert!(!set.is_empty());
        assert_eq!(set.len(), 1);
        assert!(set.query(&42));
        assert!(!set.query(&13));
    }
}
<|endoftext|>


<commit_before>//! Formatting for the time crate.

pub(crate) mod formattable;

use std::io;

use crate::format_description::{modifier, Component};
use crate::{error, Date, Time, UtcOffset};

#[allow(clippy::clippy::missing_docs_in_private_items)]
const MONTH_NAMES: [&str; 12] = [
    "January",
    "February",
    "March",
    "April",
    "May",
    "June",
    "July",
    "August",
    "September",
    "October",
    "November",
    "December",
];

#[allow(clippy::missing_docs_in_private_items)]
const WEEKDAY_NAMES: [&str; 7] = [
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday",
    "Saturday",
    "Sunday",
];

/// A trait that indicates the formatted width of the value can be determined.
///
/// Note that this should not be implemented for any signed integers. This forces the caller to
/// write the sign if desired.
pub(crate) trait DigitCount {
    /// The number of digits in the stringified value.
    fn num_digits(self) -> u8;
}
impl DigitCount for u8 {
    fn num_digits(self) -> u8 {
        if self < 10 {
            1
        } else if self < 100 {
            2
        } else {
            3
        }
    }
}
impl DigitCount for u16 {
    fn num_digits(self) -> u8 {
        if self < 10 {
            1
        } else if self < 100 {
            2
        } else if self < 1_000 {
            3
        } else if self < 10_000 {
            4
        } else {
            5
        }
    }
}
impl DigitCount for u32 {
    fn num_digits(self) -> u8 {
        if self < 10 {
            1
        } else if self < 100 {
            2
        } else if self < 1_000 {
            3
        } else if self < 10_000 {
            4
        } else if self < 100_000 {
            5
        } else if self < 1_000_000 {
            6
        } else if self < 10_000_000 {
            7
        } else if self < 100_000_000 {
            8
        } else if self < 1_000_000_000 {
            9
        } else {
            10
        }
    }
}

/// Format a number with the provided padding and width.
///
/// The sign must be written by the caller.
pub(crate) fn format_number(
    output: &mut impl io::Write,
    value: impl itoa::Integer + DigitCount + Copy,
    padding: modifier::Padding,
    width: u8,
) -> Result<usize, io::Error> {
    match padding {
        modifier::Padding::Space => {
            let mut bytes = 0;
            for _ in 0..(width.saturating_sub(value.num_digits())) {
                bytes += output.write(&[b' '])?;
            }
            bytes += itoa::write(output, value)?;
            Ok(bytes)
        }
        modifier::Padding::Zero => {
            let mut bytes = 0;
            for _ in 0..(width.saturating_sub(value.num_digits())) {
                bytes += output.write(&[b'0'])?;
            }
            bytes += itoa::write(output, value)?;
            Ok(bytes)
        }
        modifier::Padding::None => itoa::write(output, value),
    }
}

/// Format the provided component into the designated output. An `Err` will be returned if the
/// component requires information that it does not provide or if the value cannot be output to the
/// stream.
pub(crate) fn format_component(
    output: &mut impl io::Write,
    component: Component,
    date: Option<Date>,
    time: Option<Time>,
    offset: Option<UtcOffset>,
) -> Result<usize, error::Format> {
    use Component::*;
    Ok(match (component, date, time, offset) {
        (Day(modifier), Some(date), ..) => fmt_day(output, date, modifier)?,
        (Month(modifier), Some(date), ..) => fmt_month(output, date, modifier)?,
        (Ordinal(modifier), Some(date), ..) => fmt_ordinal(output, date, modifier)?,
        (Weekday(modifier), Some(date), ..) => fmt_weekday(output, date, modifier)?,
        (WeekNumber(modifier), Some(date), ..) => fmt_week_number(output, date, modifier)?,
        (Year(modifier), Some(date), ..) => fmt_year(output, date, modifier)?,
        (Hour(modifier), _, Some(time), _) => fmt_hour(output, time, modifier)?,
        (Minute(modifier), _, Some(time), _) => fmt_minute(output, time, modifier)?,
        (Period(modifier), _, Some(time), _) => fmt_period(output, time, modifier)?,
        (Second(modifier), _, Some(time), _) => fmt_second(output, time, modifier)?,
        (Subsecond(modifier), _, Some(time), _) => fmt_subsecond(output, time, modifier)?,
        (OffsetHour(modifier), .., Some(offset)) => fmt_offset_hour(output, offset, modifier)?,
        (OffsetMinute(modifier), .., Some(offset)) => fmt_offset_minute(output, offset, modifier)?,
        (OffsetSecond(modifier), .., Some(offset)) => fmt_offset_second(output, offset, modifier)?,
        _ => return Err(error::Format::InsufficientTypeInformation),
    })
}

/// Format the day into the designated output.
fn fmt_day(
    output: &mut impl io::Write,
    date: Date,
    modifier::Day { padding }: modifier::Day,
) -> Result<usize, io::Error> {
    format_number(output, date.day(), padding, 2)
}

/// Format the month into the designated output.
fn fmt_month(
    output: &mut impl io::Write,
    date: Date,
    modifier::Month { padding, repr }: modifier::Month,
) -> Result<usize, io::Error> {
    match repr {
        modifier::MonthRepr::Numerical => format_number(output, date.month(), padding, 2),
        modifier::MonthRepr::Long => {
            output.write(MONTH_NAMES[date.month() as usize - 1].as_bytes())
        }
        modifier::MonthRepr::Short => {
            output.write(MONTH_NAMES[date.month() as usize - 1][..3].as_bytes())
        }
    }
}

/// Format the ordinal into the designated output.
fn fmt_ordinal(
    output: &mut impl io::Write,
    date: Date,
    modifier::Ordinal { padding }: modifier::Ordinal,
) -> Result<usize, io::Error> {
    format_number(output, date.ordinal(), padding, 3)
}

/// Format the weekday into the designated output.
fn fmt_weekday(
    output: &mut impl io::Write,
    date: Date,
    modifier::Weekday { repr, one_indexed }: modifier::Weekday,
) -> Result<usize, io::Error> {
    match repr {
        modifier::WeekdayRepr::Short => output.write(
            WEEKDAY_NAMES[date.weekday().number_days_from_monday() as usize][..3].as_bytes(),
        ),
        modifier::WeekdayRepr::Long => output
            .write(WEEKDAY_NAMES[date.weekday().number_days_from_monday() as usize].as_bytes()),
        modifier::WeekdayRepr::Sunday => format_number(
            output,
            date.weekday().number_days_from_sunday() + one_indexed as u8,
            modifier::Padding::None,
            1,
        ),
        modifier::WeekdayRepr::Monday => format_number(
            output,
            date.weekday().number_days_from_monday() + one_indexed as u8,
            modifier::Padding::None,
            1,
        ),
    }
}

/// Format the week number into the designated output.
fn fmt_week_number(
    output: &mut impl io::Write,
    date: Date,
    modifier::WeekNumber { padding, repr }: modifier::WeekNumber,
) -> Result<usize, io::Error> {
    format_number(
        output,
        match repr {
            modifier::WeekNumberRepr::Iso => date.iso_week(),
            modifier::WeekNumberRepr::Sunday => date.sunday_based_week(),
            modifier::WeekNumberRepr::Monday => date.monday_based_week(),
        },
        padding,
        2,
    )
}

/// Format the year into the designated output.
fn fmt_year(
    output: &mut impl io::Write,
    date: Date,
    modifier::Year {
        padding,
        repr,
        iso_week_based,
        sign_is_mandatory,
    }: modifier::Year,
) -> Result<usize, io::Error> {
    let full_year = if iso_week_based {
        date.iso_year_week().0
    } else {
        date.year()
    };
    let value = match repr {
        modifier::YearRepr::Full => full_year,
        modifier::YearRepr::LastTwo => (full_year % 100).abs(),
    };
    let width = match repr {
        #[cfg(feature = "large-dates")]
        modifier::YearRepr::Full if value.abs() >= 100_000 => 6,
        #[cfg(feature = "large-dates")]
        modifier::YearRepr::Full if value.abs() >= 10_000 => 5,
        modifier::YearRepr::Full => 4,
        modifier::YearRepr::LastTwo => 2,
    };
    let mut bytes = 0;
    if repr != modifier::YearRepr::LastTwo {
        if full_year < 0 {
            bytes += output.write(&[b'-'])?;
        } else if sign_is_mandatory || cfg!(feature = "large-dates") && full_year >= 10_000 {
            bytes += output.write(&[b'+'])?;
        }
    }
    bytes += format_number(output, value.abs() as u32, padding, width)?;
    Ok(bytes)
}

/// Format the hour into the designated output.
fn fmt_hour(
    output: &mut impl io::Write,
    time: Time,
    modifier::Hour {
        padding,
        is_12_hour_clock,
    }: modifier::Hour,
) -> Result<usize, io::Error> {
    let value = match (time.hour, is_12_hour_clock) {
        (hour, false) => hour,
        (0, true) | (12, true) => 12,
        (hour, true) if hour < 12 => hour,
        (hour, true) => hour - 12,
    };
    format_number(output, value, padding, 2)
}

/// Format the minute into the designated output.
fn fmt_minute(
    output: &mut impl io::Write,
    time: Time,
    modifier::Minute { padding }: modifier::Minute,
) -> Result<usize, io::Error> {
    format_number(output, time.minute, padding, 2)
}

/// Format the period into the designated output.
fn fmt_period(
    output: &mut impl io::Write,
    time: Time,
    modifier::Period { is_uppercase }: modifier::Period,
) -> Result<usize, io::Error> {
    match (time.hour >= 12, is_uppercase) {
        (false, false) => output.write(b"am"),
        (false, true) => output.write(b"AM"),
        (true, false) => output.write(b"pm"),
        (true, true) => output.write(b"PM"),
    }
}

/// Format the second into the designated output.
fn fmt_second(
    output: &mut impl io::Write,
    time: Time,
    modifier::Second { padding }: modifier::Second,
) -> Result<usize, io::Error> {
    format_number(output, time.second, padding, 2)
}

/// Format the subsecond into the designated output.
fn fmt_subsecond(
    output: &mut impl io::Write,
    time: Time,
    modifier::Subsecond { digits }: modifier::Subsecond,
) -> Result<usize, io::Error> {
    let (value, width) = match digits {
        modifier::SubsecondDigits::One => (time.nanosecond / 100_000_000, 1),
        modifier::SubsecondDigits::Two => (time.nanosecond / 10_000_000, 2),
        modifier::SubsecondDigits::Three => (time.nanosecond / 1_000_000, 3),
        modifier::SubsecondDigits::Four => (time.nanosecond / 100_000, 4),
        modifier::SubsecondDigits::Five => (time.nanosecond / 10_000, 5),
        modifier::SubsecondDigits::Six => (time.nanosecond / 1_000, 6),
        modifier::SubsecondDigits::Seven => (time.nanosecond / 100, 7),
        modifier::SubsecondDigits::Eight => (time.nanosecond / 10, 8),
        modifier::SubsecondDigits::Nine => (time.nanosecond, 9),
        modifier::SubsecondDigits::OneOrMore => match time.nanosecond {
            nanos if nanos % 10 != 0 => (nanos, 9),
            nanos if (nanos / 10) % 10 != 0 => (nanos / 10, 8),
            nanos if (nanos / 100) % 10 != 0 => (nanos / 100, 7),
            nanos if (nanos / 1_000) % 10 != 0 => (nanos / 1_000, 6),
            nanos if (nanos / 10_000) % 10 != 0 => (nanos / 10_000, 5),
            nanos if (nanos / 100_000) % 10 != 0 => (nanos / 100_000, 4),
            nanos if (nanos / 1_000_000) % 10 != 0 => (nanos / 1_000_000, 3),
            nanos if (nanos / 10_000_000) % 10 != 0 => (nanos / 10_000_000, 2),
            nanos => (nanos / 100_000_000, 1),
        },
    };
    format_number(output, value, modifier::Padding::Zero, width)
}

/// Format the offset hour into the designated output.
fn fmt_offset_hour(
    output: &mut impl io::Write,
    offset: UtcOffset,
    modifier::OffsetHour {
        padding,
        sign_is_mandatory,
    }: modifier::OffsetHour,
) -> Result<usize, io::Error> {
    let mut bytes = 0;
    if offset.is_negative() {
        bytes += output.write(&[b'-'])?;
    } else if sign_is_mandatory {
        bytes += output.write(&[b'+'])?;
    }
    bytes += format_number(output, offset.hours.abs() as u8, padding, 2)?;
    Ok(bytes)
}

/// Format the offset minute into the designated output.
fn fmt_offset_minute(
    output: &mut impl io::Write,
    offset: UtcOffset,
    modifier::OffsetMinute { padding }: modifier::OffsetMinute,
) -> Result<usize, io::Error> {
    format_number(output, offset.minutes.abs() as u8, padding, 2)
}

/// Format the offset second into the designated output.
fn fmt_offset_second(
    output: &mut impl io::Write,
    offset: UtcOffset,
    modifier::OffsetSecond { padding }: modifier::OffsetSecond,
) -> Result<usize, io::Error> {
    format_number(output, offset.seconds.abs() as u8, padding, 2)
}
<commit_msg>Use bytearray for literals<commit_after>//! Formatting for the time crate.

pub(crate) mod formattable;

use std::io;

use crate::format_description::{modifier, Component};
use crate::{error, Date, Time, UtcOffset};

#[allow(clippy::clippy::missing_docs_in_private_items)]
const MONTH_NAMES: [&[u8]; 12] = [
    b"January",
    b"February",
    b"March",
    b"April",
    b"May",
    b"June",
    b"July",
    b"August",
    b"September",
    b"October",
    b"November",
    b"December",
];

#[allow(clippy::missing_docs_in_private_items)]
const WEEKDAY_NAMES: [&[u8]; 7] = [
    b"Monday",
    b"Tuesday",
    b"Wednesday",
    b"Thursday",
    b"Friday",
    b"Saturday",
    b"Sunday",
];

/// A trait that indicates the formatted width of the value can be determined.
///
/// Note that this should not be implemented for any signed integers. This forces the caller to
/// write the sign if desired.
pub(crate) trait DigitCount {
    /// The number of digits in the stringified value.
    fn num_digits(self) -> u8;
}
impl DigitCount for u8 {
    fn num_digits(self) -> u8 {
        if self < 10 {
            1
        } else if self < 100 {
            2
        } else {
            3
        }
    }
}
impl DigitCount for u16 {
    fn num_digits(self) -> u8 {
        if self < 10 {
            1
        } else if self < 100 {
            2
        } else if self < 1_000 {
            3
        } else if self < 10_000 {
            4
        } else {
            5
        }
    }
}
impl DigitCount for u32 {
    fn num_digits(self) -> u8 {
        if self < 10 {
            1
        } else if self < 100 {
            2
        } else if self < 1_000 {
            3
        } else if self < 10_000 {
            4
        } else if self < 100_000 {
            5
        } else if self < 1_000_000 {
            6
        } else if self < 10_000_000 {
            7
        } else if self < 100_000_000 {
            8
        } else if self < 1_000_000_000 {
            9
        } else {
            10
        }
    }
}

/// Format a number with the provided padding and width.
///
/// The sign must be written by the caller.
pub(crate) fn format_number(
    output: &mut impl io::Write,
    value: impl itoa::Integer + DigitCount + Copy,
    padding: modifier::Padding,
    width: u8,
) -> Result<usize, io::Error> {
    match padding {
        modifier::Padding::Space => {
            let mut bytes = 0;
            for _ in 0..(width.saturating_sub(value.num_digits())) {
                bytes += output.write(&[b' '])?;
            }
            bytes += itoa::write(output, value)?;
            Ok(bytes)
        }
        modifier::Padding::Zero => {
            let mut bytes = 0;
            for _ in 0..(width.saturating_sub(value.num_digits())) {
                bytes += output.write(&[b'0'])?;
            }
            bytes += itoa::write(output, value)?;
            Ok(bytes)
        }
        modifier::Padding::None => itoa::write(output, value),
    }
}

/// Format the provided component into the designated output. An `Err` will be returned if the
/// component requires information that it does not provide or if the value cannot be output to the
/// stream.
pub(crate) fn format_component(
    output: &mut impl io::Write,
    component: Component,
    date: Option<Date>,
    time: Option<Time>,
    offset: Option<UtcOffset>,
) -> Result<usize, error::Format> {
    use Component::*;
    Ok(match (component, date, time, offset) {
        (Day(modifier), Some(date), ..) => fmt_day(output, date, modifier)?,
        (Month(modifier), Some(date), ..) => fmt_month(output, date, modifier)?,
        (Ordinal(modifier), Some(date), ..) => fmt_ordinal(output, date, modifier)?,
        (Weekday(modifier), Some(date), ..) => fmt_weekday(output, date, modifier)?,
        (WeekNumber(modifier), Some(date), ..) => fmt_week_number(output, date, modifier)?,
        (Year(modifier), Some(date), ..) => fmt_year(output, date, modifier)?,
        (Hour(modifier), _, Some(time), _) => fmt_hour(output, time, modifier)?,
        (Minute(modifier), _, Some(time), _) => fmt_minute(output, time, modifier)?,
        (Period(modifier), _, Some(time), _) => fmt_period(output, time, modifier)?,
        (Second(modifier), _, Some(time), _) => fmt_second(output, time, modifier)?,
        (Subsecond(modifier), _, Some(time), _) => fmt_subsecond(output, time, modifier)?,
        (OffsetHour(modifier), .., Some(offset)) => fmt_offset_hour(output, offset, modifier)?,
        (OffsetMinute(modifier), .., Some(offset)) => fmt_offset_minute(output, offset, modifier)?,
        (OffsetSecond(modifier), .., Some(offset)) => fmt_offset_second(output, offset, modifier)?,
        _ => return Err(error::Format::InsufficientTypeInformation),
    })
}

/// Format the day into the designated output.
fn fmt_day(
    output: &mut impl io::Write,
    date: Date,
    modifier::Day { padding }: modifier::Day,
) -> Result<usize, io::Error> {
    format_number(output, date.day(), padding, 2)
}

/// Format the month into the designated output.
fn fmt_month(
    output: &mut impl io::Write,
    date: Date,
    modifier::Month { padding, repr }: modifier::Month,
) -> Result<usize, io::Error> {
    match repr {
        modifier::MonthRepr::Numerical => format_number(output, date.month(), padding, 2),
        modifier::MonthRepr::Long => output.write(MONTH_NAMES[date.month() as usize - 1]),
        modifier::MonthRepr::Short => output.write(&MONTH_NAMES[date.month() as usize - 1][..3]),
    }
}

/// Format the ordinal into the designated output.
fn fmt_ordinal(
    output: &mut impl io::Write,
    date: Date,
    modifier::Ordinal { padding }: modifier::Ordinal,
) -> Result<usize, io::Error> {
    format_number(output, date.ordinal(), padding, 3)
}

/// Format the weekday into the designated output.
fn fmt_weekday(
    output: &mut impl io::Write,
    date: Date,
    modifier::Weekday { repr, one_indexed }: modifier::Weekday,
) -> Result<usize, io::Error> {
    match repr {
        modifier::WeekdayRepr::Short => {
            output.write(&WEEKDAY_NAMES[date.weekday().number_days_from_monday() as usize][..3])
        }
        modifier::WeekdayRepr::Long => {
            output.write(WEEKDAY_NAMES[date.weekday().number_days_from_monday() as usize])
        }
        modifier::WeekdayRepr::Sunday => format_number(
            output,
            date.weekday().number_days_from_sunday() + one_indexed as u8,
            modifier::Padding::None,
            1,
        ),
        modifier::WeekdayRepr::Monday => format_number(
            output,
            date.weekday().number_days_from_monday() + one_indexed as u8,
            modifier::Padding::None,
            1,
        ),
    }
}

/// Format the week number into the designated output.
fn fmt_week_number(
    output: &mut impl io::Write,
    date: Date,
    modifier::WeekNumber { padding, repr }: modifier::WeekNumber,
) -> Result<usize, io::Error> {
    format_number(
        output,
        match repr {
            modifier::WeekNumberRepr::Iso => date.iso_week(),
            modifier::WeekNumberRepr::Sunday => date.sunday_based_week(),
            modifier::WeekNumberRepr::Monday => date.monday_based_week(),
        },
        padding,
        2,
    )
}

/// Format the year into the designated output.
fn fmt_year(
    output: &mut impl io::Write,
    date: Date,
    modifier::Year {
        padding,
        repr,
        iso_week_based,
        sign_is_mandatory,
    }: modifier::Year,
) -> Result<usize, io::Error> {
    let full_year = if iso_week_based {
        date.iso_year_week().0
    } else {
        date.year()
    };
    let value = match repr {
        modifier::YearRepr::Full => full_year,
        modifier::YearRepr::LastTwo => (full_year % 100).abs(),
    };
    let width = match repr {
        #[cfg(feature = "large-dates")]
        modifier::YearRepr::Full if value.abs() >= 100_000 => 6,
        #[cfg(feature = "large-dates")]
        modifier::YearRepr::Full if value.abs() >= 10_000 => 5,
        modifier::YearRepr::Full => 4,
        modifier::YearRepr::LastTwo => 2,
    };
    let mut bytes = 0;
    if repr != modifier::YearRepr::LastTwo {
        if full_year < 0 {
            bytes += output.write(&[b'-'])?;
        } else if sign_is_mandatory || cfg!(feature = "large-dates") && full_year >= 10_000 {
            bytes += output.write(&[b'+'])?;
        }
    }
    bytes += format_number(output, value.abs() as u32, padding, width)?;
    Ok(bytes)
}

/// Format the hour into the designated output.
fn fmt_hour(
    output: &mut impl io::Write,
    time: Time,
    modifier::Hour {
        padding,
        is_12_hour_clock,
    }: modifier::Hour,
) -> Result<usize, io::Error> {
    let value = match (time.hour, is_12_hour_clock) {
        (hour, false) => hour,
        (0, true) | (12, true) => 12,
        (hour, true) if hour < 12 => hour,
        (hour, true) => hour - 12,
    };
    format_number(output, value, padding, 2)
}

/// Format the minute into the designated output.
fn fmt_minute(
    output: &mut impl io::Write,
    time: Time,
    modifier::Minute { padding }: modifier::Minute,
) -> Result<usize, io::Error> {
    format_number(output, time.minute, padding, 2)
}

/// Format the period into the designated output.
fn fmt_period(
    output: &mut impl io::Write,
    time: Time,
    modifier::Period { is_uppercase }: modifier::Period,
) -> Result<usize, io::Error> {
    match (time.hour >= 12, is_uppercase) {
        (false, false) => output.write(b"am"),
        (false, true) => output.write(b"AM"),
        (true, false) => output.write(b"pm"),
        (true, true) => output.write(b"PM"),
    }
}

/// Format the second into the designated output.
fn fmt_second(
    output: &mut impl io::Write,
    time: Time,
    modifier::Second { padding }: modifier::Second,
) -> Result<usize, io::Error> {
    format_number(output, time.second, padding, 2)
}

/// Format the subsecond into the designated output.
fn fmt_subsecond(
    output: &mut impl io::Write,
    time: Time,
    modifier::Subsecond { digits }: modifier::Subsecond,
) -> Result<usize, io::Error> {
    let (value, width) = match digits {
        modifier::SubsecondDigits::One => (time.nanosecond / 100_000_000, 1),
        modifier::SubsecondDigits::Two => (time.nanosecond / 10_000_000, 2),
        modifier::SubsecondDigits::Three => (time.nanosecond / 1_000_000, 3),
        modifier::SubsecondDigits::Four => (time.nanosecond / 100_000, 4),
        modifier::SubsecondDigits::Five => (time.nanosecond / 10_000, 5),
        modifier::SubsecondDigits::Six => (time.nanosecond / 1_000, 6),
        modifier::SubsecondDigits::Seven => (time.nanosecond / 100, 7),
        modifier::SubsecondDigits::Eight => (time.nanosecond / 10, 8),
        modifier::SubsecondDigits::Nine => (time.nanosecond, 9),
        modifier::SubsecondDigits::OneOrMore => match time.nanosecond {
            nanos if nanos % 10 != 0 => (nanos, 9),
            nanos if (nanos / 10) % 10 != 0 => (nanos / 10, 8),
            nanos if (nanos / 100) % 10 != 0 => (nanos / 100, 7),
            nanos if (nanos / 1_000) % 10 != 0 => (nanos / 1_000, 6),
            nanos if (nanos / 10_000) % 10 != 0 => (nanos / 10_000, 5),
            nanos if (nanos / 100_000) % 10 != 0 => (nanos / 100_000, 4),
            nanos if (nanos / 1_000_000) % 10 != 0 => (nanos / 1_000_000, 3),
            nanos if (nanos / 10_000_000) % 10 != 0 => (nanos / 10_000_000, 2),
            nanos => (nanos / 100_000_000, 1),
        },
    };
    format_number(output, value, modifier::Padding::Zero, width)
}

/// Format the offset hour into the designated output.
fn fmt_offset_hour(
    output: &mut impl io::Write,
    offset: UtcOffset,
    modifier::OffsetHour {
        padding,
        sign_is_mandatory,
    }: modifier::OffsetHour,
) -> Result<usize, io::Error> {
    let mut bytes = 0;
    if offset.is_negative() {
        bytes += output.write(&[b'-'])?;
    } else if sign_is_mandatory {
        bytes += output.write(&[b'+'])?;
    }
    bytes += format_number(output, offset.hours.abs() as u8, padding, 2)?;
    Ok(bytes)
}

/// Format the offset minute into the designated output.
fn fmt_offset_minute(
    output: &mut impl io::Write,
    offset: UtcOffset,
    modifier::OffsetMinute { padding }: modifier::OffsetMinute,
) -> Result<usize, io::Error> {
    format_number(output, offset.minutes.abs() as u8, padding, 2)
}

/// Format the offset second into the designated output.
fn fmt_offset_second(
    output: &mut impl io::Write,
    offset: UtcOffset,
    modifier::OffsetSecond { padding }: modifier::OffsetSecond,
) -> Result<usize, io::Error> {
    format_number(output, offset.seconds.abs() as u8, padding, 2)
}
<|endoftext|>