<commit_before>use crate::prelude::*;

const HEADER_CONTAINER: &str = "header_container";
const BODY_CONTAINER: &str = "body_container";

#[derive(Default,AsAny)]
pub struct TabHeaderState {
    //Only used during initialization
    on_header_click_callback: Option<Box<dyn 'static + Fn(&mut StatesContext, Point) -> bool>>,
    //Only used during initialization
    on_close_click_callback: Option<Box<dyn 'static + Fn(&mut StatesContext, Point) -> bool>>
}

impl State for TabHeaderState {}

widget!(
    /// The `TabHeader` widget is used internally to managege tabs headers. Not meant for other uses.
    TabHeader<TabHeaderState>: ChangedHandler
    {
        /// Sets or shares the background property.
        background: Brush,

        /// Sets or shares the border radius property.
        border_radius: f64,

        /// Sets or shares the border thickness property.
        border_width: Thickness,

        /// Sets or shares the border brush property.
        border_brush: Brush,

        /// Sets or shares the padding property.
        padding: Thickness,

        /// Sets or shares the foreground property.
        foreground: Brush,

        /// Sets or shares the text property.
        text: String16,

        /// Sets or share the font size property.
        font_size: f64,

        /// Sets or shares the font property.
        font: String,

        /// Sets or shares the icon property.
        icon: String,

        /// Sets or shares the icon brush property.
        icon_brush: Brush,

        /// Sets or share the icon font size property.
        icon_size: f64,

        /// Sets or shares the icon font property.
        icon_font: String,

        /// Sets or shares the selected property.
        selected: bool,

        /// Sets or shares the spacing between icon and text.
        spacing: f64,

        /// Sets or shares the spacing between icon and text.
        close_button: bool
    }
);

impl TabHeader
{
    pub fn on_header_click<T: 'static + Fn(&mut StatesContext, Point) -> bool>(mut self,callback: T)->Self
    {
        self.state.on_header_click_callback = Some(Box::new(callback));
        self
    }
    pub fn on_close_click<T: 'static + Fn(&mut StatesContext, Point) -> bool>(mut self,callback: T)->Self
    {
        self.state.on_close_click_callback = Some(Box::new(callback));
        self
    }
}

impl Template for TabHeader {
    fn template(mut self, id: Entity, ctx: &mut BuildContext) -> Self {
        let mut toggle_button = ToggleButton::new()
        .background(id)
        .border_radius(id)
        .border_width(id)
        .border_brush(id)
        .padding(id)
        .foreground(id)
        .text(id)
        .font_size(id)
        .font(id)
        .icon(id)
        .icon_brush(id)
        .icon_size(id)
        .icon_font(id)
        .selected(id)
        .spacing(id);

        if let Some(callback) = self.state.on_header_click_callback.take() {toggle_button = toggle_button.on_click(callback);}

        let mut button = Button::new()
        .background(id)
        .border_radius(id)
        .border_width(id)
        .border_brush(id)
        .padding(id)
        .foreground(id)
        .font_size(id)
        .font(id)
        .icon(id)
        .icon_brush(id)
        .icon_size(id)
        .icon_font(id)
        .spacing(id)
        .pressed(false)
        .text("X")
        .width(2.0);

        if let Some(callback) = self.state.on_close_click_callback.take() {button = button.on_click(callback);}
        //if self.close_button() == false {button = button.visibility(Visibility::Collapsed);}

        self.name("TabWidget")
        .selected(false)
        .height(36.0)
        .min_width(64.0)
        .background(colors::LYNCH_COLOR)
        .border_radius(4.0)
        .border_width(0.0)
        .border_brush("transparent")
        .padding((16.0, 0.0, 16.0, 0.0))
        .foreground(colors::LINK_WATER_COLOR)
        .text("Unnamed Tab")
        .font_size(fonts::FONT_SIZE_12)
        .font("Roboto Regular")
        .icon("")
        .icon_font("Material Icons")
        .icon_size(fonts::ICON_FONT_SIZE_12)
        .icon_brush(colors::LINK_WATER_COLOR)
        .spacing(8.0)
        .child(
            Stack::new()
            .orientation("horizontal")
            .child(toggle_button.build(ctx))
            .child(button.build(ctx))
            .build(ctx)
        )
    }
}

pub enum Action
{
    SelectByIndex(usize),
    SelectByBody(Entity),
    Add(String,Entity),
    Remove(Entity)
}

#[derive(Default, AsAny)]
pub struct TabWidgetState {
    actions: Vec<Action>,

    header_container: Entity,
    body_container: Entity,

    tabs: Vec<(Entity,Entity)>,//Header , Body
    selected: usize
}

impl TabWidgetState {
    pub fn select_by_index(&mut self,index: usize)
    {
        self.actions.push(Action::SelectByIndex(index));
    }
    pub fn select_by_body(&mut self,entity: Entity)
    {
        self.actions.push(Action::SelectByBody(entity));
    }

    pub fn remove_by_body(&mut self,entity: Entity)
    {
        self.actions.push(Action::Remove(entity));
    }

    pub fn tab<T: Into<String>>(&mut self,header: T, body: Entity)
    {
        self.actions.push(Action::Add(header.into(),body));
        if self.tabs.len() == 0 {self.select_by_index(0);}//At this point the tab has not been added yet, so we can check if len is == 0
    }

    pub fn get_index(&self,tab_body: Entity)->Option<usize>
    {
        for i in 0..self.tabs.len()
        {
            let (_,body) = self.tabs[i];
            if body == tab_body {return Some(i);}
        }
        return None;
    }

    fn refresh_selected_tab(&mut self,ctx: &mut Context)
    {
        let tab = self.tabs[self.selected];
        ctx.get_widget(tab.0).set("selected",true);
        ctx.get_widget(tab.1).set("visibility",Visibility::Visible);
    }

    fn select_by_index_internal(&mut self,ctx: &mut Context,mut index: usize)
    {
        if self.tabs.len() == 0 {return;}//No tabs could be selected if there are no one

        //If the passed index is greater than tab count, select the last one
        if index >= self.tabs.len() && index != 0 {index = self.tabs.len()-1;}

        if self.selected != index
        {
            let current_tab = self.tabs[self.selected];
            let new_tab = self.tabs[index];

            //Toggle current button, the new button is toggled by user click
            ctx.get_widget(current_tab.0).set("selected",false);

            //Hide current body
            ctx.get_widget(current_tab.1).set("visibility",Visibility::Hidden);

            //The new tab is not "selected" because it is already done by the user click

            //Show new body
            ctx.get_widget(new_tab.1).set("visibility",Visibility::Visible);

            self.selected = index;
        }
    }

    fn add_tab_internal(&mut self,ctx: &mut Context,header_text: String,body: Entity)
    {
        //Create the new tab
        let header = create_tab_header(ctx,header_text,body);

        //Set tab body hidden
        ctx.get_widget(body).set("visibility",Visibility::Hidden);

        //Push button to the header container
        ctx.append_child_entity_to(header,self.header_container);

        //Push the body to the body container
        ctx.append_child_entity_to(body,self.body_container);

        //Push the new tab to the list
        self.tabs.push((header,body));

        //If the added tab is the first
        if self.tabs.len() == 1
        {
            //Select the tab just inserted
            self.selected = 0;
            self.refresh_selected_tab(ctx);
        }
    }

    fn remove_tab_internal(&mut self,ctx: &mut Context,body: Entity)
    {
        match self.get_index(body)
        {
            Some(index)=>
            {
                let (header,body) = self.tabs.remove(index);

                //Push button to the header container
                ctx.remove_child_from(header,self.header_container);

                //Push the body to the body container
                ctx.remove_child_from(body,self.body_container);

                //If there is at least one tab
                if self.tabs.len() != 0
                {
                    //If selected is greater than tab count, select the last one
                    if self.selected >= self.tabs.len(){self.selected = self.tabs.len()-1;}

                    //Only update current selection if the removed tab is lesser than the selected.
                    //If it is greater, there is no need to update, but simply remove the target tab
                    if index <= self.selected {self.refresh_selected_tab(ctx);}
                }

            }
            None=>()
        }
    }

}

impl State for TabWidgetState {
    fn init(&mut self, _: &mut Registry, ctx: &mut Context)
    {
        self.header_container = ctx.child(HEADER_CONTAINER).entity();
        self.body_container = ctx.child(BODY_CONTAINER).entity();
    }
    fn update(&mut self, _: &mut Registry, ctx: &mut Context)
    {
        let actions: Vec<Action> = self.actions.drain(..).collect();
        for action in actions
        {
            match action
            {
                Action::SelectByIndex(index)=>{self.select_by_index_internal(ctx,index);}
                Action::SelectByBody(body)=>
                {
                    if let Some(index) = self.get_index(body){self.select_by_index_internal(ctx,index)}
                }
                Action::Add(header_text,body)=>{self.add_tab_internal(ctx,header_text,body);}
                Action::Remove(body)=>{self.remove_tab_internal(ctx,body);}
            }
        }
    }
}

