<commit_before>#![feature(proc_macro)]
#![feature(proc_macro_lib)]

#[macro_use] extern crate nom;
#[macro_use] extern crate quick_error;
#[macro_use] extern crate quote;

extern crate proc_macro;
extern crate syn;

mod parsbart;

use proc_macro::TokenStream;

enum Ast {
    Sequence(Vec<Ast>),
    Literal(&'static str),
    Interpolation(&'static str),
    Iteration(&'static str, Box<Ast>),
}

fn generate(node: Ast, scope_level: i32) -> quote::Tokens {
    match node {
        Ast::Sequence(seq) => {
            let items = seq.into_iter().map(|node| generate(node, scope_level));
            quote! { #(#items)* }
        },
        Ast::Literal(text) => {
            quote! { f.write_str(#text)?; }
        },
        Ast::Interpolation(ident_text) => {
            let ident = syn::Ident::new(ident_text);
            quote! { DisplayHtmlSafe::safe_fmt(&#ident, f)?; }
        },
        Ast::Iteration(ident_text, nested) => {
            let ident = syn::Ident::new(ident_text);
            let scope_variable = syn::Ident::new(format!("_s{}", scope_level));
            let nested_generated = generate(*nested, scope_level + 1);
            quote! {
                for ref #scope_variable in &#ident {
                    #nested_generated
                }
            }
        },
    }
}

#[proc_macro_derive(StacheDisplay, attributes(template))]
pub fn stache_display(input: TokenStream) -> TokenStream {
    let s = input.to_string();
    let ast = syn::parse_macro_input(&s).unwrap();

    let template_attr = ast.attrs.iter()
        .find(|&x| x.name() == "template")
        .expect("#[derive(StacheDisplay)] requires #[template = \"...\"]");

    let template = match &template_attr.value {
        &syn::MetaItem::NameValue(_, syn::Lit::Str(ref template, _)) => template,
        _ => panic!("#[derive(StacheDisplay)] requires #[template = \"...\"]")
    };

    parsbart::kake(template).unwrap();
    // TODO Use result from parsing

    let mock_parsed = Ast::Sequence(vec![
        Ast::Literal("Hello, "),
        Ast::Interpolation("self.name"),
        Ast::Literal(" ("),
        Ast::Interpolation("self.age"),
        Ast::Literal(")\n"),
        Ast::Iteration("self.stuff", Box::new(Ast::Sequence(vec![
            Ast::Literal("
"),
            Ast::Interpolation("_s1"),
            Ast::Literal("
\n"),
        ])))
    ]);

    let generated = generate(mock_parsed, 1);

    let name = &ast.ident;
    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

    let gen = quote! {
        impl #impl_generics ::std::fmt::Display for #name #ty_generics #where_clause {
            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                use display_html_safe::DisplayHtmlSafe;
                let ref _s0 = self;

                #generated

                Ok(())
            }
        }
    };

    gen.parse().unwrap()
}
<commit_msg>Cleanup<commit_after>#![feature(proc_macro)]
#![feature(proc_macro_lib)]

#[macro_use] extern crate nom;
#[macro_use] extern crate quick_error;
#[macro_use] extern crate quote;

extern crate proc_macro;
extern crate syn;

mod parsbart;

use proc_macro::TokenStream;

enum Ast {
    Sequence(Vec<Ast>),
    Literal(&'static str),
    Interpolation(&'static str),
    Iteration(&'static str, Box<Ast>),
}

fn generate(node: Ast, scope_level: i32) -> quote::Tokens {
    use Ast::*;
    match node {
        Sequence(seq) => {
            let items = seq.into_iter().map(|node| generate(node, scope_level));
            quote! { #(#items)* }
        },
        Literal(text) => {
            quote! { f.write_str(#text)?; }
        },
        Interpolation(ident_text) => {
            let ident = syn::Ident::new(ident_text);
            quote! { DisplayHtmlSafe::safe_fmt(&#ident, f)?; }
        },
        Iteration(ident_text, nested) => {
            let ident = syn::Ident::new(ident_text);
            let scope_variable = syn::Ident::new(format!("_s{}", scope_level));
            let nested_generated = generate(*nested, scope_level + 1);
            quote! {
                for ref #scope_variable in &#ident {
                    #nested_generated
                }
            }
        },
    }
}

#[proc_macro_derive(StacheDisplay, attributes(template))]
pub fn stache_display(input: TokenStream) -> TokenStream {
    let s = input.to_string();
    let ast = syn::parse_macro_input(&s).unwrap();

    let template_attr = ast.attrs.iter()
        .find(|&x| x.name() == "template")
        .expect("#[derive(StacheDisplay)] requires #[template = \"...\"]");

    let template = match &template_attr.value {
        &syn::MetaItem::NameValue(_, syn::Lit::Str(ref template, _)) => template,
        _ => panic!("#[derive(StacheDisplay)] requires #[template = \"...\"]")
    };

    parsbart::kake(template).unwrap();
    // TODO Use result from parsing

    let mock_parsed = Ast::Sequence(vec![
        Ast::Literal("Hello, "),
        Ast::Interpolation("self.name"),
        Ast::Literal(" ("),
        Ast::Interpolation("self.age"),
        Ast::Literal(")\n"),
        Ast::Iteration("self.stuff", Box::new(Ast::Sequence(vec![
            Ast::Literal("
"),
            Ast::Interpolation("_s1"),
            Ast::Literal("
\n"),
        ])))
    ]);

    let generated = generate(mock_parsed, 1);

    let name = &ast.ident;
    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

    let gen = quote! {
        impl #impl_generics ::std::fmt::Display for #name #ty_generics #where_clause {
            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                use display_html_safe::DisplayHtmlSafe;
                let ref _s0 = self;

                #generated

                Ok(())
            }
        }
    };

    gen.parse().unwrap()
}
<|endoftext|>


<commit_before>use term_input::Key;
use termbox_simple::Termbox;

use std::convert::From;

use time::{self, Tm};

use crate::{
    config,
    config::{Colors, Style},
    exit_dialogue::ExitDialogue,
    msg_area::{
        line::{SchemeStyle, SegStyle},
        MsgArea,
    },
    termbox,
    text_field::TextField,
    trie::Trie,
    widget::WidgetRet,
};

/// A messaging screen is just a text field to type messages and msg area to
/// show incoming/sent messages.
pub(crate) struct MessagingUI {
    /// Incoming and sent messages appear
    msg_area: MsgArea,

    // exit_dialogue handles input when available.
    // two fields (instead of an enum etc.) to avoid borrowchk problems
    input_field: TextField,
    exit_dialogue: Option<ExitDialogue>,

    width: i32,
    height: i32,

    // Option to disable status messages ( join/part )
    show_status: bool,

    // All nicks in the channel. Need to keep this up-to-date to be able to
    // properly highlight mentions.
    nicks: Trie,

    current_nick: Option<String>,
    draw_current_nick: bool,

    last_activity_line: Option<ActivityLine>,
    last_activity_ts: Option<Timestamp>,
}

/// Like `time::Tm`, but we only care about hour and minute parts.
#[derive(PartialEq, Eq, Clone, Copy)]
pub(crate) struct Timestamp {
    hour: i32,
    min: i32,
}

impl Timestamp {
    fn stamp(self, msg_area: &mut MsgArea) {
        msg_area.set_style(SegStyle::SchemeStyle(SchemeStyle::Timestamp));
        msg_area.add_text(&format!("{:02}:{:02} ", self.hour, self.min));
    }
}

impl From<Tm> for Timestamp {
    fn from(tm: Tm) -> Timestamp {
        Timestamp {
            hour: tm.tm_hour,
            min: tm.tm_min,
        }
    }
}

/// An activity line is just a line that we update on joins / leaves /
/// disconnects. We group activities that happen in the same minute to avoid
/// redundantly showing lines.
struct ActivityLine {
    ts: Timestamp,
    line_idx: usize,
}

impl MessagingUI {
    pub(crate) fn new(width: i32, height: i32, status: bool) -> MessagingUI {
        MessagingUI {
            msg_area: MsgArea::new(width, height - 1),
            input_field: TextField::new(width),
            exit_dialogue: None,
            width,
            height,
            show_status: status,
            nicks: Trie::new(),
            current_nick: None,
            draw_current_nick: true,
            last_activity_line: None,
            last_activity_ts: None,
        }
    }

    pub(crate) fn set_nick(&mut self, nick: String) {
        self.current_nick = Some(nick);
        // update text field size
        let w = self.width;
        let h = self.height;
        self.resize(w, h);
    }

    pub(crate) fn get_nick(&self) -> Option<&str> {
        self.current_nick.as_ref().map(String::as_str)
    }

    fn draw_input_field(&self, tb: &mut Termbox, colors: &Colors, pos_x: i32, pos_y: i32) {
        match self.exit_dialogue {
            Some(ref exit_dialogue) => exit_dialogue.draw(tb, colors, pos_x, pos_y),
            None => self.input_field.draw(tb, colors, pos_x, pos_y),
        }
    }

    pub(crate) fn draw(&self, tb: &mut Termbox, colors: &Colors, pos_x: i32, pos_y: i32) {
        self.msg_area.draw(tb, colors, pos_x, pos_y);

        if let Some(ref nick) = self.current_nick {
            if self.draw_current_nick {
                let nick_color = colors.nick[self.get_nick_color(nick) % colors.nick.len()];
                let style = Style {
                    fg: u16::from(nick_color),
                    bg: colors.user_msg.bg,
                };
                termbox::print_chars(tb, pos_x, pos_y + self.height - 1, style, nick.chars());
                tb.change_cell(
                    pos_x + nick.len() as i32,
                    pos_y + self.height - 1,
                    ':',
                    colors.user_msg.fg | config::TB_BOLD,
                    colors.user_msg.bg,
                );
                self.draw_input_field(
                    tb,
                    colors,
                    pos_x + nick.len() as i32 + 2,
                    pos_y + self.height - 1,
                );
            } else {
                self.draw_input_field(tb, colors, pos_x, pos_y + self.height - 1);
            }
        } else {
            self.draw_input_field(tb, colors, pos_x, pos_y + self.height - 1);
        }
    }

    pub(crate) fn keypressed(&mut self, key: Key) -> WidgetRet {
        match key {
            Key::Ctrl('c') => {
                self.toggle_exit_dialogue();
                WidgetRet::KeyHandled
            }

            Key::Ctrl('u') | Key::PageUp => {
                self.msg_area.page_up();
                WidgetRet::KeyHandled
            }

            Key::Ctrl('d') | Key::PageDown => {
                self.msg_area.page_down();
                WidgetRet::KeyHandled
            }

            Key::ShiftUp => {
                self.msg_area.scroll_up();
                WidgetRet::KeyHandled
            }

            Key::ShiftDown => {
                self.msg_area.scroll_down();
                WidgetRet::KeyHandled
            }

            Key::Tab => {
                if self.exit_dialogue.is_none() {
                    self.input_field.autocomplete(&self.nicks);
                }
                WidgetRet::KeyHandled
            }

            Key::Home => {
                self.msg_area.scroll_top();
                WidgetRet::KeyHandled
            }

            Key::End => {
                self.msg_area.scroll_bottom();
                WidgetRet::KeyHandled
            }

            key => {
                let ret = {
                    if let Some(exit_dialogue) = self.exit_dialogue.as_ref() {
                        exit_dialogue.keypressed(key)
                    } else {
                        self.input_field.keypressed(key)
                    }
                };

                if let WidgetRet::Remove = ret {
                    self.exit_dialogue = None;
                    WidgetRet::KeyHandled
                } else {
                    ret
                }
            }
        }
    }

    pub(crate) fn resize(&mut self, width: i32, height: i32) {
        self.width = width;
        self.height = height;
        self.msg_area.resize(width, height - 1);

        let nick_width = match self.current_nick {
            None => 0,
            Some(ref rc) =>
            // +2 for ": "
            {
                rc.len() as i32 + 2
            }
        };

        self.draw_current_nick = (nick_width as f32) <= (width as f32) * (30f32 / 100f32);

        let widget_width = if self.draw_current_nick {
            width - nick_width
        } else {
            width
        };

        self.input_field.resize(widget_width);
        for exit_dialogue in &mut self.exit_dialogue {
            exit_dialogue.resize(widget_width);
        }
    }

    /// Get contents of the input field and clear it.
    pub(crate) fn flush_input_field(&mut self) -> String {
        self.input_field.flush()
    }

    /// Add a line to input field history.
    pub(crate) fn add_input_field_history(&mut self, str: &str) {
        self.input_field.add_history(str)
    }

    /// Set input field contents.
    pub(crate) fn set_input_field(&mut self, str: &str) {
        self.input_field.set(str)
    }

    fn toggle_exit_dialogue(&mut self) {
        let exit_dialogue = ::std::mem::replace(&mut self.exit_dialogue, None);
        if exit_dialogue.is_none() {
            self.exit_dialogue = Some(ExitDialogue::new(self.width));
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
// Adding new messages

impl MessagingUI {
    fn add_timestamp(&mut self, ts: Timestamp) {
        if let Some(ts_) = self.last_activity_ts {
            if ts_ != ts {
                ts.stamp(&mut self.msg_area);
            }
        } else {
            ts.stamp(&mut self.msg_area);
        }
        self.last_activity_ts = Some(ts);
    }

    pub(crate) fn show_topic(&mut self, topic: &str, ts: Timestamp) {
        self.add_timestamp(ts);

        self.msg_area
            .set_style(SegStyle::SchemeStyle(SchemeStyle::Topic));
        self.msg_area.add_text(topic);

        self.msg_area.flush_line();
    }

    pub(crate) fn add_client_err_msg(&mut self, msg: &str) {
        self.reset_activity_line();

        self.msg_area
            .set_style(SegStyle::SchemeStyle(SchemeStyle::ErrMsg));
        self.msg_area.add_text(msg);
        self.msg_area.flush_line();
    }

    pub(crate) fn add_client_notify_msg(&mut self, msg: &str) {
        self.reset_activity_line();

        self.msg_area
            .set_style(SegStyle::SchemeStyle(SchemeStyle::Faded));
        self.msg_area.add_text(msg);
        self.msg_area.flush_line();
        self.reset_activity_line();
    }

    pub(crate) fn add_client_msg(&mut self, msg: &str) {
        self.reset_activity_line();

        self.msg_area
            .set_style(SegStyle::SchemeStyle(SchemeStyle::UserMsg));
        self.msg_area.add_text(msg);
        self.msg_area.flush_line();
        self.reset_activity_line();
    }

    pub(crate) fn add_privmsg(
        &mut self,
        sender: &str,
        msg: &str,
        ts: Timestamp,
        highlight: bool,
        is_action: bool,
    ) {
        self.reset_activity_line();
        self.add_timestamp(ts);

        if is_action {
            self.msg_area
                .set_style(SegStyle::SchemeStyle(SchemeStyle::UserMsg));
            self.msg_area.add_text("** ");
        }

        {
            let nick_color = self.get_nick_color(sender);
            let style = SegStyle::Index(nick_color);
            self.msg_area.set_style(style);
            self.msg_area.add_text(sender);
        }

        self.msg_area
            .set_style(SegStyle::SchemeStyle(SchemeStyle::UserMsg));

        if !is_action {
            self.msg_area.add_char(':');
        }
        self.msg_area.add_char(' ');

        if highlight {
            self.msg_area
                .set_style(SegStyle::SchemeStyle(SchemeStyle::Highlight));
        }

        self.msg_area.add_text(msg);
        self.msg_area.flush_line();
    }

    pub(crate) fn add_msg(&mut self, msg: &str, ts: Timestamp) {
        self.reset_activity_line();

        self.add_timestamp(ts);
        self.msg_area
            .set_style(SegStyle::SchemeStyle(SchemeStyle::UserMsg));
        self.msg_area.add_text(msg);
        self.msg_area.flush_line();
    }

    pub(crate) fn add_err_msg(&mut self, msg: &str, ts: Timestamp) {
        self.reset_activity_line();

        self.add_timestamp(ts);
        self.msg_area
            .set_style(SegStyle::SchemeStyle(SchemeStyle::ErrMsg));
        self.msg_area.add_text(msg);
        self.msg_area.flush_line();
    }

    pub(crate) fn clear(&mut self) {
        self.msg_area.clear();
    }

    fn get_nick_color(&self, sender: &str) -> usize {
        // Anything works as long as it's fast
        let mut hash: usize = 5381;
        for c in sender.chars() {
            hash = hash.wrapping_mul(33).wrapping_add(c as usize);
        }
        hash
    }
}

////////////////////////////////////////////////////////////////////////////////
// Keeping nick list up-to-date

impl MessagingUI {
    pub(crate) fn clear_nicks(&mut self) {
        self.nicks.clear();
    }

    pub(crate) fn join(&mut self, nick: &str, ts: Option<Timestamp>) {
        if self.show_status && !self.nicks.contains(nick) {
            if let Some(ts) = ts {
                let line_idx = self.get_activity_line_idx(ts);
                self.msg_area.modify_line(line_idx, |line| {
                    line.set_style(SegStyle::SchemeStyle(SchemeStyle::Join));
                    line.add_char('+');
                    line.set_style(SegStyle::SchemeStyle(SchemeStyle::Faded));
                    line.add_text(nick);
                    line.add_char(' ');
                });
            }
        }

        self.nicks.insert(nick);
    }

    pub(crate) fn part(&mut self, nick: &str, ts: Option<Timestamp>) {
        self.nicks.remove(nick);

        if self.show_status {
            if let Some(ts) = ts {
                let line_idx = self.get_activity_line_idx(ts);
                self.msg_area.modify_line(line_idx, |line| {
                    line.set_style(SegStyle::SchemeStyle(SchemeStyle::Part));
                    line.add_char('-');
                    line.set_style(SegStyle::SchemeStyle(SchemeStyle::Faded));
                    line.add_text(nick);
                    line.add_char(' ');
                });
            }
        }
    }

    /// `state` == `None` means toggle
    /// `state` == `Some(state)` means set it to `state`
    pub(crate) fn set_or_toggle_ignore(&mut self, state: Option<bool>) {
        self.show_status = state.unwrap_or(!self.show_status);
        if self.show_status {
            self.add_client_notify_msg("Ignore disabled");
        } else {
            self.add_client_notify_msg("Ignore enabled");
        }
    }

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

    pub(crate) fn nick(&mut self, old_nick: &str, new_nick: &str, ts: Timestamp) {
        self.nicks.remove(old_nick);
        self.nicks.insert(new_nick);

        let line_idx = self.get_activity_line_idx(ts);
        self.msg_area.modify_line(line_idx, |line| {
            line.set_style(SegStyle::SchemeStyle(SchemeStyle::Faded));
            line.add_text(old_nick);
            line.set_style(SegStyle::SchemeStyle(SchemeStyle::Nick));
            line.add_char('>');
            line.set_style(SegStyle::SchemeStyle(SchemeStyle::Faded));
            line.add_text(new_nick);
            line.add_char(' ');
        });
    }

    fn reset_activity_line(&mut self) {
        self.last_activity_line = None;
    }

    fn get_activity_line_idx(&mut self, ts: Timestamp) -> usize {
        // borrow checker strikes again
        if let Some(ref mut l) = self.last_activity_line {
            if l.ts == ts {
                return l.line_idx;
            }
        }

        self.add_timestamp(ts);
        let line_idx = self.msg_area.flush_line();
        self.last_activity_line = Some(ActivityLine { ts, line_idx });
        line_idx
    }
}
<commit_msg>tui: Minor refactoring, and a tiny style change<commit_after>use term_input::Key;
use termbox_simple::Termbox;

use std::convert::From;

use time::{self, Tm};

use crate::{
    config::{Colors, Style},
    exit_dialogue::ExitDialogue,
    msg_area::{
        line::{SchemeStyle, SegStyle},
        MsgArea,
    },
    termbox,
    text_field::TextField,
    trie::Trie,
    widget::WidgetRet,
};

/// A messaging screen is just a text field to type messages and msg area to
/// show incoming/sent messages.
pub(crate) struct MessagingUI {
    /// Incoming and sent messages appear
    msg_area: MsgArea,

    // exit_dialogue handles input when available.
    // two fields (instead of an enum etc.) to avoid borrowchk problems
    input_field: TextField,
    exit_dialogue: Option<ExitDialogue>,

    width: i32,
    height: i32,

    // Option to disable status messages ( join/part )
    show_status: bool,

    // All nicks in the channel. Need to keep this up-to-date to be able to
    // properly highlight mentions.
    nicks: Trie,

    current_nick: Option<String>,
    show_current_nick: bool,

    last_activity_line: Option<ActivityLine>,
    last_activity_ts: Option<Timestamp>,
}

/// Like `time::Tm`, but we only care about hour and minute parts.
#[derive(PartialEq, Eq, Clone, Copy)]
pub(crate) struct Timestamp {
    hour: i32,
    min: i32,
}

impl Timestamp {
    fn stamp(self, msg_area: &mut MsgArea) {
        msg_area.set_style(SegStyle::SchemeStyle(SchemeStyle::Timestamp));
        msg_area.add_text(&format!("{:02}:{:02} ", self.hour, self.min));
    }
}

impl From<Tm> for Timestamp {
    fn from(tm: Tm) -> Timestamp {
        Timestamp {
            hour: tm.tm_hour,
            min: tm.tm_min,
        }
    }
}

/// An activity line is just a line that we update on joins / leaves /
/// disconnects. We group activities that happen in the same minute to avoid
/// redundantly showing lines.
struct ActivityLine {
    ts: Timestamp,
    line_idx: usize,
}

impl MessagingUI {
    pub(crate) fn new(width: i32, height: i32, status: bool) -> MessagingUI {
        MessagingUI {
            msg_area: MsgArea::new(width, height - 1),
            input_field: TextField::new(width),
            exit_dialogue: None,
            width,
            height,
            show_status: status,
            nicks: Trie::new(),
            current_nick: None,
            show_current_nick: true,
            last_activity_line: None,
            last_activity_ts: None,
        }
    }

    pub(crate) fn set_nick(&mut self, nick: String) {
        self.current_nick = Some(nick);
        // update text field size
        let w = self.width;
        let h = self.height;
        self.resize(w, h);
    }

    pub(crate) fn get_nick(&self) -> Option<&str> {
        self.current_nick.as_ref().map(String::as_str)
    }

    fn draw_input_field(&self, tb: &mut Termbox, colors: &Colors, pos_x: i32, pos_y: i32) {
        match self.exit_dialogue {
            Some(ref exit_dialogue) => exit_dialogue.draw(tb, colors, pos_x, pos_y),
            None => self.input_field.draw(tb, colors, pos_x, pos_y),
        }
    }

    pub(crate) fn draw(&self, tb: &mut Termbox, colors: &Colors, pos_x: i32, pos_y: i32) {
        self.msg_area.draw(tb, colors, pos_x, pos_y);

        if let Some(ref nick) = self.current_nick {
            if self.show_current_nick {
                let nick_color = colors.nick[self.get_nick_color(nick) % colors.nick.len()];
                let style = Style {
                    fg: u16::from(nick_color),
                    bg: colors.user_msg.bg,
                };
                termbox::print_chars(tb, pos_x, pos_y + self.height - 1, style, nick.chars());
                tb.change_cell(
                    pos_x + nick.len() as i32,
                    pos_y + self.height - 1,
                    ':',
                    colors.user_msg.fg,
                    colors.user_msg.bg,
                );
                self.draw_input_field(
                    tb,
                    colors,
                    pos_x + nick.len() as i32 + 2,
                    pos_y + self.height - 1,
                );
            } else {
                self.draw_input_field(tb, colors, pos_x, pos_y + self.height - 1);
            }
        } else {
            self.draw_input_field(tb, colors, pos_x, pos_y + self.height - 1);
        }
    }

    pub(crate) fn keypressed(&mut self, key: Key) -> WidgetRet {
        match key {
            Key::Ctrl('c') => {
                self.toggle_exit_dialogue();
                WidgetRet::KeyHandled
            }

            Key::Ctrl('u') | Key::PageUp => {
                self.msg_area.page_up();
                WidgetRet::KeyHandled
            }

            Key::Ctrl('d') | Key::PageDown => {
                self.msg_area.page_down();
                WidgetRet::KeyHandled
            }

            Key::ShiftUp => {
                self.msg_area.scroll_up();
                WidgetRet::KeyHandled
            }

            Key::ShiftDown => {
                self.msg_area.scroll_down();
                WidgetRet::KeyHandled
            }

            Key::Tab => {
                if self.exit_dialogue.is_none() {
                    self.input_field.autocomplete(&self.nicks);
                }
                WidgetRet::KeyHandled
            }

            Key::Home => {
                self.msg_area.scroll_top();
                WidgetRet::KeyHandled
            }

            Key::End => {
                self.msg_area.scroll_bottom();
                WidgetRet::KeyHandled
            }

            key => {
                let ret = {
                    if let Some(exit_dialogue) = self.exit_dialogue.as_ref() {
                        exit_dialogue.keypressed(key)
                    } else {
                        self.input_field.keypressed(key)
                    }
                };

                if let WidgetRet::Remove = ret {
                    self.exit_dialogue = None;
                    WidgetRet::KeyHandled
                } else {
                    ret
                }
            }
        }
    }

    pub(crate) fn resize(&mut self, width: i32, height: i32) {
        self.width = width;
        self.height = height;
        self.msg_area.resize(width, height - 1);

        let nick_width = match self.current_nick {
            None => 0,
            Some(ref rc) =>
            // +2 for ": "
            {
                rc.len() as i32 + 2
            }
        };

        self.show_current_nick = (nick_width as f32) <= (width as f32) * (30f32 / 100f32);

        let widget_width = if self.show_current_nick {
            width - nick_width
        } else {
            width
        };

        self.input_field.resize(widget_width);
        for exit_dialogue in &mut self.exit_dialogue {
            exit_dialogue.resize(widget_width);
        }
    }

    /// Get contents of the input field and clear it.
    pub(crate) fn flush_input_field(&mut self) -> String {
        self.input_field.flush()
    }

    /// Add a line to input field history.
    pub(crate) fn add_input_field_history(&mut self, str: &str) {
        self.input_field.add_history(str)
    }

    /// Set input field contents.
    pub(crate) fn set_input_field(&mut self, str: &str) {
        self.input_field.set(str)
    }

    fn toggle_exit_dialogue(&mut self) {
        let exit_dialogue = ::std::mem::replace(&mut self.exit_dialogue, None);
        if exit_dialogue.is_none() {
            self.exit_dialogue = Some(ExitDialogue::new(self.width));
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
// Adding new messages

impl MessagingUI {
    fn add_timestamp(&mut self, ts: Timestamp) {
        if let Some(ts_) = self.last_activity_ts {
            if ts_ != ts {
                ts.stamp(&mut self.msg_area);
            }
        } else {
            ts.stamp(&mut self.msg_area);
        }
        self.last_activity_ts = Some(ts);
    }

    pub(crate) fn show_topic(&mut self, topic: &str, ts: Timestamp) {
        self.add_timestamp(ts);

        self.msg_area
            .set_style(SegStyle::SchemeStyle(SchemeStyle::Topic));
        self.msg_area.add_text(topic);

        self.msg_area.flush_line();
    }

    pub(crate) fn add_client_err_msg(&mut self, msg: &str) {
        self.reset_activity_line();

        self.msg_area
            .set_style(SegStyle::SchemeStyle(SchemeStyle::ErrMsg));
        self.msg_area.add_text(msg);
        self.msg_area.flush_line();
    }

    pub(crate) fn add_client_notify_msg(&mut self, msg: &str) {
        self.reset_activity_line();

        self.msg_area
            .set_style(SegStyle::SchemeStyle(SchemeStyle::Faded));
        self.msg_area.add_text(msg);
        self.msg_area.flush_line();
        self.reset_activity_line();
    }

    pub(crate) fn add_client_msg(&mut self, msg: &str) {
        self.reset_activity_line();

        self.msg_area
            .set_style(SegStyle::SchemeStyle(SchemeStyle::UserMsg));
        self.msg_area.add_text(msg);
        self.msg_area.flush_line();
        self.reset_activity_line();
    }

    pub(crate) fn add_privmsg(
        &mut self,
        sender: &str,
        msg: &str,
        ts: Timestamp,
        highlight: bool,
        is_action: bool,
    ) {
        self.reset_activity_line();
        self.add_timestamp(ts);

        if is_action {
            self.msg_area
                .set_style(SegStyle::SchemeStyle(SchemeStyle::UserMsg));
            self.msg_area.add_text("** ");
        }

        {
            let nick_color = self.get_nick_color(sender);
            let style = SegStyle::Index(nick_color);
            self.msg_area.set_style(style);
            self.msg_area.add_text(sender);
        }

        self.msg_area
            .set_style(SegStyle::SchemeStyle(SchemeStyle::UserMsg));

        if !is_action {
            self.msg_area.add_char(':');
        }
        self.msg_area.add_char(' ');

        if highlight {
            self.msg_area
                .set_style(SegStyle::SchemeStyle(SchemeStyle::Highlight));
        }

        self.msg_area.add_text(msg);
        self.msg_area.flush_line();
    }

    pub(crate) fn add_msg(&mut self, msg: &str, ts: Timestamp) {
        self.reset_activity_line();

        self.add_timestamp(ts);
        self.msg_area
            .set_style(SegStyle::SchemeStyle(SchemeStyle::UserMsg));
        self.msg_area.add_text(msg);
        self.msg_area.flush_line();
    }

    pub(crate) fn add_err_msg(&mut self, msg: &str, ts: Timestamp) {
        self.reset_activity_line();

        self.add_timestamp(ts);
        self.msg_area
            .set_style(SegStyle::SchemeStyle(SchemeStyle::ErrMsg));
        self.msg_area.add_text(msg);
        self.msg_area.flush_line();
    }

    pub(crate) fn clear(&mut self) {
        self.msg_area.clear();
    }

    fn get_nick_color(&self, sender: &str) -> usize {
        // Anything works as long as it's fast
        let mut hash: usize = 5381;
        for c in sender.chars() {
            hash = hash.wrapping_mul(33).wrapping_add(c as usize);
        }
        hash
    }
}

////////////////////////////////////////////////////////////////////////////////
// Keeping nick list up-to-date

impl MessagingUI {
    pub(crate) fn clear_nicks(&mut self) {
        self.nicks.clear();
    }

    pub(crate) fn join(&mut self, nick: &str, ts: Option<Timestamp>) {
        if self.show_status && !self.nicks.contains(nick) {
            if let Some(ts) = ts {
                let line_idx = self.get_activity_line_idx(ts);
                self.msg_area.modify_line(line_idx, |line| {
                    line.set_style(SegStyle::SchemeStyle(SchemeStyle::Join));
                    line.add_char('+');
                    line.set_style(SegStyle::SchemeStyle(SchemeStyle::Faded));
                    line.add_text(nick);
                    line.add_char(' ');
                });
            }
        }

        self.nicks.insert(nick);
    }

    pub(crate) fn part(&mut self, nick: &str, ts: Option<Timestamp>) {
        self.nicks.remove(nick);

        if self.show_status {
            if let Some(ts) = ts {
                let line_idx = self.get_activity_line_idx(ts);
                self.msg_area.modify_line(line_idx, |line| {
                    line.set_style(SegStyle::SchemeStyle(SchemeStyle::Part));
                    line.add_char('-');
                    line.set_style(SegStyle::SchemeStyle(SchemeStyle::Faded));
                    line.add_text(nick);
                    line.add_char(' ');
                });
            }
        }
    }

    /// `state` == `None` means toggle
    /// `state` == `Some(state)` means set it to `state`
    pub(crate) fn set_or_toggle_ignore(&mut self, state: Option<bool>) {
        self.show_status = state.unwrap_or(!self.show_status);
        if self.show_status {
            self.add_client_notify_msg("Ignore disabled");
        } else {
            self.add_client_notify_msg("Ignore enabled");
        }
    }

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

    pub(crate) fn nick(&mut self, old_nick: &str, new_nick: &str, ts: Timestamp) {
        self.nicks.remove(old_nick);
        self.nicks.insert(new_nick);

        let line_idx = self.get_activity_line_idx(ts);
        self.msg_area.modify_line(line_idx, |line| {
            line.set_style(SegStyle::SchemeStyle(SchemeStyle::Faded));
            line.add_text(old_nick);
            line.set_style(SegStyle::SchemeStyle(SchemeStyle::Nick));
            line.add_char('>');
            line.set_style(SegStyle::SchemeStyle(SchemeStyle::Faded));
            line.add_text(new_nick);
            line.add_char(' ');
        });
    }

    fn reset_activity_line(&mut self) {
        self.last_activity_line = None;
    }

    fn get_activity_line_idx(&mut self, ts: Timestamp) -> usize {
        // borrow checker strikes again
        if let Some(ref mut l) = self.last_activity_line {
            if l.ts == ts {
                return l.line_idx;
            }
        }

        self.add_timestamp(ts);
        let line_idx = self.msg_area.flush_line();
        self.last_activity_line = Some(ActivityLine { ts, line_idx });
        line_idx
    }
}
<|endoftext|>


<commit_before>extern crate fuse;
extern crate time;
extern crate libc;
extern crate serialize;

use std::io::{FileType, USER_FILE, USER_DIR};
use std::mem;
use std::os;
use libc::{ENOENT, ENOSYS};
use time::Timespec;
use fuse::{FileAttr, Filesystem, Request, ReplyAttr, ReplyEntry, ReplyDirectory};
use serialize::json;

struct JsonFilesystem {
    tree: json::Object,
}

impl Filesystem for JsonFilesystem {
    fn getattr(&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
        println!("getattr(ino={})", ino);
        let mut attr: FileAttr = unsafe { mem::zeroed() };
        let ttl = Timespec::new(1, 0);
        if ino == 1 {
            attr.ino = 1;
            attr.kind = FileType::Directory;
            attr.perm = USER_DIR;
            reply.attr(&ttl, &attr);
        } else {
            let tree_index = (ino - 2) as uint;
            println!("\ttree_index={}", tree_index);
            if tree_index < self.tree.len() {
                attr.ino = ino;
                attr.kind = FileType::RegularFile;
                attr.perm = USER_FILE;
                reply.attr(&ttl, &attr);
            } else {
                reply.error(ENOSYS);
            }
        }
    }

    fn lookup(&mut self, _req: &Request, parent: u64, name: &PosixPath, reply: ReplyEntry) {
        println!("lookup(parent={}, name={})", parent, name.display());
        reply.error(ENOENT);
    }

    fn readdir(&mut self, _req: &Request, ino: u64, fh: u64, offset: u64, mut reply: ReplyDirectory) {
        println!("readdir(ino={}, fh={}, offset={})", ino, fh, offset);
        if ino == 1 {
            if offset == 0 {
                reply.add(1, 0, FileType::Directory, &PosixPath::new("."));
                reply.add(1, 1, FileType::Directory, &PosixPath::new(".."));
                for (i, key) in self.tree.keys().enumerate() {
                    let inode: u64 = 2 + i as u64;
                    let offset: u64 = 2 + i as u64;
                    println!("\tkey={}, inode={}, offset={}", key, inode, offset);
                    reply.add(inode, offset, FileType::RegularFile, &PosixPath::new(key));
                }
            }
            reply.ok();
        } else {
            reply.error(ENOENT);
        }
    }
}

fn main() {
    println!("24 days of Rust - fuse (day N)");
    let data = json::from_str("{\"foo\": \"bar\", \"answer\": 42}").unwrap();
    let tree = data.as_object().unwrap();
    let fs = JsonFilesystem { tree: tree.clone() };
    let mountpoint = Path::new(os::args()[1].as_slice());
    fuse::mount(fs, &mountpoint, &[]);
}
<commit_msg>Working lookup() implementation.<commit_after>extern crate fuse;
extern crate time;
extern crate libc;
extern crate serialize;

use std::io::{FileType, USER_FILE, USER_DIR};
use std::mem;
use std::os;
use libc::{ENOENT, ENOSYS};
use time::Timespec;
use fuse::{FileAttr, Filesystem, Request, ReplyAttr, ReplyEntry, ReplyDirectory};
use serialize::json;

struct JsonFilesystem {
    tree: json::Object,
}

impl Filesystem for JsonFilesystem {
    fn getattr(&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
        println!("getattr(ino={})", ino);
        let mut attr: FileAttr = unsafe { mem::zeroed() };
        let ttl = Timespec::new(1, 0);
        if ino == 1 {
            attr.ino = 1;
            attr.kind = FileType::Directory;
            attr.perm = USER_DIR;
            reply.attr(&ttl, &attr);
        } else {
            let tree_index = (ino - 2) as uint;
            println!("\ttree_index={}", tree_index);
            if tree_index < self.tree.len() {
                attr.ino = ino;
                attr.kind = FileType::RegularFile;
                attr.perm = USER_FILE;
                reply.attr(&ttl, &attr);
            } else {
                reply.error(ENOSYS);
            }
        }
    }

    fn lookup(&mut self, _req: &Request, parent: u64, name: &PosixPath, reply: ReplyEntry) {
        println!("lookup(parent={}, name={})", parent, name.display());
        let mut attr: FileAttr = unsafe { mem::zeroed() };
        let ttl = Timespec::new(1, 0);
        let name = name.as_str().unwrap();
        if name == "/" {
            attr.ino = 1;
            attr.kind = FileType::Directory;
            attr.perm = USER_DIR;
            reply.entry(&ttl, &attr, 0);
        } else {
            match self.tree.get(name) {
                Some(value) => {
                    println!("\t name={}, value={}", name, value);
                    attr.kind = FileType::RegularFile;
                    attr.perm = USER_FILE;
                    // TODO: correct attributes!
                    attr.ino = 2;
                    reply.entry(&ttl, &attr, 0);
                },
                None => reply.error(ENOENT)
            };
        }
    }

    fn readdir(&mut self, _req: &Request, ino: u64, fh: u64, offset: u64, mut reply: ReplyDirectory) {
        println!("readdir(ino={}, fh={}, offset={})", ino, fh, offset);
        if ino == 1 {
            if offset == 0 {
                reply.add(1, 0, FileType::Directory, &PosixPath::new("."));
                reply.add(1, 1, FileType::Directory, &PosixPath::new(".."));
                for (i, key) in self.tree.keys().enumerate() {
                    let inode: u64 = 2 + i as u64;
                    let offset: u64 = 2 + i as u64;
                    println!("\tkey={}, inode={}, offset={}", key, inode, offset);
                    reply.add(inode, offset, FileType::RegularFile, &PosixPath::new(key));
                }
            }
            reply.ok();
        } else {
            reply.error(ENOENT);
        }
    }
}

fn main() {
    println!("24 days of Rust - fuse (day N)");
    let data = json::from_str("{\"foo\": \"bar\", \"answer\": 42}").unwrap();
    let tree = data.as_object().unwrap();
    let fs = JsonFilesystem { tree: tree.clone() };
    let mountpoint = Path::new(os::args()[1].as_slice());
    fuse::mount(fs, &mountpoint, &[]);
}
<|endoftext|>


<commit_before>// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::alloc::{alloc, alloc_zeroed, dealloc, Layout};

/// A contiguous memory allocation with a specified size and alignment, with a
/// Drop impl to perform the deallocation.
///
/// Conceptually this is like a Box<[u8]> but for which we can select a minimum
/// required alignment at the time of allocation.
///
/// # Example
///
/// ```
/// use std::alloc::Layout;
/// use std::mem;
/// use sys_util::LayoutAllocation;
///
/// #[repr(C)]
/// struct Header {
///     q: usize,
///     entries: [Entry; 0], // flexible array member
/// }
///
/// #[repr(C)]
/// struct Entry {
///     e: usize,
/// }
///
/// fn demo(num_entries: usize) {
///     let size = mem::size_of::<Header>() + num_entries * mem::size_of::<Entry>();
///     let layout = Layout::from_size_align(size, mem::align_of::<Header>()).unwrap();
///     let mut allocation = LayoutAllocation::zeroed(layout);
///
///     // Safe to obtain an exclusive reference because there are no other
///     // references to the allocation yet and all-zero is a valid bit pattern for
///     // our header.
///     let header = unsafe { allocation.as_mut::<Header>() };
/// }
/// ```
pub struct LayoutAllocation {
    ptr: *mut u8,
    layout: Layout,
}

impl LayoutAllocation {
    /// Allocates memory with the specified size and alignment. The content is
    /// not initialized.
    ///
    /// Uninitialized data is not safe to read. Further, it is not safe to
    /// obtain a reference to data potentially holding a bit pattern
    /// incompatible with its type, for example an uninitialized bool or enum.
    pub fn uninitialized(layout: Layout) -> Self {
        let ptr = if layout.size() > 0 {
            unsafe {
                // Safe as long as we guarantee layout.size() > 0.
                alloc(layout)
            }
        } else {
            layout.align() as *mut u8
        };
        LayoutAllocation { ptr, layout }
    }

    /// Allocates memory with the specified size and alignment and initializes
    /// the content to all zero-bytes.
    ///
    /// Note that zeroing the memory does not necessarily make it safe to obtain
    /// a reference to the allocation. Depending on the intended type T,
    /// all-zero may or may not be a legal bit pattern for that type. For
    /// example obtaining a reference would immediately be undefined behavior if
    /// one of the fields has type NonZeroUsize.
    pub fn zeroed(layout: Layout) -> Self {
        let ptr = if layout.size() > 0 {
            unsafe {
                // Safe as long as we guarantee layout.size() > 0.
                alloc_zeroed(layout)
            }
        } else {
            layout.align() as *mut u8
        };
        LayoutAllocation { ptr, layout }
    }

    /// Returns a raw pointer to the allocated data.
    pub fn as_ptr<T>(&self) -> *mut T {
        self.ptr as *mut T
    }

    /// Returns a shared reference to the allocated data.
    ///
    /// # Safety
    ///
    /// Caller is responsible for ensuring that the data behind this pointer has
    /// been initialized as much as necessary and that there are no already
    /// existing mutable references to any part of the data.
    pub unsafe fn as_ref<T>(&self) -> &T {
        &*self.as_ptr()
    }

    /// Returns an exclusive reference to the allocated data.
    ///
    /// # Safety
    ///
    /// Caller is responsible for ensuring that the data behind this pointer has
    /// been initialized as much as necessary and that there are no already
    /// existing references to any part of the data.
    pub unsafe fn as_mut<T>(&mut self) -> &mut T {
        &mut *self.as_ptr()
    }
}

impl Drop for LayoutAllocation {
    fn drop(&mut self) {
        if self.layout.size() > 0 {
            unsafe {
                // Safe as long as we guarantee layout.size() > 0.
                dealloc(self.ptr, self.layout);
            }
        }
    }
}
<commit_msg>sys_util: add LayoutAllocation as_(mut_)slice()<commit_after>// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::alloc::{alloc, alloc_zeroed, dealloc, Layout};
use std::cmp::min;

/// A contiguous memory allocation with a specified size and alignment, with a
/// Drop impl to perform the deallocation.
///
/// Conceptually this is like a Box<[u8]> but for which we can select a minimum
/// required alignment at the time of allocation.
///
/// # Example
///
/// ```
/// use std::alloc::Layout;
/// use std::mem;
/// use sys_util::LayoutAllocation;
///
/// #[repr(C)]
/// struct Header {
///     q: usize,
///     entries: [Entry; 0], // flexible array member
/// }
///
/// #[repr(C)]
/// struct Entry {
///     e: usize,
/// }
///
/// fn demo(num_entries: usize) {
///     let size = mem::size_of::<Header>() + num_entries * mem::size_of::<Entry>();
///     let layout = Layout::from_size_align(size, mem::align_of::<Header>()).unwrap();
///     let mut allocation = LayoutAllocation::zeroed(layout);
///
///     // Safe to obtain an exclusive reference because there are no other
///     // references to the allocation yet and all-zero is a valid bit pattern for
///     // our header.
///     let header = unsafe { allocation.as_mut::<Header>() };
/// }
/// ```
pub struct LayoutAllocation {
    ptr: *mut u8,
    layout: Layout,
}

impl LayoutAllocation {
    /// Allocates memory with the specified size and alignment. The content is
    /// not initialized.
    ///
    /// Uninitialized data is not safe to read. Further, it is not safe to
    /// obtain a reference to data potentially holding a bit pattern
    /// incompatible with its type, for example an uninitialized bool or enum.
    pub fn uninitialized(layout: Layout) -> Self {
        let ptr = if layout.size() > 0 {
            unsafe {
                // Safe as long as we guarantee layout.size() > 0.
                alloc(layout)
            }
        } else {
            layout.align() as *mut u8
        };
        LayoutAllocation { ptr, layout }
    }

    /// Allocates memory with the specified size and alignment and initializes
    /// the content to all zero-bytes.
    ///
    /// Note that zeroing the memory does not necessarily make it safe to obtain
    /// a reference to the allocation. Depending on the intended type T,
    /// all-zero may or may not be a legal bit pattern for that type. For
    /// example obtaining a reference would immediately be undefined behavior if
    /// one of the fields has type NonZeroUsize.
    pub fn zeroed(layout: Layout) -> Self {
        let ptr = if layout.size() > 0 {
            unsafe {
                // Safe as long as we guarantee layout.size() > 0.
                alloc_zeroed(layout)
            }
        } else {
            layout.align() as *mut u8
        };
        LayoutAllocation { ptr, layout }
    }

    /// Returns a raw pointer to the allocated data.
    pub fn as_ptr<T>(&self) -> *mut T {
        self.ptr as *mut T
    }

    /// Returns a shared reference to the allocated data.
    ///
    /// # Safety
    ///
    /// Caller is responsible for ensuring that the data behind this pointer has
    /// been initialized as much as necessary and that there are no already
    /// existing mutable references to any part of the data.
    pub unsafe fn as_ref<T>(&self) -> &T {
        &*self.as_ptr()
    }

    /// Returns an exclusive reference to the allocated data.
    ///
    /// # Safety
    ///
    /// Caller is responsible for ensuring that the data behind this pointer has
    /// been initialized as much as necessary and that there are no already
    /// existing references to any part of the data.
    pub unsafe fn as_mut<T>(&mut self) -> &mut T {
        &mut *self.as_ptr()
    }

    /// Returns a shared slice reference to the allocated data.
    ///
    /// # Arguments
    ///
    /// `num_elements` - Number of `T` elements to include in the slice.
    ///                  The length of the slice will be capped to the allocation's size.
    ///                  Caller must ensure that any sliced elements are initialized.
    ///
    /// # Safety
    ///
    /// Caller is responsible for ensuring that the data behind this pointer has
    /// been initialized as much as necessary and that there are no already
    /// existing mutable references to any part of the data.
    pub unsafe fn as_slice<T>(&self, num_elements: usize) -> &[T] {
        let len = min(num_elements, self.layout.size() / std::mem::size_of::<T>());
        std::slice::from_raw_parts(self.as_ptr(), len)
    }

    /// Returns an exclusive slice reference to the allocated data.
    ///
    /// # Arguments
    ///
    /// `num_elements` - Number of `T` elements to include in the slice.
    ///                  The length of the slice will be capped to the allocation's size.
    ///                  Caller must ensure that any sliced elements are initialized.
    ///
    /// # Safety
    ///
    /// Caller is responsible for ensuring that the data behind this pointer has
    /// been initialized as much as necessary and that there are no already
    /// existing references to any part of the data.
    pub unsafe fn as_mut_slice<T>(&mut self, num_elements: usize) -> &mut [T] {
        let len = min(num_elements, self.layout.size() / std::mem::size_of::<T>());
        std::slice::from_raw_parts_mut(self.as_ptr(), len)
    }
}

impl Drop for LayoutAllocation {
    fn drop(&mut self) {
        if self.layout.size() > 0 {
            unsafe {
                // Safe as long as we guarantee layout.size() > 0.
                dealloc(self.ptr, self.layout);
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::mem::{align_of, size_of};

    #[test]
    fn test_as_slice_u32() {
        let layout = Layout::from_size_align(size_of::<u32>() * 15, align_of::<u32>()).unwrap();
        let allocation = LayoutAllocation::zeroed(layout);
        let slice: &[u32] = unsafe { allocation.as_slice(15) };
        assert_eq!(slice.len(), 15);
        assert_eq!(slice[0], 0);
        assert_eq!(slice[14], 0);
    }

    #[test]
    fn test_as_slice_u32_smaller_len() {
        let layout = Layout::from_size_align(size_of::<u32>() * 15, align_of::<u32>()).unwrap();
        let allocation = LayoutAllocation::zeroed(layout);

        // Slice less than the allocation size, which will return a slice of only the requested length.
        let slice: &[u32] = unsafe { allocation.as_slice(5) };
        assert_eq!(slice.len(), 5);
    }

    #[test]
    fn test_as_slice_u32_larger_len() {
        let layout = Layout::from_size_align(size_of::<u32>() * 15, align_of::<u32>()).unwrap();
        let allocation = LayoutAllocation::zeroed(layout);

        // Slice more than the allocation size, which will clamp the returned slice len to the limit.
        let slice: &[u32] = unsafe { allocation.as_slice(100) };
        assert_eq!(slice.len(), 15);
    }

    #[test]
    fn test_as_slice_u32_remainder() {
        // Allocate a buffer that is not a multiple of u32 in size.
        let layout = Layout::from_size_align(size_of::<u32>() * 15 + 2, align_of::<u32>()).unwrap();
        let allocation = LayoutAllocation::zeroed(layout);

        // Slice as many u32s as possible, which should return a slice that only includes the full
        // u32s, not the trailing 2 bytes.
        let slice: &[u32] = unsafe { allocation.as_slice(100) };
        assert_eq!(slice.len(), 15);
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2014, 2015 Robert Clipsham <robert@octarineparrot.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Support for implementing transport layer protocols
//!
//! The transport module provides the ability to send and receive packets at
//! the transport layer using IPv4 or IPv6. It also enables layer 3 networking
//! for specific transport protocols, using IPv4 only.
//!
//! Note that this is limited by operating system support - for example, on OS
//! X and FreeBSD, it is impossible to implement protocols which are already
//! implemented in the kernel such as TCP and UDP.

#![macro_use]

extern crate libc;
extern crate pnet_sys;
extern crate pnet_packet;

use pnet_packet::Packet;
use pnet_packet::ip::IpNextHeaderProtocol;
use pnet_packet::ipv4::Ipv4Packet;
use pnet_packet::udp::UdpPacket;
use pnet_packet::icmp::IcmpPacket;
use pnet_packet::icmpv6::Icmpv6Packet;
use pnet_packet::tcp::TcpPacket;
use self::TransportChannelType::{Layer3, Layer4};
use self::TransportProtocol::{Ipv4, Ipv6};

use std::io;
use std::io::Error;
use std::mem;
use std::net::{self, IpAddr};
use std::sync::Arc;
use std::time::Duration;

/// Represents a transport layer protocol
#[derive(Clone, Copy)]
pub enum TransportProtocol {
    /// Represents a transport protocol built on top of IPv4
    Ipv4(IpNextHeaderProtocol),
    /// Represents a transport protocol built on top of IPv6
    Ipv6(IpNextHeaderProtocol),
}

/// Type of transport channel to present
#[derive(Clone, Copy)]
pub enum TransportChannelType {
    /// The application will send and receive transport layer packets
    Layer4(TransportProtocol),
    /// The application will send and receive IPv4 packets, with the specified transport protocol
    Layer3(IpNextHeaderProtocol),
}

/// Structure used for sending at the transport layer. Should be created with transport_channel()
pub struct TransportSender {
    socket: Arc<pnet_sys::FileDesc>,
    _channel_type: TransportChannelType,
}

/// Structure used for receiving at the transport layer. Should be created with transport_channel()
pub struct TransportReceiver {
    socket: Arc<pnet_sys::FileDesc>,
    buffer: Vec<u8>,
    channel_type: TransportChannelType,
}

/// Create a new (TransportSender, TransportReceiver) pair
///
/// This allows for sending and receiving packets at the transport layer. The buffer size should be
/// large enough to handle the largest packet you wish to receive.
///
/// The channel type specifies what layer to send and receive packets at, and the transport
/// protocol you wish to implement. For example, `Layer4(Ipv4(IpNextHeaderProtocols::Udp))` would
/// allow sending and receiving UDP packets using IPv4; whereas Layer3(IpNextHeaderProtocols::Udp)
/// would include the IPv4 Header in received values, and require manual construction of an IP
/// header when sending.
pub fn transport_channel(buffer_size: usize,
                         channel_type: TransportChannelType)
    -> io::Result<(TransportSender, TransportReceiver)> {
    use std::net;

    // This hack makes sure that winsock is initialised
    let _ = {
        let ip = net::Ipv4Addr::new(255, 255, 255, 255);
        let sockaddr = net::SocketAddr::V4(net::SocketAddrV4::new(ip, 0));

        net::UdpSocket::bind(sockaddr)
    };

    let socket = unsafe {
        match channel_type {
            Layer4(Ipv4(IpNextHeaderProtocol(proto))) |
            Layer3(IpNextHeaderProtocol(proto)) => {
                pnet_sys::socket(pnet_sys::AF_INET, pnet_sys::SOCK_RAW, proto as libc::c_int)
            }
            Layer4(Ipv6(IpNextHeaderProtocol(proto))) => {
                pnet_sys::socket(pnet_sys::AF_INET6, pnet_sys::SOCK_RAW, proto as libc::c_int)
            }
        }
    };
    if socket == pnet_sys::INVALID_SOCKET {
        return Err(Error::last_os_error());
    }

    if match channel_type {
        Layer3(_) | Layer4(Ipv4(_)) => true,
        _ => false,
    } {
        let hincl: libc::c_int = match channel_type {
            Layer4(..) => 0,
            _ => 1,
        };
        let res = unsafe {
            pnet_sys::setsockopt(
                socket,
                pnet_sys::IPPROTO_IP,
                pnet_sys::IP_HDRINCL,
                (&hincl as *const libc::c_int) as pnet_sys::Buf,
                mem::size_of::<libc::c_int>() as pnet_sys::SockLen
            )
        };
        if res == -1 {
            let err = Error::last_os_error();
            unsafe {
                pnet_sys::close(socket);
            }
            return Err(err);
        }
    }

    let sock = Arc::new(pnet_sys::FileDesc { fd: socket });
    let sender = TransportSender {
        socket: sock.clone(),
        _channel_type: channel_type,
    };
    let receiver = TransportReceiver {
        socket: sock,
        buffer: vec![0; buffer_size],
        channel_type: channel_type,
    };

    Ok((sender, receiver))
}

impl TransportSender {
    fn send<T: Packet>(&mut self, packet: T, dst: IpAddr) -> io::Result<usize> {
        let mut caddr = unsafe { mem::zeroed() };
        let sockaddr = match dst {
            IpAddr::V4(ip_addr) => net::SocketAddr::V4(net::SocketAddrV4::new(ip_addr, 0)),
            IpAddr::V6(ip_addr) => net::SocketAddr::V6(net::SocketAddrV6::new(ip_addr, 0, 0, 0)),
        };
        let slen = pnet_sys::addr_to_sockaddr(sockaddr, &mut caddr);
        let caddr_ptr = (&caddr as *const pnet_sys::SockAddrStorage) as *const pnet_sys::SockAddr;

        pnet_sys::send_to(self.socket.fd, packet.packet(), caddr_ptr, slen)
    }

    /// Send a packet to the provided destination
    #[inline]
    pub fn send_to<T: Packet>(&mut self, packet: T, destination: IpAddr) -> io::Result<usize> {
        self.send_to_impl(packet, destination)
    }

    #[cfg(all(not(target_os = "freebsd"), not(target_os = "macos")))]
    fn send_to_impl<T: Packet>(&mut self, packet: T, dst: IpAddr) -> io::Result<usize> {
        self.send(packet, dst)
    }

    #[cfg(any(target_os = "freebsd", target_os = "macos"))]
    fn send_to_impl<T: Packet>(&mut self, packet: T, dst: IpAddr) -> io::Result<usize> {
        use pnet_packet::MutablePacket;
        use pnet_packet::ipv4::MutableIpv4Packet;

        // FreeBSD and OS X expect total length and fragment offset fields of IPv4
        // packets to be in host byte order rather than network byte order. Fragment offset is the
        // ip_off field in the ip struct and contains both the offset and the three flag bits.
        // See `man 4 ip`/Raw IP Sockets)
        if let Layer3(_) = self._channel_type {
            let mut mut_slice: Vec<u8> = vec![0; packet.packet().len()];

            let mut new_packet = MutableIpv4Packet::new(&mut mut_slice[..]).unwrap();
            new_packet.clone_from(&packet);
            let length = new_packet.get_total_length().to_be();
            new_packet.set_total_length(length);
            {
                // Turn fragment offset into host order
                let d = new_packet.packet_mut();
                let host_order = u16::from_be((d[6] as u16) << 8 | d[7] as u16);
                d[6] = (host_order >> 8) as u8;
                d[7] = host_order as u8;
            }
            return self.send(new_packet, dst);
        }

        self.send(packet, dst)
    }
}

/// Create an iterator for some packet type.
///
/// Usage:
/// ```ignore
/// transport_channel_iterator!(Ipv4Packet, // Type to iterate over
///                             Ipv4TransportChannelIterator, // Name for iterator struct
///                             ipv4_packet_iter) // Name of function to create iterator
/// ```
#[macro_export]
macro_rules! transport_channel_iterator {
    ($ty:ident, $iter:ident, $func:ident) => (
        /// An iterator over packets of type $ty
        pub struct $iter<'a> {
            tr: &'a mut TransportReceiver
        }
        /// Return a packet iterator with packets of type $ty for some transport receiver
        pub fn $func(tr: &mut TransportReceiver) -> $iter {
            $iter {
                tr: tr
            }
        }

        impl<'a> $iter<'a> {
            /// Get the next ($ty, IpAddr) pair for the given channel.
            pub fn next(&mut self) -> io::Result<($ty, IpAddr)> {
                let mut caddr: pnet_sys::SockAddrStorage = unsafe { mem::zeroed() };
                let res = pnet_sys::recv_from(self.tr.socket.fd,
                                              &mut self.tr.buffer[..],
                                              &mut caddr);

                let offset = match self.tr.channel_type {
                    Layer4(Ipv4(_)) => {
                        let ip_header = Ipv4Packet::new(&self.tr.buffer[..]).unwrap();

                        ip_header.get_header_length() as usize * 4usize
                    },
                    Layer3(_) => {
                        fixup_packet(&mut self.tr.buffer[..]);

                        0
                    },
                    _ => 0
                };
                return match res {
                    Ok(len) => {
                        let packet = $ty::new(&self.tr.buffer[offset..len]).unwrap();
                        let addr = pnet_sys::sockaddr_to_addr(
                            &caddr,
                            mem::size_of::<pnet_sys::SockAddrStorage>()
                        );
                        let ip = match addr.unwrap() {
                            net::SocketAddr::V4(sa) => IpAddr::V4(*sa.ip()),
                            net::SocketAddr::V6(sa) => IpAddr::V6(*sa.ip()),
                        };
                        Ok((packet, ip))
                    },
                    Err(e) => Err(e),
                };
                #[cfg(any(target_os = "freebsd", target_os = "macos"))]
                fn fixup_packet(buffer: &mut [u8]) {
                    use pnet_packet::ipv4::MutableIpv4Packet;

                    let buflen = buffer.len();
                    let mut new_packet = MutableIpv4Packet::new(buffer).unwrap();

                    let length = u16::from_be(new_packet.get_total_length());
                    new_packet.set_total_length(length);

                    // OS X does this awesome thing where it removes the header length
                    // from the total length sometimes.
                    let length = new_packet.get_total_length() as usize +
                                 (new_packet.get_header_length() as usize * 4usize);
                    if length == buflen {
                        new_packet.set_total_length(length as u16)
                    }

                    let offset = u16::from_be(new_packet.get_fragment_offset());
                    new_packet.set_fragment_offset(offset);
                }

                #[cfg(all(not(target_os = "freebsd"), not(target_os = "macos")))]
                fn fixup_packet(_buffer: &mut [u8]) {}
            }

            /// If `t` is larger than zero, wait at maximum this number of seconds.
            #[cfg(unix)]
            pub fn next_with_timeout(&mut self, t: Duration) -> io::Result<($ty, IpAddr)> {
                let socket_fd = self.tr.socket.fd;
                let old_timeout = pnet_sys::get_socket_receive_timeout(socket_fd);
                pnet_sys::set_socket_receive_timeout(socket_fd, t);
                let r = self.next();
                pnet_sys::set_socket_receive_timeout(socket_fd, old_timeout);
                r
            }
        }
    )
}

transport_channel_iterator!(Ipv4Packet, Ipv4TransportChannelIterator, ipv4_packet_iter);

transport_channel_iterator!(UdpPacket, UdpTransportChannelIterator, udp_packet_iter);

transport_channel_iterator!(IcmpPacket, IcmpTransportChannelIterator, icmp_packet_iter);

transport_channel_iterator!(Icmpv6Packet, Icmpv6TransportChannelIterator, icmpv6_packet_iter);

transport_channel_iterator!(TcpPacket, TcpTransportChannelIterator, tcp_packet_iter);
<commit_msg>Match error kind after recv timeout and return None on timeout<commit_after>// Copyright (c) 2014, 2015 Robert Clipsham <robert@octarineparrot.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Support for implementing transport layer protocols
//!
//! The transport module provides the ability to send and receive packets at
//! the transport layer using IPv4 or IPv6. It also enables layer 3 networking
//! for specific transport protocols, using IPv4 only.
//!
//! Note that this is limited by operating system support - for example, on OS
//! X and FreeBSD, it is impossible to implement protocols which are already
//! implemented in the kernel such as TCP and UDP.

#![macro_use]

extern crate libc;
extern crate pnet_sys;
extern crate pnet_packet;

use pnet_packet::Packet;
use pnet_packet::ip::IpNextHeaderProtocol;
use pnet_packet::ipv4::Ipv4Packet;
use pnet_packet::udp::UdpPacket;
use pnet_packet::icmp::IcmpPacket;
use pnet_packet::icmpv6::Icmpv6Packet;
use pnet_packet::tcp::TcpPacket;
use self::TransportChannelType::{Layer3, Layer4};
use self::TransportProtocol::{Ipv4, Ipv6};

use std::io;
use std::io::Error;
use std::io::ErrorKind;
use std::mem;
use std::net::{self, IpAddr};
use std::sync::Arc;
use std::time::Duration;

/// Represents a transport layer protocol
#[derive(Clone, Copy)]
pub enum TransportProtocol {
    /// Represents a transport protocol built on top of IPv4
    Ipv4(IpNextHeaderProtocol),
    /// Represents a transport protocol built on top of IPv6
    Ipv6(IpNextHeaderProtocol),
}

/// Type of transport channel to present
#[derive(Clone, Copy)]
pub enum TransportChannelType {
    /// The application will send and receive transport layer packets
    Layer4(TransportProtocol),
    /// The application will send and receive IPv4 packets, with the specified transport protocol
    Layer3(IpNextHeaderProtocol),
}

/// Structure used for sending at the transport layer. Should be created with transport_channel()
pub struct TransportSender {
    socket: Arc<pnet_sys::FileDesc>,
    _channel_type: TransportChannelType,
}

/// Structure used for receiving at the transport layer. Should be created with transport_channel()
pub struct TransportReceiver {
    socket: Arc<pnet_sys::FileDesc>,
    buffer: Vec<u8>,
    channel_type: TransportChannelType,
}

/// Create a new (TransportSender, TransportReceiver) pair
///
/// This allows for sending and receiving packets at the transport layer. The buffer size should be
/// large enough to handle the largest packet you wish to receive.
///
/// The channel type specifies what layer to send and receive packets at, and the transport
/// protocol you wish to implement. For example, `Layer4(Ipv4(IpNextHeaderProtocols::Udp))` would
/// allow sending and receiving UDP packets using IPv4; whereas Layer3(IpNextHeaderProtocols::Udp)
/// would include the IPv4 Header in received values, and require manual construction of an IP
/// header when sending.
pub fn transport_channel(buffer_size: usize,
                         channel_type: TransportChannelType)
    -> io::Result<(TransportSender, TransportReceiver)> {
    use std::net;

    // This hack makes sure that winsock is initialised
    let _ = {
        let ip = net::Ipv4Addr::new(255, 255, 255, 255);
        let sockaddr = net::SocketAddr::V4(net::SocketAddrV4::new(ip, 0));

        net::UdpSocket::bind(sockaddr)
    };

    let socket = unsafe {
        match channel_type {
            Layer4(Ipv4(IpNextHeaderProtocol(proto))) |
            Layer3(IpNextHeaderProtocol(proto)) => {
                pnet_sys::socket(pnet_sys::AF_INET, pnet_sys::SOCK_RAW, proto as libc::c_int)
            }
            Layer4(Ipv6(IpNextHeaderProtocol(proto))) => {
                pnet_sys::socket(pnet_sys::AF_INET6, pnet_sys::SOCK_RAW, proto as libc::c_int)
            }
        }
    };
    if socket == pnet_sys::INVALID_SOCKET {
        return Err(Error::last_os_error());
    }

    if match channel_type {
        Layer3(_) | Layer4(Ipv4(_)) => true,
        _ => false,
    } {
        let hincl: libc::c_int = match channel_type {
            Layer4(..) => 0,
            _ => 1,
        };
        let res = unsafe {
            pnet_sys::setsockopt(
                socket,
                pnet_sys::IPPROTO_IP,
                pnet_sys::IP_HDRINCL,
                (&hincl as *const libc::c_int) as pnet_sys::Buf,
                mem::size_of::<libc::c_int>() as pnet_sys::SockLen
            )
        };
        if res == -1 {
            let err = Error::last_os_error();
            unsafe {
                pnet_sys::close(socket);
            }
            return Err(err);
        }
    }

    let sock = Arc::new(pnet_sys::FileDesc { fd: socket });
    let sender = TransportSender {
        socket: sock.clone(),
        _channel_type: channel_type,
    };
    let receiver = TransportReceiver {
        socket: sock,
        buffer: vec![0; buffer_size],
        channel_type: channel_type,
    };

    Ok((sender, receiver))
}

impl TransportSender {
    fn send<T: Packet>(&mut self, packet: T, dst: IpAddr) -> io::Result<usize> {
        let mut caddr = unsafe { mem::zeroed() };
        let sockaddr = match dst {
            IpAddr::V4(ip_addr) => net::SocketAddr::V4(net::SocketAddrV4::new(ip_addr, 0)),
            IpAddr::V6(ip_addr) => net::SocketAddr::V6(net::SocketAddrV6::new(ip_addr, 0, 0, 0)),
        };
        let slen = pnet_sys::addr_to_sockaddr(sockaddr, &mut caddr);
        let caddr_ptr = (&caddr as *const pnet_sys::SockAddrStorage) as *const pnet_sys::SockAddr;

        pnet_sys::send_to(self.socket.fd, packet.packet(), caddr_ptr, slen)
    }

    /// Send a packet to the provided destination
    #[inline]
    pub fn send_to<T: Packet>(&mut self, packet: T, destination: IpAddr) -> io::Result<usize> {
        self.send_to_impl(packet, destination)
    }

    #[cfg(all(not(target_os = "freebsd"), not(target_os = "macos")))]
    fn send_to_impl<T: Packet>(&mut self, packet: T, dst: IpAddr) -> io::Result<usize> {
        self.send(packet, dst)
    }

    #[cfg(any(target_os = "freebsd", target_os = "macos"))]
    fn send_to_impl<T: Packet>(&mut self, packet: T, dst: IpAddr) -> io::Result<usize> {
        use pnet_packet::MutablePacket;
        use pnet_packet::ipv4::MutableIpv4Packet;

        // FreeBSD and OS X expect total length and fragment offset fields of IPv4
        // packets to be in host byte order rather than network byte order. Fragment offset is the
        // ip_off field in the ip struct and contains both the offset and the three flag bits.
        // See `man 4 ip`/Raw IP Sockets)
        if let Layer3(_) = self._channel_type {
            let mut mut_slice: Vec<u8> = vec![0; packet.packet().len()];

            let mut new_packet = MutableIpv4Packet::new(&mut mut_slice[..]).unwrap();
            new_packet.clone_from(&packet);
            let length = new_packet.get_total_length().to_be();
            new_packet.set_total_length(length);
            {
                // Turn fragment offset into host order
                let d = new_packet.packet_mut();
                let host_order = u16::from_be((d[6] as u16) << 8 | d[7] as u16);
                d[6] = (host_order >> 8) as u8;
                d[7] = host_order as u8;
            }
            return self.send(new_packet, dst);
        }

        self.send(packet, dst)
    }
}

/// Create an iterator for some packet type.
///
/// Usage:
/// ```ignore
/// transport_channel_iterator!(Ipv4Packet, // Type to iterate over
///                             Ipv4TransportChannelIterator, // Name for iterator struct
///                             ipv4_packet_iter) // Name of function to create iterator
/// ```
#[macro_export]
macro_rules! transport_channel_iterator {
    ($ty:ident, $iter:ident, $func:ident) => (
        /// An iterator over packets of type $ty
        pub struct $iter<'a> {
            tr: &'a mut TransportReceiver
        }
        /// Return a packet iterator with packets of type $ty for some transport receiver
        pub fn $func(tr: &mut TransportReceiver) -> $iter {
            $iter {
                tr: tr
            }
        }

        impl<'a> $iter<'a> {
            /// Get the next ($ty, IpAddr) pair for the given channel.
            pub fn next(&mut self) -> io::Result<($ty, IpAddr)> {
                let mut caddr: pnet_sys::SockAddrStorage = unsafe { mem::zeroed() };
                let res = pnet_sys::recv_from(self.tr.socket.fd,
                                              &mut self.tr.buffer[..],
                                              &mut caddr);

                let offset = match self.tr.channel_type {
                    Layer4(Ipv4(_)) => {
                        let ip_header = Ipv4Packet::new(&self.tr.buffer[..]).unwrap();

                        ip_header.get_header_length() as usize * 4usize
                    },
                    Layer3(_) => {
                        fixup_packet(&mut self.tr.buffer[..]);

                        0
                    },
                    _ => 0
                };
                return match res {
                    Ok(len) => {
                        let packet = $ty::new(&self.tr.buffer[offset..len]).unwrap();
                        let addr = pnet_sys::sockaddr_to_addr(
                            &caddr,
                            mem::size_of::<pnet_sys::SockAddrStorage>()
                        );
                        let ip = match addr.unwrap() {
                            net::SocketAddr::V4(sa) => IpAddr::V4(*sa.ip()),
                            net::SocketAddr::V6(sa) => IpAddr::V6(*sa.ip()),
                        };
                        Ok((packet, ip))
                    },
                    Err(e) => Err(e),
                };

                #[cfg(any(target_os = "freebsd", target_os = "macos"))]
                fn fixup_packet(buffer: &mut [u8]) {
                    use pnet_packet::ipv4::MutableIpv4Packet;

                    let buflen = buffer.len();
                    let mut new_packet = MutableIpv4Packet::new(buffer).unwrap();

                    let length = u16::from_be(new_packet.get_total_length());
                    new_packet.set_total_length(length);

                    // OS X does this awesome thing where it removes the header length
                    // from the total length sometimes.
                    let length = new_packet.get_total_length() as usize +
                                 (new_packet.get_header_length() as usize * 4usize);
                    if length == buflen {
                        new_packet.set_total_length(length as u16)
                    }

                    let offset = u16::from_be(new_packet.get_fragment_offset());
                    new_packet.set_fragment_offset(offset);
                }

                #[cfg(all(not(target_os = "freebsd"), not(target_os = "macos")))]
                fn fixup_packet(_buffer: &mut [u8]) {}
            }

            /// Wait only for a timespan of `t` to receive some data, then return. If no data was
            /// received, then `Ok(None)` is returned.
            #[cfg(unix)]
            pub fn next_with_timeout(&mut self, t: Duration) -> io::Result<Option<($ty, IpAddr)>> {
                let socket_fd = self.tr.socket.fd;
                let old_timeout = pnet_sys::get_socket_receive_timeout(socket_fd);
                pnet_sys::set_socket_receive_timeout(socket_fd, t);
                let r = match self.next() {
                    Ok(r) => Ok(Some(r)),
                    Err(e) => match e.kind() {
                        ErrorKind::WouldBlock => Ok(None),
                        _ => {
                            Err(e)
                        }
                    }
                };
                pnet_sys::set_socket_receive_timeout(socket_fd, old_timeout);
                r
            }
        }
    )
}

transport_channel_iterator!(Ipv4Packet, Ipv4TransportChannelIterator, ipv4_packet_iter);

transport_channel_iterator!(UdpPacket, UdpTransportChannelIterator, udp_packet_iter);

transport_channel_iterator!(IcmpPacket, IcmpTransportChannelIterator, icmp_packet_iter);

transport_channel_iterator!(Icmpv6Packet, Icmpv6TransportChannelIterator, icmpv6_packet_iter);

transport_channel_iterator!(TcpPacket, TcpTransportChannelIterator, tcp_packet_iter);
<|endoftext|>


<commit_before>mod colors;
mod quadtree;
use nannou::prelude::*;
use nannou::prelude::Frame;
use std::process::exit;
use nannou::rand::*;
use crate::colors::Palette;

const LENGTH_FRAME: u64 = 700;
const START_FRAME: u64 = 0;
const SEED: u64 = 1;

fn main() {
    nannou::app(model).update(update).run();
}

struct Model {
    palette: Palette,
    things: Vec<Thing>,
    rng: XorShiftRng,
}

// a Thing will be our main object, it'll try to grow outward
struct Thing {
    position: Vector2,
    size: f32,
    energy: f32,
    frac: f32,
    alive: bool,
    grown: bool,
    generation: usize,
    parent: Option<usize>,
    children: Vec<usize>
}
// we only care if they are on the same spot
impl PartialEq for Thing {
    fn eq(&self, other: &Thing) -> bool {
        self.position == other.position
    }
}
// define what to use for the quadtree
impl quadtree::WithPos for Thing {
    fn get_pos(&self) -> Vector2{
        self.position
    }
}

impl Thing {
    fn new(x: f32, y:f32, s:f32, f:f32, parent: Option<usize>) -> Self {
        let position = pt2(x,y);
        let size = s;
        let frac = f;

        Thing {
            position,
            size,
            energy: 0.0,
            frac,
            alive: true,
            grown: false,
            generation: 0,
            parent,
            children: Vec::new()
        }
    }

    fn distancept(&self, x:f32, y:f32) -> f32 {
        self.position.distance(vec2(x,y))
    }
    fn distance(&self, other: &Thing) -> f32 {
        self.position.distance(other.position)
    }
}

fn model(app: &App) -> Model {
    let _window = app
        .new_window()
        .with_dimensions(1024,1024)
        .view(view)
        .event(window_event)
        .build()
        .unwrap();

    //create the random values we will need each frame
    let rng  = XorShiftRng::seed_from_u64(SEED);

    let palette = Palette::new();

    let mut things = Vec::new();

    //we'll start from one thing in the middle
    let x = 0.0;//(rng.gen::<f32>()-0.5)*1024.0;
    let y = 0.0;//(rng.gen::<f32>()-0.5)*1024.0;
    let size = 1.0;//(rng.gen::<f32>()*30.0)+5.0;
    let frac = 0.0;//rng.gen::<f32>();
    let candidate = Thing::new(x,y,size, frac, None);
    things.push(candidate);

    Model { palette: palette, things, rng}
}

//nothing to do here
fn window_event(_app: &App, _model: &mut Model, event: WindowEvent) {
    match event {
        KeyPressed(_key) => {}
        KeyReleased(_key) => {}
        MouseMoved(_pos) => {}
        MousePressed(_button) => {}
        MouseReleased(_button) => {}
        MouseEntered => {}
        MouseExited => {}
        MouseWheel(_amount, _phase) => {}
        Moved(_pos) => {}
        Resized(_size) => {}
        Touch(_touch) => {}
        TouchPressure(_pressure) => {}
        HoveredFile(_path) => {}
        DroppedFile(_path) => {}
        HoveredFileCancelled => {}
        Focused => {}
        Unfocused => {}
        Closed => {}
    }
}

fn update(app: &App, model: &mut Model, _update: Update) {
    //try to grow each circle until it hits another one

    //recreate the tree
    let mut tree = quadtree::QuadTree::new();
    for i in 0..model.things.len() {
        model.things[i].grown = false;
    }
    //grow and branch out?
    let max_count = model.things.len();
    for i in 0..model.things.len() {
        tree.insert(&model.things,i);
    }
    for i in 0..max_count {
        if model.things[i].parent == None {
            if app.elapsed_frames() < 1000 {
                model.things[i].energy += 10000.0;
                if model.things[i].alive == true {
                    model.things[i].grown = true;
                }
            }
        }
        //move the size to the children
        if model.things[i].energy > 10.0 {
            //push the size to children
            //or branch
            let prob = 0.2;

            //if no child create a one

            //create a child
            if model.rng.gen::<f32>() < prob {
                let mut angle = model.rng.gen::<f32>()*TAU;
                if let Some(parent) = model.things[i].parent {
                    let from_parent = model.things[i].position - model.things[parent].position;
                    let base_angle = from_parent.y.atan2(from_parent.x);
                    let mut diff = (model.rng.gen::<f32>()-0.5)*2.0;
                    diff = diff.powf(19.0);
                    angle = base_angle+diff*PI;
                }

                let r = model.things[i].size + 1.0;
                let x = model.things[i].position.x + r*angle.cos();
                let y = model.things[i].position.y + r*angle.sin();
                let s = 1.0;
                let mut candidate = Thing::new(x,y,s,angle/PI, Some(i));
                candidate.generation = model.things[i].generation+1;
                let indices = tree.get_elements(&model.things,x,y, 50.0);
                let mut ok = true;
                for k in 0..indices.len() {
                    let d = model.things[indices[k]].distancept(x,y);
                    if d  < model.things[indices[k]].size +1.0  {
                        ok = false;
                    }
                }
                if x.abs() > 450.0 || y.abs() > 450.0 {
                    ok = false;
                }
                if ok {
                    model.things[i].alive = false;
                    model.things[i].energy -= 1.0;
                    model.things.push(candidate);
                    let s = model.things.len() -1;
                    model.things[i].children.push(s);
                }
            }
            if model.things[i].energy > 10.0 {
                if model.things[i].children.len() > 0 {
                    model.things[i].energy -= 1.0;
                    for k in 0..model.things[i].children.len() {
                        let other = model.things[i].children[k];
                        model.things[other].energy += 3.0;
                        model.things[other].grown = true;
                    }
                }
            }
        }
    }

    //check if the grown things are free
    for i in 0..model.things.len() {
        if model.things[i].grown == true {
            let indices = tree.get_elements(&model.things,model.things[i].position.x, model.things[i].position.y, 60.0);
            for k in 0..indices.len() {
                let mut ok = true;
                if let Some(parent) = model.things[i].parent {
                    if parent == indices[k] {
                        ok = false;
                    }
                }
                if i == indices[k] {
                    ok = false;
                }
                if ok {
                    //we can check this one
                    let d = model.things[i].distance(&model.things[indices[k]]);
                    if d < model.things[i].size + model.things[indices[k]].size +1.0 {
                        model.things[i].alive = false;
                    }
                }
            }
            if model.things[i].alive == true {
                if model.things[i].size > 29.0 {
                    model.things[i].alive = false;
                } else {
                    //grow it
                    if let Some(parent) = model.things[i].parent {
                        model.things[i].size += 1.0;
                        let direction = (model.things[i].position - model.things[parent].position).normalize();
                        model.things[i].position = model.things[parent].position + direction*(model.things[i].size+model.things[parent].size);
                    } else {
                        model.things[i].size += 1.0;
                    }
                }
            }
        }
    }
}

fn view(app: &App, model: &Model, frame: Frame) -> Frame {
    // Prepare to draw.
    let draw = app.draw();

    // Clear the background
    draw.background().color(WHITE);

    let scale = 1.0;

    //how far are we from the end -> to fade out
    let mut frac_end = (((app.elapsed_frames() + 120) as i32 - LENGTH_FRAME as i32) as f32)/100.0;
    frac_end = frac_end.max(0.0).min(1.0);

    let mut c = WHITE;
    c.alpha = 1.0-frac_end;

    //draw ALL THE THINGS
    for k in 0..model.things.len(){

        //get a color from the palette indexed by frac
        let mut c2:Rgba = model.palette.somecolor_frac_interpol(model.things[k].frac).into();
        // make it fade
        c2.alpha = 1.0-frac_end;
        let mut c3 = BLACK;
        c3.alpha = 1.0-frac_end;

        //draw in three steps
        draw.ellipse()
            .resolution(20)
            .xy(model.things[k].position*scale)
            .w_h(model.things[k].size*1.3*scale, model.things[k].size*1.3*scale)
            .color(c);
        draw.ellipse()
            .resolution(20)
            .xy(model.things[k].position*scale)
            .w_h(model.things[k].size*1.2*scale, model.things[k].size*1.2*scale)
            .color(c2);
        draw.ellipse()
            .resolution(20)
            .xy(model.things[k].position*scale)
            .w_h(model.things[k].size*0.5*scale, model.things[k].size*0.5*scale)
            .color(c3);

        //link to the children
        for l in 0..model.things[k].children.len() {
            draw.line()
                .start(model.things[k].position*scale)
                .end(model.things[model.things[k].children[l]].position*scale)
                .color(c3)
                .thickness((model.things[model.things[k].children[l]].size*0.5).min(5.0));
        }

    }

    // Write to the window frame.
    draw.to_frame(app, &frame).unwrap();

    //TODO add screenshot

    // Return the drawn frame.
    frame
}
<commit_msg>added name<commit_after>// Daily Sketch 2019-04-22
// Alexis Andre (@mactuitui)

mod colors;
mod quadtree;
use nannou::prelude::*;
use nannou::prelude::Frame;
use std::process::exit;
use nannou::rand::*;
use crate::colors::Palette;

const LENGTH_FRAME: u64 = 700;
const START_FRAME: u64 = 0;
const SEED: u64 = 1;

fn main() {
    nannou::app(model).update(update).run();
}

struct Model {
    palette: Palette,
    things: Vec<Thing>,
    rng: XorShiftRng,
}

// a Thing will be our main object, it'll try to grow outward
struct Thing {
    position: Vector2,
    size: f32,
    energy: f32,
    frac: f32,
    alive: bool,
    grown: bool,
    generation: usize,
    parent: Option<usize>,
    children: Vec<usize>
}
// we only care if they are on the same spot
impl PartialEq for Thing {
    fn eq(&self, other: &Thing) -> bool {
        self.position == other.position
    }
}
// define what to use for the quadtree
impl quadtree::WithPos for Thing {
    fn get_pos(&self) -> Vector2{
        self.position
    }
}

impl Thing {
    fn new(x: f32, y:f32, s:f32, f:f32, parent: Option<usize>) -> Self {
        let position = pt2(x,y);
        let size = s;
        let frac = f;

        Thing {
            position,
            size,
            energy: 0.0,
            frac,
            alive: true,
            grown: false,
            generation: 0,
            parent,
            children: Vec::new()
        }
    }

    fn distancept(&self, x:f32, y:f32) -> f32 {
        self.position.distance(vec2(x,y))
    }
    fn distance(&self, other: &Thing) -> f32 {
        self.position.distance(other.position)
    }
}

fn model(app: &App) -> Model {
    let _window = app
        .new_window()
        .with_dimensions(1024,1024)
        .view(view)
        .event(window_event)
        .build()
        .unwrap();

    //create the random values we will need each frame
    let rng  = XorShiftRng::seed_from_u64(SEED);

    let palette = Palette::new();

    let mut things = Vec::new();

    //we'll start from one thing in the middle
    let x = 0.0;//(rng.gen::<f32>()-0.5)*1024.0;
    let y = 0.0;//(rng.gen::<f32>()-0.5)*1024.0;
    let size = 1.0;//(rng.gen::<f32>()*30.0)+5.0;
    let frac = 0.0;//rng.gen::<f32>();
    let candidate = Thing::new(x,y,size, frac, None);
    things.push(candidate);

    Model { palette: palette, things, rng}
}

//nothing to do here
fn window_event(_app: &App, _model: &mut Model, event: WindowEvent) {
    match event {
        KeyPressed(_key) => {}
        KeyReleased(_key) => {}
        MouseMoved(_pos) => {}
        MousePressed(_button) => {}
        MouseReleased(_button) => {}
        MouseEntered => {}
        MouseExited => {}
        MouseWheel(_amount, _phase) => {}
        Moved(_pos) => {}
        Resized(_size) => {}
        Touch(_touch) => {}
        TouchPressure(_pressure) => {}
        HoveredFile(_path) => {}
        DroppedFile(_path) => {}
        HoveredFileCancelled => {}
        Focused => {}
        Unfocused => {}
        Closed => {}
    }
}

fn update(app: &App, model: &mut Model, _update: Update) {
    //try to grow each circle until it hits another one

    //recreate the tree
    let mut tree = quadtree::QuadTree::new();
    for i in 0..model.things.len() {
        model.things[i].grown = false;
    }
    //grow and branch out?
    let max_count = model.things.len();
    for i in 0..model.things.len() {
        tree.insert(&model.things,i);
    }
    for i in 0..max_count {
        if model.things[i].parent == None {
            if app.elapsed_frames() < 1000 {
                model.things[i].energy += 10000.0;
                if model.things[i].alive == true {
                    model.things[i].grown = true;
                }
            }
        }
        //move the size to the children
        if model.things[i].energy > 10.0 {
            //push the size to children
            //or branch
            let prob = 0.2;

            //if no child create a one

            //create a child
            if model.rng.gen::<f32>() < prob {
                let mut angle = model.rng.gen::<f32>()*TAU;
                if let Some(parent) = model.things[i].parent {
                    let from_parent = model.things[i].position - model.things[parent].position;
                    let base_angle = from_parent.y.atan2(from_parent.x);
                    let mut diff = (model.rng.gen::<f32>()-0.5)*2.0;
                    diff = diff.powf(19.0);
                    angle = base_angle+diff*PI;
                }

                let r = model.things[i].size + 1.0;
                let x = model.things[i].position.x + r*angle.cos();
                let y = model.things[i].position.y + r*angle.sin();
                let s = 1.0;
                let mut candidate = Thing::new(x,y,s,angle/PI, Some(i));
                candidate.generation = model.things[i].generation+1;
                let indices = tree.get_elements(&model.things,x,y, 50.0);
                let mut ok = true;
                for k in 0..indices.len() {
                    let d = model.things[indices[k]].distancept(x,y);
                    if d  < model.things[indices[k]].size +1.0  {
                        ok = false;
                    }
                }
                if x.abs() > 450.0 || y.abs() > 450.0 {
                    ok = false;
                }
                if ok {
                    model.things[i].alive = false;
                    model.things[i].energy -= 1.0;
                    model.things.push(candidate);
                    let s = model.things.len() -1;
                    model.things[i].children.push(s);
                }
            }
            if model.things[i].energy > 10.0 {
                if model.things[i].children.len() > 0 {
                    model.things[i].energy -= 1.0;
                    for k in 0..model.things[i].children.len() {
                        let other = model.things[i].children[k];
                        model.things[other].energy += 3.0;
                        model.things[other].grown = true;
                    }
                }
            }
        }
    }

    //check if the grown things are free
    for i in 0..model.things.len() {
        if model.things[i].grown == true {
            let indices = tree.get_elements(&model.things,model.things[i].position.x, model.things[i].position.y, 60.0);
            for k in 0..indices.len() {
                let mut ok = true;
                if let Some(parent) = model.things[i].parent {
                    if parent == indices[k] {
                        ok = false;
                    }
                }
                if i == indices[k] {
                    ok = false;
                }
                if ok {
                    //we can check this one
                    let d = model.things[i].distance(&model.things[indices[k]]);
                    if d < model.things[i].size + model.things[indices[k]].size +1.0 {
                        model.things[i].alive = false;
                    }
                }
            }
            if model.things[i].alive == true {
                if model.things[i].size > 29.0 {
                    model.things[i].alive = false;
                } else {
                    //grow it
                    if let Some(parent) = model.things[i].parent {
                        model.things[i].size += 1.0;
                        let direction = (model.things[i].position - model.things[parent].position).normalize();
                        model.things[i].position = model.things[parent].position + direction*(model.things[i].size+model.things[parent].size);
                    } else {
                        model.things[i].size += 1.0;
                    }
                }
            }
        }
    }
}

fn view(app: &App, model: &Model, frame: Frame) -> Frame {
    // Prepare to draw.
    let draw = app.draw();

    // Clear the background
    draw.background().color(WHITE);

    let scale = 1.0;

    //how far are we from the end -> to fade out
    let mut frac_end = (((app.elapsed_frames() + 120) as i32 - LENGTH_FRAME as i32) as f32)/100.0;
    frac_end = frac_end.max(0.0).min(1.0);

    let mut c = WHITE;
    c.alpha = 1.0-frac_end;

    //draw ALL THE THINGS
    for k in 0..model.things.len(){

        //get a color from the palette indexed by frac
        let mut c2:Rgba = model.palette.somecolor_frac_interpol(model.things[k].frac).into();
        // make it fade
        c2.alpha = 1.0-frac_end;
        let mut c3 = BLACK;
        c3.alpha = 1.0-frac_end;

        //draw in three steps
        draw.ellipse()
            .resolution(20)
            .xy(model.things[k].position*scale)
            .w_h(model.things[k].size*1.3*scale, model.things[k].size*1.3*scale)
            .color(c);
        draw.ellipse()
            .resolution(20)
            .xy(model.things[k].position*scale)
            .w_h(model.things[k].size*1.2*scale, model.things[k].size*1.2*scale)
            .color(c2);
        draw.ellipse()
            .resolution(20)
            .xy(model.things[k].position*scale)
            .w_h(model.things[k].size*0.5*scale, model.things[k].size*0.5*scale)
            .color(c3);

        //link to the children
        for l in 0..model.things[k].children.len() {
            draw.line()
                .start(model.things[k].position*scale)
                .end(model.things[model.things[k].children[l]].position*scale)
                .color(c3)
                .thickness((model.things[model.things[k].children[l]].size*0.5).min(5.0));
        }

    }

    // Write to the window frame.
    draw.to_frame(app, &frame).unwrap();

    //TODO add screenshot

    // Return the drawn frame.
    frame
}
<|endoftext|>