widget!(
    /// The `TabWidget` widget can store and control multiple tabs with arbitrary content. Only the selected tab will show it's content.
    TabWidget<TabWidgetState>: ChangedHandler
    {
        /// Sets or shares the visibility of the close button.
        close_button: bool,
        /// Sets or shares the spacing between tabs.
        spacing: f64
    }
);

impl TabWidget
{
    pub fn tab<T: Into<String>>(mut self,header: T, body: Entity)->Self
    {
        self.state.actions.push(Action::Add(header.into(),body));
        self
    }
}

impl Template for TabWidget {
    fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
        self.name("TabWidget")
        .close_button(true)
        .child(
            Stack::new()
            .orientation("vertical")
            .child(
                Stack::new()
                .id(HEADER_CONTAINER)
                .orientation("horizontal")
                .spacing(id)
                .build(ctx)
            )
            .child(
                Container::new()
                .id(BODY_CONTAINER)
                .build(ctx)
            )
            .build(ctx)
        )
    }
}

fn create_tab_header(ctx: &mut Context,text: String,body: Entity)->Entity
{
    let cloned_entity = ctx.entity;
    TabHeader::new()
    .close_button(cloned_entity)
    .text(String16::from(text))
    .on_header_click(move|states,_|{
        states.get_mut::<TabWidgetState>(cloned_entity).select_by_body(body);
        true
    })
    .on_close_click(move|states,_|{
        states.get_mut::<TabWidgetState>(cloned_entity).remove_by_body(body);
        true
    })
    .build(&mut ctx.build_context())
}
<commit_msg>Added comments and enable the ability to toggle the visibility of the close button<commit_after>use crate::prelude::*;

// --- KEYS --
const HEADER_CONTAINER: &str = "header_container";
const BODY_CONTAINER: &str = "body_container";
// --- KEYS --

/**
The `TabHeaderState` is used to store some callbacks that will be applied during template function.
Once the template function is called, they are no more used.
*/
#[derive(Default,AsAny)]
pub struct TabHeaderState {
    //Callback called when user click on the header of the tab (normally used to switch to the clicked tab). Only used during initialization
    on_header_click_callback: Option<Box<dyn 'static + Fn(&mut StatesContext, Point) -> bool>>,
    //Callback called when user click on the close button near the tab header (normally used to close the tab). Only used during initialization
    on_close_click_callback: Option<Box<dyn 'static + Fn(&mut StatesContext, Point) -> bool>>
}

impl State for TabHeaderState {}

widget!(
    /// The `TabHeader` widget is used internally to managed tabs headers. Not meant for other uses.
    TabHeader<TabHeaderState>: ChangedHandler
    {
        /// Sets or shares the background property.
        background: Brush,

        /// Sets or shares the border radius property.
        border_radius: f64,

        /// Sets or shares the border thickness property.
        border_width: Thickness,

        /// Sets or shares the border brush property.
        border_brush: Brush,

        /// Sets or shares the padding property.
        padding: Thickness,

        /// Sets or shares the foreground property.
        foreground: Brush,

        /// Sets or shares the text property.
        text: String16,

        /// Sets or share the font size property.
        font_size: f64,

        /// Sets or shares the font property.
        font: String,

        /// Sets or shares the icon property.
        icon: String,

        /// Sets or shares the icon brush property.
        icon_brush: Brush,

        /// Sets or share the icon font size property.
        icon_size: f64,

        /// Sets or shares the icon font property.
        icon_font: String,

        /// Sets or shares the selected property.
        selected: bool,

        /// Sets or shares the spacing between icon and text.
        spacing: f64,

        /// Sets or shares the close button visibility.
        close_button: Visibility
    }
);

impl TabHeader
{
    ///Set the callback that is called when user click on the header (generally used to switch tab)
    pub fn on_header_click<T: 'static + Fn(&mut StatesContext, Point) -> bool>(mut self,callback: T)->Self
    {
        self.state.on_header_click_callback = Some(Box::new(callback));
        self
    }

    ///Set the callback that is called when user click on the close button near the header (generally used to close the tab)
    pub fn on_close_click<T: 'static + Fn(&mut StatesContext, Point) -> bool>(mut self,callback: T)->Self
    {
        self.state.on_close_click_callback = Some(Box::new(callback));
        self
    }
}

impl Template for TabHeader {
    fn template(mut self, id: Entity, ctx: &mut BuildContext) -> Self {
        let mut toggle_button = ToggleButton::new()
        .background(id)
        .border_radius(id)
        .border_width(id)
        .border_brush(id)
        .padding(id)
        .foreground(id)
        .text(id)
        .font_size(id)
        .font(id)
        .icon(id)
        .icon_brush(id)
        .icon_size(id)
        .icon_font(id)
        .selected(id)
        .spacing(id);

        if let Some(callback) = self.state.on_header_click_callback.take() {toggle_button = toggle_button.on_click(callback);}

        let mut button = Button::new()
        .background(id)
        .border_radius(id)
        .border_width(id)
        .border_brush(id)
        .padding(id)
        .foreground(id)
        .font_size(id)
        .font(id)
        .icon(id)
        .icon_brush(id)
        .icon_size(id)
        .icon_font(id)
        .spacing(id)
        .text("X")
        .width(2)
        .visibility(("close_button",id));

        if let Some(callback) = self.state.on_close_click_callback.take() {button = button.on_click(callback);}
        //if self.close_button() == false {button = button.visibility(Visibility::Collapsed);}

        self.name("TabWidget")
        .selected(false)
        .height(36)
        .min_width(64)
        .background(colors::LYNCH_COLOR)
        .border_radius(4)
        .border_width(0)
        .border_brush("transparent")
        .padding((16, 0, 16, 0))
        .foreground(colors::LINK_WATER_COLOR)
        .text("Unnamed Tab")
        .font_size(fonts::FONT_SIZE_12)
        .font("Roboto Regular")
        .icon("")
        .icon_font("Material Icons")
        .icon_size(fonts::ICON_FONT_SIZE_12)
        .icon_brush(colors::LINK_WATER_COLOR)
        .spacing(8)
        .close_button(Visibility::Visible)
        .child(
            Stack::new()
            .orientation("horizontal")
            .child(toggle_button.build(ctx))
            .child(button.build(ctx))
            .build(ctx)
        )
    }
}

///Used to internally
enum TabWidgetAction
{
    SelectByIndex(usize),
    SelectByBody(Entity),
    Add(String,Entity),
    Remove(Entity),
    SetCloseButtonVisibility(bool)
}
/**
Through the TabWidgetState it is possible to control the behaviour of TabWidget.
Nearly every called function on TabWidgetState will not be executed immediatly, but they are instead stored and executed during the update phase,
in the same order they are submitted.
*/
#[derive(Default, AsAny)]
pub struct TabWidgetState {
    ///Store all the pending actions. During the update call they will be resolved
    actions: Vec<TabWidgetAction>,

    header_container: Entity,
    body_container: Entity,

    tabs: Vec<(Entity,Entity)>,//Header , Body
    selected: usize,

    close_button_visibility: bool
}

impl TabWidgetState {
    /**
    Switch the selected tab with one with the passed index.
    If the index is greater than the tab count, the last one will be selected.
    */
    pub fn select_by_index(&mut self,index: usize)
    {
        self.actions.push(TabWidgetAction::SelectByIndex(index));
    }
    /**
    Switch the selected tab with one with the passed body.
    If the no such body is found among the present tabs, nothing happen.
    */
    pub fn select_by_body(&mut self,entity: Entity)
    {
        self.actions.push(TabWidgetAction::SelectByBody(entity));
    }

    /**
    Remove the tab with the passed body.
    If the no such body is found among the present tabs, nothing happen.
    */
    pub fn remove_by_body(&mut self,entity: Entity)
    {
        self.actions.push(TabWidgetAction::Remove(entity));
    }

    ///Add a new tab to the widget.
    pub fn add_tab<T: Into<String>>(&mut self,header: T, body: Entity)
    {
        self.actions.push(TabWidgetAction::Add(header.into(),body));

        //At this point the tab has not been added yet, so we can check if len is == 0
        if self.tabs.len() == 0 {self.select_by_index(0);}
    }

    ///Set the close button visibility of all the tabs.
    pub fn set_close_button_visibility(&mut self,value: bool)
    {
        self.actions.push(TabWidgetAction::SetCloseButtonVisibility(value));
    }
    ///Get the close button visibility status
    pub fn get_close_button_visibility(&self)->bool{self.close_button_visibility}

    /**
    Get the tab index associated with the passed body.
    If it is not found, None is returned.
    */
    pub fn get_index(&self,tab_body: Entity)->Option<usize>
    {
        for i in 0..self.tabs.len()
        {
            let (_,body) = self.tabs[i];
            if body == tab_body {return Some(i);}
        }
        return None;
    }

    /**
    Visually refresh the current tab. This is used when, for example, the selected tab is removed,
    so the tab that take it's place need to be visually updated.
    */
    fn refresh_selected_tab(&mut self,ctx: &mut Context)
    {
        let tab = self.tabs[self.selected];
        ctx.get_widget(tab.0).set("selected",true);
        ctx.get_widget(tab.1).set("visibility",Visibility::Visible);
    }

    /**
    Change the selected tab by index. Unlike the public "select_by_index", this happen immediatly.
    */
    fn select_by_index_internal(&mut self,ctx: &mut Context,mut index: usize)
    {
        //No tabs could be selected if there are no one, so return immediatly
        if self.tabs.len() == 0 {return;}

        //If the passed index is greater than tab count, select the last one
        if index >= self.tabs.len() && index != 0 {index = self.tabs.len()-1;}

        if self.selected != index
        {
            let current_tab = self.tabs[self.selected];
            let new_tab = self.tabs[index];

            //Toggle current button, the new button is toggled by user click
            ctx.get_widget(current_tab.0).set("selected",false);

            //Hide current body
            ctx.get_widget(current_tab.1).set("visibility",Visibility::Hidden);

            //The new tab is not "selected" because it is already done by the user click

            //Show new body
            ctx.get_widget(new_tab.1).set("visibility",Visibility::Visible);

            self.selected = index;
        }
    }

    ///Add a new tab to the widget. Unlike the public "add_tab", this happen immediatly.
    fn add_tab_internal(&mut self,ctx: &mut Context,header_text: String,body: Entity)
    {
        //Create the new tab
        let header = self.create_tab_header(ctx,header_text,body);

        //Set tab body hidden
        ctx.get_widget(body).set("visibility",Visibility::Hidden);

        //Push button to the header container
        ctx.append_child_entity_to(header,self.header_container);

        //Push the body to the body container
        ctx.append_child_entity_to(body,self.body_container);

        //Push the new tab to the list
        self.tabs.push((header,body));

        //If the added tab is the first
        if self.tabs.len() == 1
        {
            //Select the tab just inserted
            self.selected = 0;
            self.refresh_selected_tab(ctx);
        }
    }

    ///Remove a tab from the widget. Unlike the public "remove_tab", this happen immediatly.
    fn remove_tab_internal(&mut self,ctx: &mut Context,body: Entity)
    {
        match self.get_index(body)
        {
            Some(index)=>
            {
                //Pop the index tab out of the stored tabs
                let (header,body) = self.tabs.remove(index);

                //Push button to the header container
                ctx.remove_child_from(header,self.header_container);

                //Push the body to the body container
                ctx.remove_child_from(body,self.body_container);

                //If there is at least one tab
                if self.tabs.len() != 0
                {
                    //If selected is greater than tab count, select the last one
                    if self.selected >= self.tabs.len(){self.selected = self.tabs.len()-1;}

                    //Only update current selection if the removed tab is lesser than the selected.
                    //If it is greater, there is no need to update, but simply remove the target tab
                    if index <= self.selected {self.refresh_selected_tab(ctx);}
                }

            }
            None=>()
        }
    }

    /**
    Set the visibility of the close button on all tabs. Unlike the public "set_close_button_visibility", this happen immediatly.
    If the passed "value" is equal to "self.close_button_visibility", so the requested visibility is already present, nothing happen.
    */
    fn set_close_button_visibility_internal(&mut self,ctx: &mut Context,value: bool)
    {
        if self.close_button_visibility != value
        {
            self.close_button_visibility = value;
            let new_visibility = if self.close_button_visibility {Visibility::Visible} else {Visibility::Collapsed};
            for tab in &self.tabs
            {
                ctx.get_widget(tab.0).set("close_button",new_visibility);
            }
        }
    }

    //Create a new TabHeader entity and return it. For internal use.
    fn create_tab_header(&self,ctx: &mut Context,text: String,body: Entity)->Entity
    {
        let cloned_entity = ctx.entity;
        TabHeader::new()
        .visibility(if self.close_button_visibility {Visibility::Visible} else {Visibility::Collapsed})
        .text(String16::from(text))
        .on_header_click(move|states,_|{
            states.get_mut::<TabWidgetState>(cloned_entity).select_by_body(body);
            true
        })
        .on_close_click(move|states,_|{
            states.get_mut::<TabWidgetState>(cloned_entity).remove_by_body(body);
            true
        })
        .build(&mut ctx.build_context())
    }
}

impl State for TabWidgetState {
    fn init(&mut self, _: &mut Registry, ctx: &mut Context)
    {
        self.header_container = ctx.child(HEADER_CONTAINER).entity();
        self.body_container = ctx.child(BODY_CONTAINER).entity();
    }
    fn update(&mut self, _: &mut Registry, ctx: &mut Context)
    {
        let actions: Vec<TabWidgetAction> = self.actions.drain(..).collect();
        for action in actions
        {
            match action
            {
                TabWidgetAction::SelectByIndex(index)=>{self.select_by_index_internal(ctx,index);}
                TabWidgetAction::SelectByBody(body)=>
                {
                    if let Some(index) = self.get_index(body){self.select_by_index_internal(ctx,index)}
                }
                TabWidgetAction::Add(header_text,body)=>{self.add_tab_internal(ctx,header_text,body);}
                TabWidgetAction::Remove(body)=>{self.remove_tab_internal(ctx,body);}
                TabWidgetAction::SetCloseButtonVisibility(value)=>{self.set_close_button_visibility_internal(ctx,value);}
            }
        }
    }
}

widget!(
    /**
    The `TabWidget` widget can store and control multiple tabs with arbitrary content. Only the selected tab will show it's content.

    This example creates a TabWidget:
    ```rust
    TabWidget::new()
    .tab("Tab header 1",TextBlock::new().text("Tab content 1").build(ctx))
    .tab("Tab header 2",TextBlock::new().text("Tab content 2").build(ctx))
    .tab("Tab header 3",TextBlock::new().text("Tab content 3").build(ctx))
    .build(ctx)
     ```
     */
    TabWidget<TabWidgetState>: ChangedHandler
    {
        /// Sets or shares the visibility of the close button.
        //close_button: bool,
        /// Sets or shares the spacing between tabs.
        spacing: f64
    }
);

impl TabWidget
{
    ///Set the close button visibility
    pub fn close_button(mut self,value: bool)->Self
    {
        self.state.actions.push(TabWidgetAction::SetCloseButtonVisibility(value));
        self
    }
    ///Add a tab the widget
    pub fn tab<T: Into<String>>(mut self,header: T, body: Entity)->Self
    {
        self.state.actions.push(TabWidgetAction::Add(header.into(),body));
        self
    }
}

impl Template for TabWidget {
    fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
        self.name("TabWidget")
        .child(
            Grid::new()
                .rows(Rows::new().add(32).add("*"))
                .child(
                    Stack::new()
                        .id(HEADER_CONTAINER)
                        .orientation("horizontal")
                        .spacing(id)
                        .build(ctx)
                )
                .child(
                    Grid::new()
                        .attach(Grid::row(1))
                        .id(BODY_CONTAINER)
                        .build(ctx)
                )
                .build(ctx)
        )
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::{stdin, BufReader, Read};
use std::path::Path;

use memmap::MmapOptions;
use needletail::parse_fastx_reader;
use needletail::parser::Format;
use rayon::prelude::*;

use crate::filtering::FilterParams;
use crate::serialization::{
    read_finch_file, read_mash_file, MultiSketch, Sketch, FINCH_BIN_EXT, FINCH_EXT, MASH_EXT,
};
use crate::sketch_schemes::SketchParams;

pub mod distance;
pub mod filtering;
pub mod sketch_schemes;
// it would be nice if there was a `pub(in main)` or something for
// main_parsing so we don't import it for `lib` itself
pub mod errors;
#[cfg(feature = "python")]
pub mod python;
pub mod serialization;
pub mod statistics;

use crate::errors::FinchResult;

pub fn sketch_files(
    filenames: &[&str],
    sketch_params: &SketchParams,
    filters: &FilterParams,
) -> FinchResult<Vec<Sketch>> {
    let sketches: FinchResult<Vec<Sketch>> = filenames
        .par_iter()
        .map(|filename| {
            // open the file with a special case to handle stdin
            let reader: Box<dyn Read + Send> = if filename == &"-" {
                // We're not locking it so technically not thread safe
                Box::new(stdin())
            } else {
                Box::new(File::open(&Path::new(filename))?)
            };
            // sketch!
            Ok(sketch_stream(reader, filename, sketch_params, &filters)?)
        })
        .collect();
    sketches
}

pub fn sketch_stream<'a>(
    reader: Box<dyn Read + Send + 'a>,
    name: &str,
    sketch_params: &SketchParams,
    filters: &FilterParams,
) -> FinchResult<Sketch> {
    let mut filter_params = filters.clone();
    let mut sketcher = sketch_params.create_sketcher();
    // TODO: remove expects after removing failure
    let mut fastx_reader = parse_fastx_reader(reader).expect("valid file TODO");
    let mut seq_type = None;
    while let Some(record) = fastx_reader.next() {
        let seqrec = record.expect("invalid record");
        if seq_type.is_none() {
            seq_type = Some(seqrec.format());
        }
        sketcher.process(seqrec);
    }

    // disable filtering for FASTA files unless it was explicitly specified
    if filter_params.filter_on.is_none() {
        filter_params.filter_on = match seq_type.expect("Should have got a type") {
            Format::Fasta => Some(false),
            Format::Fastq => Some(true),
        };
    }

    let (seq_length, num_valid_kmers) = sketcher.total_bases_and_kmers();
    let hashes = sketcher.to_vec();

    // do filtering
    let mut filtered_hashes = filter_params.filter_counts(&hashes);
    sketch_params.process_post_filter(&mut filtered_hashes, name)?;

    Ok(Sketch {
        name: name.to_string(),
        seq_length,
        num_valid_kmers,
        comment: "".to_string(),
        hashes: filtered_hashes,
        filter_params,
        sketch_params: sketch_params.clone(),
    })
}

pub fn open_sketch_file(filename: &str) -> FinchResult<Vec<Sketch>> {
    let file = File::open(filename).map_err(|_| format_err!("Error opening {}", &filename))?;
    if filename.ends_with(MASH_EXT) {
        let mut buf_reader = BufReader::new(file);
        read_mash_file(&mut buf_reader)
    } else if filename.ends_with(FINCH_BIN_EXT) {
        let mut buf_reader = BufReader::new(file);
        read_finch_file(&mut buf_reader)
    } else if filename.ends_with(FINCH_EXT) || filename.ends_with(".json") {
        let mapped = unsafe { MmapOptions::new().map(&file)? };
        let multisketch: MultiSketch = serde_json::from_slice(&mapped)
            .map_err(|_| format_err!("Error parsing {}", &filename))?;
        multisketch.to_sketches()
    } else {
        Err(format_err!("File suffix is not *.bsk, *.msh, or *.sk"))
    }
}
<commit_msg>Make open_sketch_file work with paths<commit_after>use std::fs::File;
use std::io::{stdin, BufReader, Read};
use std::path::Path;

use memmap::MmapOptions;
use needletail::parse_fastx_reader;
use needletail::parser::Format;
use rayon::prelude::*;

use crate::filtering::FilterParams;
use crate::serialization::{
    read_finch_file, read_mash_file, MultiSketch, Sketch, FINCH_BIN_EXT, FINCH_EXT, MASH_EXT,
};
use crate::sketch_schemes::SketchParams;

pub mod distance;
pub mod filtering;
pub mod sketch_schemes;
// it would be nice if there was a `pub(in main)` or something for
// main_parsing so we don't import it for `lib` itself
pub mod errors;
#[cfg(feature = "python")]
pub mod python;
pub mod serialization;
pub mod statistics;

use crate::errors::FinchResult;

pub fn sketch_files(
    filenames: &[&str],
    sketch_params: &SketchParams,
    filters: &FilterParams,
) -> FinchResult<Vec<Sketch>> {
    let sketches: FinchResult<Vec<Sketch>> = filenames
        .par_iter()
        .map(|filename| {
            // open the file with a special case to handle stdin
            let reader: Box<dyn Read + Send> = if filename == &"-" {
                // We're not locking it so technically not thread safe
                Box::new(stdin())
            } else {
                Box::new(File::open(&Path::new(filename))?)
            };
            // sketch!
            Ok(sketch_stream(reader, filename, sketch_params, &filters)?)
        })
        .collect();
    sketches
}

pub fn sketch_stream<'a>(
    reader: Box<dyn Read + Send + 'a>,
    name: &str,
    sketch_params: &SketchParams,
    filters: &FilterParams,
) -> FinchResult<Sketch> {
    let mut filter_params = filters.clone();
    let mut sketcher = sketch_params.create_sketcher();
    // TODO: remove expects after removing failure
    let mut fastx_reader = parse_fastx_reader(reader).expect("valid file TODO");
    let mut seq_type = None;
    while let Some(record) = fastx_reader.next() {
        let seqrec = record.expect("invalid record");
        if seq_type.is_none() {
            seq_type = Some(seqrec.format());
        }
        sketcher.process(seqrec);
    }

    // disable filtering for FASTA files unless it was explicitly specified
    if filter_params.filter_on.is_none() {
        filter_params.filter_on = match seq_type.expect("Should have got a type") {
            Format::Fasta => Some(false),
            Format::Fastq => Some(true),
        };
    }

    let (seq_length, num_valid_kmers) = sketcher.total_bases_and_kmers();
    let hashes = sketcher.to_vec();

    // do filtering
    let mut filtered_hashes = filter_params.filter_counts(&hashes);
    sketch_params.process_post_filter(&mut filtered_hashes, name)?;

    Ok(Sketch {
        name: name.to_string(),
        seq_length,
        num_valid_kmers,
        comment: "".to_string(),
        hashes: filtered_hashes,
        filter_params,
        sketch_params: sketch_params.clone(),
    })
}

pub fn open_sketch_file<P: AsRef<Path>>(path: P) -> FinchResult<Vec<Sketch>> {
    let p = path.as_ref();
    let filename = p
        .file_name()
        .ok_or_else(|| format_err!("Path does not have a filename: {:?}", p))?
        .to_string_lossy();
    let file = File::open(p).map_err(|_| format_err!("Error opening {:?}", p))?;
    if filename.ends_with(MASH_EXT) {
        let mut buf_reader = BufReader::new(file);
        read_mash_file(&mut buf_reader)
    } else if filename.ends_with(FINCH_BIN_EXT) {
        let mut buf_reader = BufReader::new(file);
        read_finch_file(&mut buf_reader)
    } else if filename.ends_with(FINCH_EXT) || filename.ends_with(".json") {
        let mapped = unsafe { MmapOptions::new().map(&file)? };
        let multisketch: MultiSketch =
            serde_json::from_slice(&mapped).map_err(|_| format_err!("Error parsing {:?}", &p))?;
        multisketch.to_sketches()
    } else {
        Err(format_err!("File suffix is not *.bsk, *.msh, or *.sk"))
    }
}
<|endoftext|>


<commit_before>#![crate_name = "rustful_macros"]

#![comment = "Macros for rustful"]
#![license = "MIT"]
#![crate_type = "dylib"]

#![doc(html_root_url = "http://ogeon.github.io/rustful/doc/")]

#![feature(macro_rules, plugin_registrar, quote, phase)]

//!This crate provides some helpful macros for rustful, including `router!` and `routes!`.
//!
//!#`router!`
//!The `router!` macro generates a `Router` containing the provided handlers and routing tree.
//!This can be useful to lower the risk of typing errors, among other things.
//!
//!##Example 1
//!
//!```rust ignore
//!#![feature(phase)]
//!#[phase(plugin)]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!
//!let router = router!{
//!	"/about" => Get: about_us,
//!	"/user/:user" => Get: show_user,
//!	"/product/:name" => Get: show_product,
//!	"/*" => Get: show_error,
//!	"/" => Get: show_welcome
//!};
//!```
//!
//!##Example 2
//!
//!```rust ignore
//!#![feature(phase)]
//!#[phase(plugin)]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!
//!let router = router!{
//!	"/" => Get: show_home,
//!	"home" => Get: show_home,
//!	"user/:username" => {Get: show_user, Post: save_user},
//!	"product" => {
//!		Get: show_all_products,
//!
//!		"json" => Get: send_all_product_data
//!		":id" => {
//!			Get: show_product,
//!			Post | Delete: edit_product,
//!
//!			"json" => Get: send_product_data
//!		}
//!	}
//!};
//!```
//!
//!#`routes!`
//!
//!The `routes!` macro generates a vector of routes, which can be used to create a `Router`.
//!It has the same syntax as `routes!`.
//!
//!```rust ignore
//!#![feature(phase)]
//!#[phase(plugin)]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!
//!let routes = routes!{
//!	"/" => Get: show_home,
//!	"home" => Get: show_home,
//!	"user/:username" => {Get: show_user, Post: save_user},
//!	"product" => {
//!		Get: show_all_products,
//!
//!		"json" => Get: send_all_product_data
//!		":id" => {
//!			Get: show_product,
//!			Post | Delete: edit_product,
//!
//!			"json" => Get: send_product_data
//!		}
//!	}
//!};
//!
//!let router = Router::from_routes(&routes);
//!```

extern crate syntax;
extern crate rustc;

use std::path::BytesContainer;

use syntax::{ast, codemap};
use syntax::ext::base::{
	ExtCtxt, MacResult, MacExpr,
	NormalTT, TTMacroExpander
};
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ExtParseUtils;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::parser;
use syntax::parse::parser::Parser;
use syntax::ptr::P;
//use syntax::print::pprust;

use rustc::plugin::Registry;

#[plugin_registrar]
#[doc(hidden)]
pub fn macro_registrar(reg: &mut Registry) {
	let expander = box expand_router as Box<TTMacroExpander>;
	reg.register_syntax_extension(token::intern("router"), NormalTT(expander, None));

	let expander = box expand_routes as Box<TTMacroExpander>;
	reg.register_syntax_extension(token::intern("routes"), NormalTT(expander, None));
}

fn expand_router<'cx>(cx: &'cx mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult + 'cx> {
	let router_ident = cx.ident_of("router");
	let insert_method = cx.ident_of("insert_item");

	let mut calls: Vec<P<ast::Stmt>> = vec!(
		cx.stmt_let(sp, true, router_ident, quote_expr!(&cx, ::rustful::Router::new()))
	);

	for (path, method, handler) in parse_routes(cx, tts).into_iter() {
		let path_expr = cx.parse_expr(format!("\"{}\"", path));
		let method_expr = cx.expr_path(method);
		calls.push(cx.stmt_expr(
			cx.expr_method_call(sp, cx.expr_ident(sp, router_ident), insert_method, vec!(method_expr, path_expr, handler))
		));
	}

	let block = cx.expr_block(cx.block(sp, calls, Some(cx.expr_ident(sp, router_ident))));

	MacExpr::new(block)
}

fn expand_routes<'cx>(cx: &'cx mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult + 'cx> {
	let routes = parse_routes(cx, tts).into_iter().map(|(path, method, handler)| {
		let path_expr = cx.parse_expr(format!("\"{}\"", path));
		let method_expr = cx.expr_path(method);
		mk_tup(sp, vec!(method_expr, path_expr, handler))
	}).collect();

	MacExpr::new(cx.expr_vec(sp, routes))
}

fn parse_routes(cx: &mut ExtCtxt, tts: &[ast::TokenTree]) -> Vec<(String, ast::Path, P<ast::Expr>)> {

	let mut parser = parse::new_parser_from_tts(
		cx.parse_sess(), cx.cfg(), tts.to_vec()
	);

	parse_subroutes("", cx, &mut parser)
}

fn parse_subroutes(base: &str, cx: &mut ExtCtxt, parser: &mut Parser) -> Vec<(String, ast::Path, P<ast::Expr>)> {
	let mut routes = Vec::new();

	while !parser.eat(&token::Eof) {
		match parser.parse_optional_str() {
			Some((ref s, _)) => {
				if !parser.eat(&token::FatArrow) {
					parser.expect(&token::FatArrow);
					break;
				}

				let mut new_base = base.to_string();
				match s.container_as_str() {
					Some(s) => {
						new_base.push_str(s.trim_chars('/'));
						new_base.push_str("/");
					},
					None => cx.span_err(parser.span, "invalid path")
				}

				if parser.eat(&token::Eof) {
					cx.span_err(parser.span, "unexpected end of routing tree");
				}

				if parser.eat(&token::OpenDelim(token::Brace)) {
					let subroutes = parse_subroutes(new_base.as_slice(), cx, parser);
					routes.push_all(subroutes.as_slice());

					if parser.eat(&token::CloseDelim(token::Brace)) {
						if !parser.eat(&token::Comma) {
							break;
						}
					} else {
						parser.expect_one_of(&[token::Comma, token::CloseDelim(token::Brace)], &[]);
					}
				} else {
					for (method, handler) in parse_handler(parser).into_iter() {
						routes.push((new_base.clone(), method, handler))
					}

					if !parser.eat(&token::Comma) {
						break;
					}
				}
			},
			None => {
				for (method, handler) in parse_handler(parser).into_iter() {
					routes.push((base.to_string(), method, handler))
				}

				if !parser.eat(&token::Comma) {
					break;
				}
			}
		}
	}

	routes
}

fn parse_handler(parser: &mut Parser) -> Vec<(ast::Path, P<ast::Expr>)> {
	let mut methods = Vec::new();

	loop {
		methods.push(parser.parse_path(parser::NoTypesAllowed).path);

		if parser.eat(&token::Colon) {
			break;
		}

		if !parser.eat(&token::BinOp(token::Or)) {
			parser.expect_one_of(&[token::Colon, token::BinOp(token::Or)], &[]);
		}
	}

	let handler = parser.parse_expr();

	methods.into_iter().map(|m| (m, handler.clone())).collect()
}

fn mk_tup(sp: codemap::Span, content: Vec<P<ast::Expr>>) -> P<ast::Expr> {
	dummy_expr(sp, ast::ExprTup(content))
}

fn dummy_expr(sp: codemap::Span, e: ast::Expr_) -> P<ast::Expr> {
	P(ast::Expr {
		id: ast::DUMMY_NODE_ID,
		node: e,
		span: sp,
	})
}

/**
A macro for assigning content types.

It takes a main type, a sub type and a parameter list. Instead of this:

```
response.headers.content_type = Some(MediaType {
	type_: String::from_str("text"),
	subtype: String::from_str("html"),
	parameters: vec!((String::from_str("charset"), String::from_str("UTF-8")))
});
```

it can be written like this:

```
response.headers.content_type = content_type!("text", "html", "charset": "UTF-8");
```

The `"charset": "UTF-8"` part defines the parameter list for the content type.
It may contain more than one parameter, or be omitted:

```
response.headers.content_type = content_type!("application", "octet-stream", "type": "image/gif", "padding": "4");
```

```
response.headers.content_type = content_type!("image", "png");
```
**/
#[macro_export]
macro_rules! content_type(
	($main_type:expr, $sub_type:expr) => ({
		Some(::http::headers::content_type::MediaType {
			type_: String::from_str($main_type),
			subtype: String::from_str($sub_type),
			parameters: Vec::new()
		})
	});

	($main_type:expr, $sub_type:expr, $($param:expr: $value:expr),+) => ({
		Some(::http::headers::content_type::MediaType {
			type_: String::from_str($main_type),
			subtype: String::from_str($sub_type),
			parameters: vec!( $( (String::from_str($param), String::from_str($value)) ),+ )
		})
	});
)

/**
A macro for callig `send` in response and aborting the handle function if it fails.

This macro will print an error to `stdout` and set the response status to `500 Internal Server Error` if the send fails.
The response status may or may not be written to the client, depending on if the connection is
broken or not, or if the headers have already been written.
**/
#[macro_export]
macro_rules! try_send(
	($response:ident, $content:expr) => (
		match $response.send($content) {
			Ok(v) => v,
			Err(::rustful::ResponseError::IoError(e)) => {
				println!("IO error: {}", e);
				$response.status = http::status::InternalServerError;
				return;
			},
			Err(::rustful::ResponseError::PluginError(e)) => {
				println!("plugin error: {}", e);
				$response.status = http::status::InternalServerError;
				return;
			}
		}
	);

	($response:ident, $content:expr while $what:expr) => (
		match $response.send($content) {
			Ok(v) => v,
			Err(::rustful::ResponseError::IoError(e)) => {
				println!("IO error while {}: {}", $what, e);
				$response.status = http::status::InternalServerError;
				return;
			},
			Err(::rustful::ResponseError::PluginError(e)) => {
				println!("plugin error while {}: {}", $what, e);
				$response.status = http::status::InternalServerError;
				return;
			}
		}
	)
)<commit_msg>Parser::parse_optional_str returns a tuple with three elements<commit_after>#![crate_name = "rustful_macros"]

#![comment = "Macros for rustful"]
#![license = "MIT"]
#![crate_type = "dylib"]

#![doc(html_root_url = "http://ogeon.github.io/rustful/doc/")]

#![feature(macro_rules, plugin_registrar, quote, phase)]

//!This crate provides some helpful macros for rustful, including `router!` and `routes!`.
//!
//!#`router!`
//!The `router!` macro generates a `Router` containing the provided handlers and routing tree.
//!This can be useful to lower the risk of typing errors, among other things.
//!
//!##Example 1
//!
//!```rust ignore
//!#![feature(phase)]
//!#[phase(plugin)]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!
//!let router = router!{
//!	"/about" => Get: about_us,
//!	"/user/:user" => Get: show_user,
//!	"/product/:name" => Get: show_product,
//!	"/*" => Get: show_error,
//!	"/" => Get: show_welcome
//!};
//!```
//!
//!##Example 2
//!
//!```rust ignore
//!#![feature(phase)]
//!#[phase(plugin)]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!
//!let router = router!{
//!	"/" => Get: show_home,
//!	"home" => Get: show_home,
//!	"user/:username" => {Get: show_user, Post: save_user},
//!	"product" => {
//!		Get: show_all_products,
//!
//!		"json" => Get: send_all_product_data
//!		":id" => {
//!			Get: show_product,
//!			Post | Delete: edit_product,
//!
//!			"json" => Get: send_product_data
//!		}
//!	}
//!};
//!```
//!
//!#`routes!`
//!
//!The `routes!` macro generates a vector of routes, which can be used to create a `Router`.
//!It has the same syntax as `routes!`.
//!
//!```rust ignore
//!#![feature(phase)]
//!#[phase(plugin)]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!
//!let routes = routes!{
//!	"/" => Get: show_home,
//!	"home" => Get: show_home,
//!	"user/:username" => {Get: show_user, Post: save_user},
//!	"product" => {
//!		Get: show_all_products,
//!
//!		"json" => Get: send_all_product_data
//!		":id" => {
//!			Get: show_product,
//!			Post | Delete: edit_product,
//!
//!			"json" => Get: send_product_data
//!		}
//!	}
//!};
//!
//!let router = Router::from_routes(&routes);
//!```

extern crate syntax;
extern crate rustc;

use std::path::BytesContainer;

use syntax::{ast, codemap};
use syntax::ext::base::{
	ExtCtxt, MacResult, MacExpr,
	NormalTT, TTMacroExpander
};
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ExtParseUtils;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::parser;
use syntax::parse::parser::Parser;
use syntax::ptr::P;
//use syntax::print::pprust;

use rustc::plugin::Registry;

#[plugin_registrar]
#[doc(hidden)]
pub fn macro_registrar(reg: &mut Registry) {
	let expander = box expand_router as Box<TTMacroExpander>;
	reg.register_syntax_extension(token::intern("router"), NormalTT(expander, None));

	let expander = box expand_routes as Box<TTMacroExpander>;
	reg.register_syntax_extension(token::intern("routes"), NormalTT(expander, None));
}

fn expand_router<'cx>(cx: &'cx mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult + 'cx> {
	let router_ident = cx.ident_of("router");
	let insert_method = cx.ident_of("insert_item");

	let mut calls: Vec<P<ast::Stmt>> = vec!(
		cx.stmt_let(sp, true, router_ident, quote_expr!(&cx, ::rustful::Router::new()))
	);

	for (path, method, handler) in parse_routes(cx, tts).into_iter() {
		let path_expr = cx.parse_expr(format!("\"{}\"", path));
		let method_expr = cx.expr_path(method);
		calls.push(cx.stmt_expr(
			cx.expr_method_call(sp, cx.expr_ident(sp, router_ident), insert_method, vec!(method_expr, path_expr, handler))
		));
	}

	let block = cx.expr_block(cx.block(sp, calls, Some(cx.expr_ident(sp, router_ident))));

	MacExpr::new(block)
}

fn expand_routes<'cx>(cx: &'cx mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult + 'cx> {
	let routes = parse_routes(cx, tts).into_iter().map(|(path, method, handler)| {
		let path_expr = cx.parse_expr(format!("\"{}\"", path));
		let method_expr = cx.expr_path(method);
		mk_tup(sp, vec!(method_expr, path_expr, handler))
	}).collect();

	MacExpr::new(cx.expr_vec(sp, routes))
}

fn parse_routes(cx: &mut ExtCtxt, tts: &[ast::TokenTree]) -> Vec<(String, ast::Path, P<ast::Expr>)> {

	let mut parser = parse::new_parser_from_tts(
		cx.parse_sess(), cx.cfg(), tts.to_vec()
	);

	parse_subroutes("", cx, &mut parser)
}

fn parse_subroutes(base: &str, cx: &mut ExtCtxt, parser: &mut Parser) -> Vec<(String, ast::Path, P<ast::Expr>)> {
	let mut routes = Vec::new();

	while !parser.eat(&token::Eof) {
		match parser.parse_optional_str() {
			Some((ref s, _, _)) => {
				if !parser.eat(&token::FatArrow) {
					parser.expect(&token::FatArrow);
					break;
				}

				let mut new_base = base.to_string();
				match s.container_as_str() {
					Some(s) => {
						new_base.push_str(s.trim_chars('/'));
						new_base.push_str("/");
					},
					None => cx.span_err(parser.span, "invalid path")
				}

				if parser.eat(&token::Eof) {
					cx.span_err(parser.span, "unexpected end of routing tree");
				}

				if parser.eat(&token::OpenDelim(token::Brace)) {
					let subroutes = parse_subroutes(new_base.as_slice(), cx, parser);
					routes.push_all(subroutes.as_slice());

					if parser.eat(&token::CloseDelim(token::Brace)) {
						if !parser.eat(&token::Comma) {
							break;
						}
					} else {
						parser.expect_one_of(&[token::Comma, token::CloseDelim(token::Brace)], &[]);
					}
				} else {
					for (method, handler) in parse_handler(parser).into_iter() {
						routes.push((new_base.clone(), method, handler))
					}

					if !parser.eat(&token::Comma) {
						break;
					}
				}
			},
			None => {
				for (method, handler) in parse_handler(parser).into_iter() {
					routes.push((base.to_string(), method, handler))
				}

				if !parser.eat(&token::Comma) {
					break;
				}
			}
		}
	}

	routes
}

fn parse_handler(parser: &mut Parser) -> Vec<(ast::Path, P<ast::Expr>)> {
	let mut methods = Vec::new();

	loop {
		methods.push(parser.parse_path(parser::NoTypesAllowed).path);

		if parser.eat(&token::Colon) {
			break;
		}

		if !parser.eat(&token::BinOp(token::Or)) {
			parser.expect_one_of(&[token::Colon, token::BinOp(token::Or)], &[]);
		}
	}

	let handler = parser.parse_expr();

	methods.into_iter().map(|m| (m, handler.clone())).collect()
}

fn mk_tup(sp: codemap::Span, content: Vec<P<ast::Expr>>) -> P<ast::Expr> {
	dummy_expr(sp, ast::ExprTup(content))
}

fn dummy_expr(sp: codemap::Span, e: ast::Expr_) -> P<ast::Expr> {
	P(ast::Expr {
		id: ast::DUMMY_NODE_ID,
		node: e,
		span: sp,
	})
}

/**
A macro for assigning content types.

It takes a main type, a sub type and a parameter list. Instead of this:

```
response.headers.content_type = Some(MediaType {
	type_: String::from_str("text"),
	subtype: String::from_str("html"),
	parameters: vec!((String::from_str("charset"), String::from_str("UTF-8")))
});
```

it can be written like this:

```
response.headers.content_type = content_type!("text", "html", "charset": "UTF-8");
```

The `"charset": "UTF-8"` part defines the parameter list for the content type.
It may contain more than one parameter, or be omitted:

```
response.headers.content_type = content_type!("application", "octet-stream", "type": "image/gif", "padding": "4");
```

```
response.headers.content_type = content_type!("image", "png");
```
**/
#[macro_export]
macro_rules! content_type(
	($main_type:expr, $sub_type:expr) => ({
		Some(::http::headers::content_type::MediaType {
			type_: String::from_str($main_type),
			subtype: String::from_str($sub_type),
			parameters: Vec::new()
		})
	});

	($main_type:expr, $sub_type:expr, $($param:expr: $value:expr),+) => ({
		Some(::http::headers::content_type::MediaType {
			type_: String::from_str($main_type),
			subtype: String::from_str($sub_type),
			parameters: vec!( $( (String::from_str($param), String::from_str($value)) ),+ )
		})
	});
)

/**
A macro for callig `send` in response and aborting the handle function if it fails.

This macro will print an error to `stdout` and set the response status to `500 Internal Server Error` if the send fails.
The response status may or may not be written to the client, depending on if the connection is
broken or not, or if the headers have already been written.
**/
#[macro_export]
macro_rules! try_send(
	($response:ident, $content:expr) => (
		match $response.send($content) {
			Ok(v) => v,
			Err(::rustful::ResponseError::IoError(e)) => {
				println!("IO error: {}", e);
				$response.status = http::status::InternalServerError;
				return;
			},
			Err(::rustful::ResponseError::PluginError(e)) => {
				println!("plugin error: {}", e);
				$response.status = http::status::InternalServerError;
				return;
			}
		}
	);

	($response:ident, $content:expr while $what:expr) => (
		match $response.send($content) {
			Ok(v) => v,
			Err(::rustful::ResponseError::IoError(e)) => {
				println!("IO error while {}: {}", $what, e);
				$response.status = http::status::InternalServerError;
				return;
			},
			Err(::rustful::ResponseError::PluginError(e)) => {
				println!("plugin error while {}: {}", $what, e);
				$response.status = http::status::InternalServerError;
				return;
			}
		}
	)
)<|endoftext|>


<commit_before>#[link(name="rm", vers="1.0.0", author="Arcterus")];

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Arcterus <arcterus@mail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern mod extra;

use std::os;
use std::io::{stderr,fs,io_error};
use extra::getopts::groups;

enum InteractiveMode {
    InteractiveNone,
    InteractiveOnce,
    InteractiveAlways
}

fn main() {
    let args = os::args();
    let program = args[0].clone();

    // TODO: make getopts support -R in addition to -r
    let opts = ~[
        groups::optflag("f", "force", "ignore nonexistent files and arguments, never prompt"),
        groups::optflag("i", "", "prompt before every removal"),
        groups::optflag("I", "", "prompt once before removing more than three files, or when removing recursively.  Less intrusive than -i, while still giving some protection against most mistakes"),
        groups::optflagopt("", "interactive", "prompt according to WHEN: never, once (-I), or always (-i).  Without WHEN, prompts always", "WHEN"),
        groups::optflag("", "one-file-system", "when removing a hierarchy recursively, skip any directory that is on a file system different from that of the corresponding command line argument"),
        groups::optflag("", "no-preserve-root", "do not treat '/' specially"),
        groups::optflag("", "preserve-root", "do not remove '/' (default)"),
        groups::optflag("r", "recursive", "remove directories and their contents recursively"),
        groups::optflag("d", "dir", "remove empty directories"),
        groups::optflag("v", "verbose", "explain what is being done"),
        groups::optflag("", "help", "display this help and exit"),
        groups::optflag("", "version", "output version information and exit")
    ];
    let matches = match groups::getopts(args.tail(), opts) {
        Ok(m) => m,
        Err(f) => {
            writeln!(&mut stderr() as &mut Writer,
                     "{}", f.to_err_msg());
            os::set_exit_status(1);
            return
        }
    };
    if matches.opt_present("help") {
        println("rm 1.0.0");
        println("");
        println("Usage:");
        println!("  {0:s} [OPTION]... [FILE]...", program);
        println("");
        print(groups::usage("Remove (unlink) the FILE(s).", opts));
        println("");
        println("By default, rm does not remove directories.  Use the --recursive (-r)");
        println("option to remove each listed directory, too, along with all of its contents");
        println("");
        println("To remove a file whose name starts with a '-', for example '-foo',");
        println("use one of these commands:");
        println("rm -- -foo");
        println("");
        println("rm ./-foo");
        println("");
        println("Note that if you use rm to remove a file, it might be possible to recover");
        println("some of its contents, given sufficient expertise and/or time.  For greater");
        println("assurance that the contents are truly unrecoverable, consider using shred.");
    } else if matches.opt_present("version") {
        println("rm 1.0.0");
    } else {
        let force = matches.opt_present("force");
        let interactive =
            if matches.opt_present("i") {
                InteractiveAlways
            } else if matches.opt_present("I") {
                InteractiveOnce
            } else if matches.opt_present("interactive") {
                match matches.opt_str("interactive").unwrap() {
                    ~"none" => InteractiveNone,
                    ~"once" => InteractiveOnce,
                    ~"always" => InteractiveAlways,
                    val => {
                        writeln!(&mut stderr() as &mut Writer,
                                 "Invalid argument to interactive ({})", val);
                        os::set_exit_status(1);
                        return
                    }
                }
            } else {
                InteractiveNone
            };
        let one_fs = matches.opt_present("one-file-system");
        let preserve_root = !matches.opt_present("no-preserve-root");
        let recursive = matches.opt_present("recursive");
        let dir = matches.opt_present("dir");
        let verbose = matches.opt_present("verbose");
        remove_files(matches.free, force, interactive, one_fs, preserve_root,
                     recursive, dir, verbose);
    }
}

// TODO: implement one-file-system and interactive
fn remove_files(files: &[~str], force: bool, interactive: InteractiveMode, one_fs: bool, preserve_root: bool, recursive: bool, dir: bool, verbose: bool) {
    for filename in files.iter() {
        let file = Path::new(filename.to_owned());
        if file.exists() {
            if file.is_dir() {
                if recursive && (*filename != ~"/" || !preserve_root) {
                    remove_files(fs::walk_dir(&file).map(|x| x.as_str().unwrap().to_owned()).to_owned_vec(), force, interactive, one_fs, preserve_root, recursive, dir, verbose);
                    io_error::cond.trap(|_| {
                        writeln!(&mut stderr() as &mut Writer,
                                 "Could not remove directory: '{}'", *filename);
                        os::set_exit_status(1);
                    }).inside(|| {
                        fs::rmdir(&file);
                        println!("Removed '{}'", *filename);
                    });
                } else if dir && (*filename != ~"/" || !preserve_root) {
                    io_error::cond.trap(|_| {
                        writeln!(&mut stderr() as &mut Writer,
                                 "Could not remove directory '{}'", *filename);
                        os::set_exit_status(1);
                    }).inside(|| {
                        fs::rmdir(&file);
                        println!("Removed '{}'", *filename);
                    });
                } else {
                    if recursive {
                        writeln!(&mut stderr() as &mut Writer,
                                 "Could not remove directory '{}'",
                                 *filename);
                    } else {
                        writeln!(&mut stderr() as &mut Writer,
                                 "Could not remove directory '{}' (did you mean to pass '-r'?)",
                                 *filename);
                        os::set_exit_status(1);
                    }
                }
            } else {
                io_error::cond.trap(|_| {
                    writeln!(&mut stderr() as &mut Writer,
                             "Could not remove file: '{}'", *filename);
                    os::set_exit_status(1);
                }).inside(|| {
                    fs::unlink(&file);
                    println!("Removed '{}'", *filename);
                });
            }
        } else if !force {
            writeln!(&mut stderr() as &mut Writer,
                     "No such file or directory '{}'", *filename);
            os::set_exit_status(1);
        }
    }
}

<commit_msg>Implemented --interactive for rm<commit_after>#[link(name="rm", vers="1.0.0", author="Arcterus")];

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Arcterus <arcterus@mail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern mod extra;

use std::os;
use std::io::{stdin,stderr,stdio,fs,buffered,io_error};
use extra::getopts::groups;

enum InteractiveMode {
    InteractiveNone,
    InteractiveOnce,
    InteractiveAlways
}

impl Eq for InteractiveMode {
    fn eq(&self, other: &InteractiveMode) -> bool {
        match (*self, *other) {
            (InteractiveNone, InteractiveNone) |
            (InteractiveOnce, InteractiveOnce) |
            (InteractiveAlways, InteractiveAlways) => true,
            _ => false
        }
    }

    fn ne(&self, other: &InteractiveMode) -> bool {
        !self.eq(other)
    }
}

fn main() {
    let args = os::args();
    let program = args[0].clone();

    // TODO: make getopts support -R in addition to -r
    let opts = ~[
        groups::optflag("f", "force", "ignore nonexistent files and arguments, never prompt"),
        groups::optflag("i", "", "prompt before every removal"),
        groups::optflag("I", "", "prompt once before removing more than three files, or when removing recursively.  Less intrusive than -i, while still giving some protection against most mistakes"),
        groups::optflagopt("", "interactive", "prompt according to WHEN: never, once (-I), or always (-i).  Without WHEN, prompts always", "WHEN"),
        groups::optflag("", "one-file-system", "when removing a hierarchy recursively, skip any directory that is on a file system different from that of the corresponding command line argument"),
        groups::optflag("", "no-preserve-root", "do not treat '/' specially"),
        groups::optflag("", "preserve-root", "do not remove '/' (default)"),
        groups::optflag("r", "recursive", "remove directories and their contents recursively"),
        groups::optflag("d", "dir", "remove empty directories"),
        groups::optflag("v", "verbose", "explain what is being done"),
        groups::optflag("", "help", "display this help and exit"),
        groups::optflag("", "version", "output version information and exit")
    ];
    let matches = match groups::getopts(args.tail(), opts) {
        Ok(m) => m,
        Err(f) => {
            writeln!(&mut stderr() as &mut Writer,
                     "{}", f.to_err_msg());
            os::set_exit_status(1);
            return
        }
    };
    if matches.opt_present("help") {
        println("rm 1.0.0");
        println("");
        println("Usage:");
        println!("  {0:s} [OPTION]... [FILE]...", program);
        println("");
        print(groups::usage("Remove (unlink) the FILE(s).", opts));
        println("");
        println("By default, rm does not remove directories.  Use the --recursive (-r)");
        println("option to remove each listed directory, too, along with all of its contents");
        println("");
        println("To remove a file whose name starts with a '-', for example '-foo',");
        println("use one of these commands:");
        println("rm -- -foo");
        println("");
        println("rm ./-foo");
        println("");
        println("Note that if you use rm to remove a file, it might be possible to recover");
        println("some of its contents, given sufficient expertise and/or time.  For greater");
        println("assurance that the contents are truly unrecoverable, consider using shred.");
    } else if matches.opt_present("version") {
        println("rm 1.0.0");
    } else if matches.free.is_empty() {
        writeln!(&mut stderr() as &mut Writer, "Missing an argument");
        writeln!(&mut stderr() as &mut Writer,
                 "For help, try '{0:s} --help'", program);
        os::set_exit_status(1);
    } else {
        let force = matches.opt_present("force");
        let interactive =
            if matches.opt_present("i") {
                InteractiveAlways
            } else if matches.opt_present("I") {
                InteractiveOnce
            } else if matches.opt_present("interactive") {
                match matches.opt_str("interactive").unwrap() {
                    ~"none" => InteractiveNone,
                    ~"once" => InteractiveOnce,
                    ~"always" => InteractiveAlways,
                    val => {
                        writeln!(&mut stderr() as &mut Writer,
                                 "Invalid argument to interactive ({})", val);
                        os::set_exit_status(1);
                        return
                    }
                }
            } else {
                InteractiveNone
            };
        let one_fs = matches.opt_present("one-file-system");
        let preserve_root = !matches.opt_present("no-preserve-root");
        let recursive = matches.opt_present("recursive");
        let dir = matches.opt_present("dir");
        let verbose = matches.opt_present("verbose");
        if interactive == InteractiveOnce && (recursive || matches.free.len() > 3) {
            let msg =
                if recursive {
                    "Remove all arguments recursively? "
                } else {
                    "Remove all arguments? "
                };
            if !prompt(msg) {
                return;
            }
        }
        remove(matches.free, force, interactive, one_fs, preserve_root,
               recursive, dir, verbose);
    }
}

// TODO: implement one-file-system
fn remove(files: &[~str], force: bool, interactive: InteractiveMode, one_fs: bool, preserve_root: bool, recursive: bool, dir: bool, verbose: bool) {
    for filename in files.iter() {
        let file = Path::new(filename.to_owned());
        if file.exists() {
            if file.is_dir() {
                if recursive && (*filename != ~"/" || !preserve_root) {
                    remove(fs::walk_dir(&file).map(|x| x.as_str().unwrap().to_owned()).to_owned_vec(), force, interactive, one_fs, preserve_root, recursive, dir, verbose);
                    remove_dir(&file, *filename, interactive, verbose);
                } else if dir && (*filename != ~"/" || !preserve_root) {
                    remove_dir(&file, *filename, interactive, verbose);
                } else {
                    if recursive {
                        writeln!(&mut stderr() as &mut Writer,
                                 "Could not remove directory '{}'",
                                 *filename);
                    } else {
                        writeln!(&mut stderr() as &mut Writer,
                                 "Could not remove directory '{}' (did you mean to pass '-r'?)",
                                 *filename);
                    }
                    os::set_exit_status(1);
                }
            } else {
                remove_file(&file, *filename, interactive, verbose);
            }
        } else if !force {
            writeln!(&mut stderr() as &mut Writer,
                     "No such file or directory '{}'", *filename);
            os::set_exit_status(1);
        }
    }
}

fn remove_dir(path: &Path, name: &str, interactive: InteractiveMode, verbose: bool) {
    let response =
        if interactive == InteractiveAlways {
            prompt_file(path, name)
        } else {
            true
        };
    if response {
        io_error::cond.trap(|_| {
            writeln!(&mut stderr() as &mut Writer,
                     "Could not remove directory '{}'", name);
            os::set_exit_status(1);
        }).inside(|| {
            fs::rmdir(path);
            if verbose {
                println!("Removed '{}'", name);
            }
        });
    }
}

fn remove_file(path: &Path, name: &str, interactive: InteractiveMode, verbose: bool) {
    let response =
        if interactive == InteractiveAlways {
            prompt_file(path, name)
        } else {
            true
        };
    if response {
        io_error::cond.trap(|_| {
            writeln!(&mut stderr() as &mut Writer,
                     "Could not remove file '{}'", name);
            os::set_exit_status(1);
        }).inside(|| {
            fs::unlink(path);
            if verbose {
                println!("Removed '{}'", name);
            }
        });
    }
}

fn prompt_file(path: &Path, name: &str) -> bool {
    if path.is_dir() {
        prompt(format!("Remove directory '{}'? ", name))
    } else {
        prompt(format!("Remove file '{}'? ", name))
    }
}

fn prompt(msg: &str) -> bool {
    print(msg);
    read_prompt()
}

fn read_prompt() -> bool {
    stdio::flush();
    match buffered::BufferedReader::new(stdin()).read_line() {
        Some(line) => {
            match line.char_at(0) {
                'y' | 'Y' => true,
                'n' | 'N' => false,
                _ => {
                    print("Please enter either Y or N: ");
                    read_prompt()
                }
            }
        }
        None => true
    }
}

<|endoftext|>


<commit_before>#![feature(test)]

extern crate test;
extern crate syntect;
use test::Bencher;

use syntect::parsing::{SyntaxSet, SyntaxDefinition};
use syntect::highlighting::{ThemeSet, Theme};
use syntect::easy::HighlightLines;
use std::fs::File;
use std::io::Read;

fn do_highlight(s: &str, syntax: &SyntaxDefinition, theme: &Theme) {
    let mut h = HighlightLines::new(syntax, theme);
    for line in s.lines() {
        let regions = h.highlight(line);
        test::black_box(&regions);
    }
}

fn highlight_file(b: &mut Bencher, path_s: &str) {
    // don't load from dump so we don't count lazy regex compilation time
    let ps = SyntaxSet::load_defaults_nonewlines();
    let ts = ThemeSet::load_defaults();

    let syntax = ps.find_syntax_for_file(path_s).unwrap().unwrap();
    let mut f = File::open(path_s).unwrap();
    let mut s = String::new();
    f.read_to_string(&mut s).unwrap();

    do_highlight(&s, syntax, &ts.themes["base16-ocean.dark"]);
    b.iter(|| {
        do_highlight(&s, syntax, &ts.themes["base16-ocean.dark"]);
    });
}

#[bench]
fn bench_highlighting_nesting(b: &mut Bencher) {
    highlight_file(b, "testdata/highlight_test.erb");
}

#[bench]
fn bench_highlighting_xml(b: &mut Bencher) {
    highlight_file(b, "testdata/InspiredGitHub.tmtheme/InspiredGitHub.tmTheme");
}

#[bench]
fn bench_highlighting_yaml(b: &mut Bencher) {
    highlight_file(b, "testdata/Packages/Ruby/Ruby.sublime-syntax");
}

#[bench]
fn bench_highlighting_jquery(b: &mut Bencher) {
    highlight_file(b, "testdata/jquery.js");
}

#[bench]
fn bench_highlighting_rustc(b: &mut Bencher) {
    highlight_file(b, "testdata/parser.rs");
}

#[bench]
fn bench_highlighting_scope(b: &mut Bencher) {
    highlight_file(b, "src/parsing/scope.rs");
}
<commit_msg>Scope stack matching benchmark<commit_after>#![feature(test)]

extern crate test;
extern crate syntect;
use test::Bencher;

use syntect::parsing::{SyntaxSet, SyntaxDefinition, ScopeStack};
use syntect::highlighting::{ThemeSet, Theme};
use syntect::easy::HighlightLines;
use std::str::FromStr;
use std::fs::File;
use std::io::Read;

fn do_highlight(s: &str, syntax: &SyntaxDefinition, theme: &Theme) {
    let mut h = HighlightLines::new(syntax, theme);
    for line in s.lines() {
        let regions = h.highlight(line);
        test::black_box(&regions);
    }
}

fn highlight_file(b: &mut Bencher, path_s: &str) {
    // don't load from dump so we don't count lazy regex compilation time
    let ps = SyntaxSet::load_defaults_nonewlines();
    let ts = ThemeSet::load_defaults();

    let syntax = ps.find_syntax_for_file(path_s).unwrap().unwrap();
    let mut f = File::open(path_s).unwrap();
    let mut s = String::new();
    f.read_to_string(&mut s).unwrap();

    do_highlight(&s, syntax, &ts.themes["base16-ocean.dark"]);
    b.iter(|| {
        do_highlight(&s, syntax, &ts.themes["base16-ocean.dark"]);
    });
}

#[bench]
fn bench_highlighting_nesting(b: &mut Bencher) {
    highlight_file(b, "testdata/highlight_test.erb");
}

#[bench]
fn bench_highlighting_xml(b: &mut Bencher) {
    highlight_file(b, "testdata/InspiredGitHub.tmtheme/InspiredGitHub.tmTheme");
}

#[bench]
fn bench_highlighting_yaml(b: &mut Bencher) {
    highlight_file(b, "testdata/Packages/Ruby/Ruby.sublime-syntax");
}

#[bench]
fn bench_highlighting_jquery(b: &mut Bencher) {
    highlight_file(b, "testdata/jquery.js");
}

#[bench]
fn bench_highlighting_rustc(b: &mut Bencher) {
    highlight_file(b, "testdata/parser.rs");
}

#[bench]
fn bench_highlighting_scope(b: &mut Bencher) {
    highlight_file(b, "src/parsing/scope.rs");
}

#[bench]
fn bench_stack_matching(b: &mut Bencher) {
    let s = "source.js meta.group.js meta.group.js meta.block.js meta.function-call.method.js meta.group.js meta.object-literal.js meta.block.js meta.function-call.method.js meta.group.js variable.other.readwrite.js";
    let stack = ScopeStack::from_str(s).unwrap();
    let selector = ScopeStack::from_str("source meta.function-call.method").unwrap();
    b.iter(|| {
        let res = selector.does_match(stack.as_slice());
        test::black_box(res);
    });
}
<|endoftext|>