<commit_before>//! This module contains functions for editing syntax trees. As the trees are
//! immutable, all function here return a fresh copy of the tree, instead of
//! doing an in-place modification.
use std::{iter, ops::RangeInclusive};

use arrayvec::ArrayVec;
use rustc_hash::FxHashMap;

use crate::{
    algo,
    ast::{
        self,
        make::{self, tokens},
        AstNode, TypeBoundsOwner,
    },
    AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, SyntaxKind,
    SyntaxKind::{ATTR, COMMENT, WHITESPACE},
    SyntaxNode, SyntaxToken, T,
};

impl ast::BinExpr {
    #[must_use]
    pub fn replace_op(&self, op: SyntaxKind) -> Option<ast::BinExpr> {
        let op_node: SyntaxElement = self.op_details()?.0.into();
        let to_insert: Option<SyntaxElement> = Some(make::token(op).into());
        Some(replace_children(self, single_node(op_node), to_insert))
    }
}

impl ast::FnDef {
    #[must_use]
    pub fn with_body(&self, body: ast::Block) -> ast::FnDef {
        let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new();
        let old_body_or_semi: SyntaxElement = if let Some(old_body) = self.body() {
            old_body.syntax().clone().into()
        } else if let Some(semi) = self.semicolon_token() {
            to_insert.push(make::tokens::single_space().into());
            semi.into()
        } else {
            to_insert.push(make::tokens::single_space().into());
            to_insert.push(body.syntax().clone().into());
            return insert_children(self, InsertPosition::Last, to_insert);
        };
        to_insert.push(body.syntax().clone().into());
        replace_children(self, single_node(old_body_or_semi), to_insert)
    }
}

impl ast::ItemList {
    #[must_use]
    pub fn append_items(&self, items: impl Iterator<Item = ast::ImplItem>) -> ast::ItemList {
        let mut res = self.clone();
        if !self.syntax().text().contains_char('\n') {
            res = res.make_multiline();
        }
        items.for_each(|it| res = res.append_item(it));
        res
    }

    #[must_use]
    pub fn append_item(&self, item: ast::ImplItem) -> ast::ItemList {
        let (indent, position) = match self.impl_items().last() {
            Some(it) => (
                leading_indent(it.syntax()).unwrap_or_default().to_string(),
                InsertPosition::After(it.syntax().clone().into()),
            ),
            None => match self.l_curly() {
                Some(it) => (
                    "    ".to_string() + &leading_indent(self.syntax()).unwrap_or_default(),
                    InsertPosition::After(it),
                ),
                None => return self.clone(),
            },
        };
        let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
        let to_insert: ArrayVec<[SyntaxElement; 2]> =
            [ws.ws().into(), item.syntax().clone().into()].into();
        insert_children(self, position, to_insert)
    }

    fn l_curly(&self) -> Option<SyntaxElement> {
        self.syntax().children_with_tokens().find(|it| it.kind() == T!['{'])
    }

    fn make_multiline(&self) -> ast::ItemList {
        let l_curly = match self.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) {
            Some(it) => it,
            None => return self.clone(),
        };
        let sibling = match l_curly.next_sibling_or_token() {
            Some(it) => it,
            None => return self.clone(),
        };
        let existing_ws = match sibling.as_token() {
            None => None,
            Some(tok) if tok.kind() != WHITESPACE => None,
            Some(ws) => {
                if ws.text().contains('\n') {
                    return self.clone();
                }
                Some(ws.clone())
            }
        };

        let indent = leading_indent(self.syntax()).unwrap_or_default();
        let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
        let to_insert = iter::once(ws.ws().into());
        match existing_ws {
            None => insert_children(self, InsertPosition::After(l_curly), to_insert),
            Some(ws) => replace_children(self, single_node(ws), to_insert),
        }
    }
}

impl ast::RecordFieldList {
    #[must_use]
    pub fn append_field(&self, field: &ast::RecordField) -> ast::RecordFieldList {
        self.insert_field(InsertPosition::Last, field)
    }

    #[must_use]
    pub fn insert_field(
        &self,
        position: InsertPosition<&'_ ast::RecordField>,
        field: &ast::RecordField,
    ) -> ast::RecordFieldList {
        let is_multiline = self.syntax().text().contains_char('\n');
        let ws;
        let space = if is_multiline {
            ws = tokens::WsBuilder::new(&format!(
                "\n{}    ",
                leading_indent(self.syntax()).unwrap_or_default()
            ));
            ws.ws()
        } else {
            tokens::single_space()
        };

        let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new();
        to_insert.push(space.into());
        to_insert.push(field.syntax().clone().into());
        to_insert.push(make::token(T![,]).into());

        macro_rules! after_l_curly {
            () => {{
                let anchor = match self.l_curly() {
                    Some(it) => it,
                    None => return self.clone(),
                };
                InsertPosition::After(anchor)
            }};
        }

        macro_rules! after_field {
            ($anchor:expr) => {
                if let Some(comma) = $anchor
                    .syntax()
                    .siblings_with_tokens(Direction::Next)
                    .find(|it| it.kind() == T![,])
                {
                    InsertPosition::After(comma)
                } else {
                    to_insert.insert(0, make::token(T![,]).into());
                    InsertPosition::After($anchor.syntax().clone().into())
                }
            };
        };

        let position = match position {
            InsertPosition::First => after_l_curly!(),
            InsertPosition::Last => {
                if !is_multiline {
                    // don't insert comma before curly
                    to_insert.pop();
                }
                match self.fields().last() {
                    Some(it) => after_field!(it),
                    None => after_l_curly!(),
                }
            }
            InsertPosition::Before(anchor) => {
                InsertPosition::Before(anchor.syntax().clone().into())
            }
            InsertPosition::After(anchor) => after_field!(anchor),
        };

        insert_children(self, position, to_insert)
    }

    fn l_curly(&self) -> Option<SyntaxElement> {
        self.syntax().children_with_tokens().find(|it| it.kind() == T!['{'])
    }
}

impl ast::TypeParam {
    #[must_use]
    pub fn remove_bounds(&self) -> ast::TypeParam {
        let colon = match self.colon_token() {
            Some(it) => it,
            None => return self.clone(),
        };
        let end = match self.type_bound_list() {
            Some(it) => it.syntax().clone().into(),
            None => colon.clone().into(),
        };
        replace_children(self, colon.into()..=end, iter::empty())
    }
}

impl ast::Path {
    #[must_use]
    pub fn with_segment(&self, segment: ast::PathSegment) -> ast::Path {
        if let Some(old) = self.segment() {
            return replace_children(
                self,
                single_node(old.syntax().clone()),
                iter::once(segment.syntax().clone().into()),
            );
        }
        self.clone()
    }
}

impl ast::PathSegment {
    #[must_use]
    pub fn with_type_args(&self, type_args: ast::TypeArgList) -> ast::PathSegment {
        self._with_type_args(type_args, false)
    }

    #[must_use]
    pub fn with_turbo_fish(&self, type_args: ast::TypeArgList) -> ast::PathSegment {
        self._with_type_args(type_args, true)
    }

    fn _with_type_args(&self, type_args: ast::TypeArgList, turbo: bool) -> ast::PathSegment {
        if let Some(old) = self.type_arg_list() {
            return replace_children(
                self,
                single_node(old.syntax().clone()),
                iter::once(type_args.syntax().clone().into()),
            );
        }
        let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new();
        if turbo {
            to_insert.push(make::token(T![::]).into());
        }
        to_insert.push(type_args.syntax().clone().into());
        insert_children(self, InsertPosition::Last, to_insert)
    }
}

impl ast::UseItem {
    #[must_use]
    pub fn with_use_tree(&self, use_tree: ast::UseTree) -> ast::UseItem {
        if let Some(old) = self.use_tree() {
            return replace_descendants(self, iter::once((old, use_tree)));
        }
        self.clone()
    }
}

impl ast::UseTree {
    #[must_use]
    pub fn with_path(&self, path: ast::Path) -> ast::UseTree {
        if let Some(old) = self.path() {
            return replace_descendants(self, iter::once((old, path)));
        }
        self.clone()
    }

    #[must_use]
    pub fn with_use_tree_list(&self, use_tree_list: ast::UseTreeList) -> ast::UseTree {
        if let Some(old) = self.use_tree_list() {
            return replace_descendants(self, iter::once((old, use_tree_list)));
        }
        self.clone()
    }
}

#[must_use]
pub fn strip_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N {
    N::cast(strip_attrs_and_docs_inner(node.syntax().clone())).unwrap()
}

fn strip_attrs_and_docs_inner(mut node: SyntaxNode) -> SyntaxNode {
    while let Some(start) =
        node.children_with_tokens().find(|it| it.kind() == ATTR || it.kind() == COMMENT)
    {
        let end = match &start.next_sibling_or_token() {
            Some(el) if el.kind() == WHITESPACE => el.clone(),
            Some(_) | None => start.clone(),
        };
        node = algo::replace_children(&node, start..=end, &mut iter::empty());
    }
    node
}

#[must_use]
pub fn replace_descendants<N: AstNode, D: AstNode>(
    parent: &N,
    replacement_map: impl IntoIterator<Item = (D, D)>,
) -> N {
    let map = replacement_map
        .into_iter()
        .map(|(from, to)| (from.syntax().clone().into(), to.syntax().clone().into()))
        .collect::<FxHashMap<SyntaxElement, _>>();
    let new_syntax = algo::replace_descendants(parent.syntax(), |n| map.get(n).cloned());
    N::cast(new_syntax).unwrap()
}

#[derive(Debug, Clone, Copy)]
pub struct IndentLevel(pub u8);

impl From<u8> for IndentLevel {
    fn from(level: u8) -> IndentLevel {
        IndentLevel(level)
    }
}

impl IndentLevel {
    pub fn from_node(node: &SyntaxNode) -> IndentLevel {
        let first_token = match node.first_token() {
            Some(it) => it,
            None => return IndentLevel(0),
        };
        for ws in prev_tokens(first_token).filter_map(ast::Whitespace::cast) {
            let text = ws.syntax().text();
            if let Some(pos) = text.rfind('\n') {
                let level = text[pos + 1..].chars().count() / 4;
                return IndentLevel(level as u8);
            }
        }
        IndentLevel(0)
    }

    pub fn increase_indent<N: AstNode>(self, node: N) -> N {
        N::cast(self._increase_indent(node.syntax().clone())).unwrap()
    }

    fn _increase_indent(self, node: SyntaxNode) -> SyntaxNode {
        let replacements: FxHashMap<SyntaxElement, SyntaxElement> = node
            .descendants_with_tokens()
            .filter_map(|el| el.into_token())
            .filter_map(ast::Whitespace::cast)
            .filter(|ws| {
                let text = ws.syntax().text();
                text.contains('\n')
            })
            .map(|ws| {
                (
                    ws.syntax().clone().into(),
                    make::tokens::whitespace(&format!(
                        "{}{:width$}",
                        ws.syntax().text(),
                        "",
                        width = self.0 as usize * 4
                    ))
                    .into(),
                )
            })
            .collect();
        algo::replace_descendants(&node, |n| replacements.get(n).cloned())
    }

    pub fn decrease_indent<N: AstNode>(self, node: N) -> N {
        N::cast(self._decrease_indent(node.syntax().clone())).unwrap()
    }

    fn _decrease_indent(self, node: SyntaxNode) -> SyntaxNode {
        let replacements: FxHashMap<SyntaxElement, SyntaxElement> = node
            .descendants_with_tokens()
            .filter_map(|el| el.into_token())
            .filter_map(ast::Whitespace::cast)
            .filter(|ws| {
                let text = ws.syntax().text();
                text.contains('\n')
            })
            .map(|ws| {
                (
                    ws.syntax().clone().into(),
                    make::tokens::whitespace(
                        &ws.syntax()
                            .text()
                            .replace(&format!("\n{:1$}", "", self.0 as usize * 4), "\n"),
                    )
                    .into(),
                )
            })
            .collect();
        algo::replace_descendants(&node, |n| replacements.get(n).cloned())
    }
}

// FIXME: replace usages with IndentLevel above
fn leading_indent(node: &SyntaxNode) -> Option<SmolStr> {
    for token in prev_tokens(node.first_token()?) {
        if let Some(ws) = ast::Whitespace::cast(token.clone()) {
            let ws_text = ws.text();
            if let Some(pos) = ws_text.rfind('\n') {
                return Some(ws_text[pos + 1..].into());
            }
        }
        if token.text().contains('\n') {
            break;
        }
    }
    None
}

fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> {
    iter::successors(Some(token), |token| token.prev_token())
}

#[must_use]
fn insert_children<N: AstNode>(
    parent: &N,
    position: InsertPosition<SyntaxElement>,
    to_insert: impl IntoIterator<Item = SyntaxElement>,
) -> N {
    let new_syntax = algo::insert_children(parent.syntax(), position, to_insert);
    N::cast(new_syntax).unwrap()
}

fn single_node(element: impl Into<SyntaxElement>) -> RangeInclusive<SyntaxElement> {
    let element = element.into();
    element.clone()..=element
}

#[must_use]
fn replace_children<N: AstNode>(
    parent: &N,
    to_replace: RangeInclusive<SyntaxElement>,
    to_insert: impl IntoIterator<Item = SyntaxElement>,
) -> N {
    let new_syntax = algo::replace_children(parent.syntax(), to_replace, to_insert);
    N::cast(new_syntax).unwrap()
}

#[test]
fn test_increase_indent() {
    let arm_list = {
        let arm = make::match_arm(iter::once(make::placeholder_pat().into()), make::expr_unit());
        make::match_arm_list(vec![arm.clone(), arm])
    };
    assert_eq!(
        arm_list.syntax().to_string(),
        "{
    _ => (),
    _ => (),
}"
    );
    let indented = IndentLevel(2).increase_indent(arm_list);
    assert_eq!(
        indented.syntax().to_string(),
        "{
            _ => (),
            _ => (),
        }"
    );
}
<commit_msg>Strongly-typed generic methods for editing nodes<commit_after>//! This module contains functions for editing syntax trees. As the trees are
//! immutable, all function here return a fresh copy of the tree, instead of
//! doing an in-place modification.
use std::{iter, ops::RangeInclusive};

use arrayvec::ArrayVec;
use rustc_hash::FxHashMap;

use crate::{
    algo,
    ast::{
        self,
        make::{self, tokens},
        AstNode, TypeBoundsOwner,
    },
    AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, SyntaxKind,
    SyntaxKind::{ATTR, COMMENT, WHITESPACE},
    SyntaxNode, SyntaxToken, T,
};

impl ast::BinExpr {
    #[must_use]
    pub fn replace_op(&self, op: SyntaxKind) -> Option<ast::BinExpr> {
        let op_node: SyntaxElement = self.op_details()?.0.into();
        let to_insert: Option<SyntaxElement> = Some(make::token(op).into());
        Some(self.replace_children(single_node(op_node), to_insert))
    }
}

impl ast::FnDef {
    #[must_use]
    pub fn with_body(&self, body: ast::Block) -> ast::FnDef {
        let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new();
        let old_body_or_semi: SyntaxElement = if let Some(old_body) = self.body() {
            old_body.syntax().clone().into()
        } else if let Some(semi) = self.semicolon_token() {
            to_insert.push(make::tokens::single_space().into());
            semi.into()
        } else {
            to_insert.push(make::tokens::single_space().into());
            to_insert.push(body.syntax().clone().into());
            return self.insert_children(InsertPosition::Last, to_insert);
        };
        to_insert.push(body.syntax().clone().into());
        self.replace_children(single_node(old_body_or_semi), to_insert)
    }
}

impl ast::ItemList {
    #[must_use]
    pub fn append_items(&self, items: impl Iterator<Item = ast::ImplItem>) -> ast::ItemList {
        let mut res = self.clone();
        if !self.syntax().text().contains_char('\n') {
            res = res.make_multiline();
        }
        items.for_each(|it| res = res.append_item(it));
        res
    }

    #[must_use]
    pub fn append_item(&self, item: ast::ImplItem) -> ast::ItemList {
        let (indent, position) = match self.impl_items().last() {
            Some(it) => (
                leading_indent(it.syntax()).unwrap_or_default().to_string(),
                InsertPosition::After(it.syntax().clone().into()),
            ),
            None => match self.l_curly() {
                Some(it) => (
                    "    ".to_string() + &leading_indent(self.syntax()).unwrap_or_default(),
                    InsertPosition::After(it),
                ),
                None => return self.clone(),
            },
        };
        let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
        let to_insert: ArrayVec<[SyntaxElement; 2]> =
            [ws.ws().into(), item.syntax().clone().into()].into();
        self.insert_children(position, to_insert)
    }

    fn l_curly(&self) -> Option<SyntaxElement> {
        self.syntax().children_with_tokens().find(|it| it.kind() == T!['{'])
    }

    fn make_multiline(&self) -> ast::ItemList {
        let l_curly = match self.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) {
            Some(it) => it,
            None => return self.clone(),
        };
        let sibling = match l_curly.next_sibling_or_token() {
            Some(it) => it,
            None => return self.clone(),
        };
        let existing_ws = match sibling.as_token() {
            None => None,
            Some(tok) if tok.kind() != WHITESPACE => None,
            Some(ws) => {
                if ws.text().contains('\n') {
                    return self.clone();
                }
                Some(ws.clone())
            }
        };

        let indent = leading_indent(self.syntax()).unwrap_or_default();
        let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
        let to_insert = iter::once(ws.ws().into());
        match existing_ws {
            None => self.insert_children(InsertPosition::After(l_curly), to_insert),
            Some(ws) => self.replace_children(single_node(ws), to_insert),
        }
    }
}

impl ast::RecordFieldList {
    #[must_use]
    pub fn append_field(&self, field: &ast::RecordField) -> ast::RecordFieldList {
        self.insert_field(InsertPosition::Last, field)
    }

    #[must_use]
    pub fn insert_field(
        &self,
        position: InsertPosition<&'_ ast::RecordField>,
        field: &ast::RecordField,
    ) -> ast::RecordFieldList {
        let is_multiline = self.syntax().text().contains_char('\n');
        let ws;
        let space = if is_multiline {
            ws = tokens::WsBuilder::new(&format!(
                "\n{}    ",
                leading_indent(self.syntax()).unwrap_or_default()
            ));
            ws.ws()
        } else {
            tokens::single_space()
        };

        let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new();
        to_insert.push(space.into());
        to_insert.push(field.syntax().clone().into());
        to_insert.push(make::token(T![,]).into());

        macro_rules! after_l_curly {
            () => {{
                let anchor = match self.l_curly() {
                    Some(it) => it,
                    None => return self.clone(),
                };
                InsertPosition::After(anchor)
            }};
        }

        macro_rules! after_field {
            ($anchor:expr) => {
                if let Some(comma) = $anchor
                    .syntax()
                    .siblings_with_tokens(Direction::Next)
                    .find(|it| it.kind() == T![,])
                {
                    InsertPosition::After(comma)
                } else {
                    to_insert.insert(0, make::token(T![,]).into());
                    InsertPosition::After($anchor.syntax().clone().into())
                }
            };
        };

        let position = match position {
            InsertPosition::First => after_l_curly!(),
            InsertPosition::Last => {
                if !is_multiline {
                    // don't insert comma before curly
                    to_insert.pop();
                }
                match self.fields().last() {
                    Some(it) => after_field!(it),
                    None => after_l_curly!(),
                }
            }
            InsertPosition::Before(anchor) => {
                InsertPosition::Before(anchor.syntax().clone().into())
            }
            InsertPosition::After(anchor) => after_field!(anchor),
        };

        self.insert_children(position, to_insert)
    }

    fn l_curly(&self) -> Option<SyntaxElement> {
        self.syntax().children_with_tokens().find(|it| it.kind() == T!['{'])
    }
}

impl ast::TypeParam {
    #[must_use]
    pub fn remove_bounds(&self) -> ast::TypeParam {
        let colon = match self.colon_token() {
            Some(it) => it,
            None => return self.clone(),
        };
        let end = match self.type_bound_list() {
            Some(it) => it.syntax().clone().into(),
            None => colon.clone().into(),
        };
        self.replace_children(colon.into()..=end, iter::empty())
    }
}

impl ast::Path {
    #[must_use]
    pub fn with_segment(&self, segment: ast::PathSegment) -> ast::Path {
        if let Some(old) = self.segment() {
            return self.replace_children(
                single_node(old.syntax().clone()),
                iter::once(segment.syntax().clone().into()),
            );
        }
        self.clone()
    }
}

impl ast::PathSegment {
    #[must_use]
    pub fn with_type_args(&self, type_args: ast::TypeArgList) -> ast::PathSegment {
        self._with_type_args(type_args, false)
    }

    #[must_use]
    pub fn with_turbo_fish(&self, type_args: ast::TypeArgList) -> ast::PathSegment {
        self._with_type_args(type_args, true)
    }

    fn _with_type_args(&self, type_args: ast::TypeArgList, turbo: bool) -> ast::PathSegment {
        if let Some(old) = self.type_arg_list() {
            return self.replace_children(
                single_node(old.syntax().clone()),
                iter::once(type_args.syntax().clone().into()),
            );
        }
        let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new();
        if turbo {
            to_insert.push(make::token(T![::]).into());
        }
        to_insert.push(type_args.syntax().clone().into());
        self.insert_children(InsertPosition::Last, to_insert)
    }
}

impl ast::UseItem {
    #[must_use]
    pub fn with_use_tree(&self, use_tree: ast::UseTree) -> ast::UseItem {
        if let Some(old) = self.use_tree() {
            return self.replace_descendants(iter::once((old, use_tree)));
        }
        self.clone()
    }
}

impl ast::UseTree {
    #[must_use]
    pub fn with_path(&self, path: ast::Path) -> ast::UseTree {
        if let Some(old) = self.path() {
            return self.replace_descendants(iter::once((old, path)));
        }
        self.clone()
    }

    #[must_use]
    pub fn with_use_tree_list(&self, use_tree_list: ast::UseTreeList) -> ast::UseTree {
        if let Some(old) = self.use_tree_list() {
            return self.replace_descendants(iter::once((old, use_tree_list)));
        }
        self.clone()
    }
}

#[must_use]
pub fn strip_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N {
    N::cast(strip_attrs_and_docs_inner(node.syntax().clone())).unwrap()
}

fn strip_attrs_and_docs_inner(mut node: SyntaxNode) -> SyntaxNode {
    while let Some(start) =
        node.children_with_tokens().find(|it| it.kind() == ATTR || it.kind() == COMMENT)
    {
        let end = match &start.next_sibling_or_token() {
            Some(el) if el.kind() == WHITESPACE => el.clone(),
            Some(_) | None => start.clone(),
        };
        node = algo::replace_children(&node, start..=end, &mut iter::empty());
    }
    node
}

#[derive(Debug, Clone, Copy)]
pub struct IndentLevel(pub u8);

impl From<u8> for IndentLevel {
    fn from(level: u8) -> IndentLevel {
        IndentLevel(level)
    }
}

impl IndentLevel {
    pub fn from_node(node: &SyntaxNode) -> IndentLevel {
        let first_token = match node.first_token() {
            Some(it) => it,
            None => return IndentLevel(0),
        };
        for ws in prev_tokens(first_token).filter_map(ast::Whitespace::cast) {
            let text = ws.syntax().text();
            if let Some(pos) = text.rfind('\n') {
                let level = text[pos + 1..].chars().count() / 4;
                return IndentLevel(level as u8);
            }
        }
        IndentLevel(0)
    }

    pub fn increase_indent<N: AstNode>(self, node: N) -> N {
        N::cast(self._increase_indent(node.syntax().clone())).unwrap()
    }

    fn _increase_indent(self, node: SyntaxNode) -> SyntaxNode {
        let replacements: FxHashMap<SyntaxElement, SyntaxElement> = node
            .descendants_with_tokens()
            .filter_map(|el| el.into_token())
            .filter_map(ast::Whitespace::cast)
            .filter(|ws| {
                let text = ws.syntax().text();
                text.contains('\n')
            })
            .map(|ws| {
                (
                    ws.syntax().clone().into(),
                    make::tokens::whitespace(&format!(
                        "{}{:width$}",
                        ws.syntax().text(),
                        "",
                        width = self.0 as usize * 4
                    ))
                    .into(),
                )
            })
            .collect();
        algo::replace_descendants(&node, |n| replacements.get(n).cloned())
    }

    pub fn decrease_indent<N: AstNode>(self, node: N) -> N {
        N::cast(self._decrease_indent(node.syntax().clone())).unwrap()
    }

    fn _decrease_indent(self, node: SyntaxNode) -> SyntaxNode {
        let replacements: FxHashMap<SyntaxElement, SyntaxElement> = node
            .descendants_with_tokens()
            .filter_map(|el| el.into_token())
            .filter_map(ast::Whitespace::cast)
            .filter(|ws| {
                let text = ws.syntax().text();
                text.contains('\n')
            })
            .map(|ws| {
                (
                    ws.syntax().clone().into(),
                    make::tokens::whitespace(
                        &ws.syntax()
                            .text()
                            .replace(&format!("\n{:1$}", "", self.0 as usize * 4), "\n"),
                    )
                    .into(),
                )
            })
            .collect();
        algo::replace_descendants(&node, |n| replacements.get(n).cloned())
    }
}

// FIXME: replace usages with IndentLevel above
fn leading_indent(node: &SyntaxNode) -> Option<SmolStr> {
    for token in prev_tokens(node.first_token()?) {
        if let Some(ws) = ast::Whitespace::cast(token.clone()) {
            let ws_text = ws.text();
            if let Some(pos) = ws_text.rfind('\n') {
                return Some(ws_text[pos + 1..].into());
            }
        }
        if token.text().contains('\n') {
            break;
        }
    }
    None
}

fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> {
    iter::successors(Some(token), |token| token.prev_token())
}

pub trait AstNodeEdit: AstNode + Sized {
    #[must_use]
    fn insert_children(
        &self,
        position: InsertPosition<SyntaxElement>,
        to_insert: impl IntoIterator<Item = SyntaxElement>,
    ) -> Self {
        let new_syntax = algo::insert_children(self.syntax(), position, to_insert);
        Self::cast(new_syntax).unwrap()
    }

    #[must_use]
    fn replace_children(
        &self,
        to_replace: RangeInclusive<SyntaxElement>,
        to_insert: impl IntoIterator<Item = SyntaxElement>,
    ) -> Self {
        let new_syntax = algo::replace_children(self.syntax(), to_replace, to_insert);
        Self::cast(new_syntax).unwrap()
    }

    #[must_use]
    fn replace_descendants<D: AstNode>(
        &self,
        replacement_map: impl IntoIterator<Item = (D, D)>,
    ) -> Self {
        let map = replacement_map
            .into_iter()
            .map(|(from, to)| (from.syntax().clone().into(), to.syntax().clone().into()))
            .collect::<FxHashMap<SyntaxElement, _>>();
        let new_syntax = algo::replace_descendants(self.syntax(), |n| map.get(n).cloned());
        Self::cast(new_syntax).unwrap()
    }
}

impl<N: AstNode> AstNodeEdit for N {}

fn single_node(element: impl Into<SyntaxElement>) -> RangeInclusive<SyntaxElement> {
    let element = element.into();
    element.clone()..=element
}

#[test]
fn test_increase_indent() {
    let arm_list = {
        let arm = make::match_arm(iter::once(make::placeholder_pat().into()), make::expr_unit());
        make::match_arm_list(vec![arm.clone(), arm])
    };
    assert_eq!(
        arm_list.syntax().to_string(),
        "{
    _ => (),
    _ => (),
}"
    );
    let indented = IndentLevel(2).increase_indent(arm_list);
    assert_eq!(
        indented.syntax().to_string(),
        "{
            _ => (),
            _ => (),
        }"
    );
}
<|endoftext|>


<commit_before>#[feature(globs)];

extern mod OpenCL;
use OpenCL::CL::*;
use OpenCL::CL::ll::*;
use std::ptr;
use std::mem;
use std::libc;
use std::vec;
use std::cast;

#[fixed_stack_segment]
fn main()
{
	unsafe
	{
		let ker =
				~"__kernel void vector_add(__global const long *A, __global const long *B, __global long *C) {
					int i = get_global_id(0);
					C[i] = A[i] + B[i];
				 }";

		let sz: uint = 8;
		let vec_a = ~[0, 1, 2, -3, 4, 5, 6, 7];
		let vec_b = ~[-7, -6, 5, -4, 0, -1, 2, 3];

		let mut r: cl_int = 1;

		let platforms = OpenCL::hl::get_platforms();

		let device = OpenCL::hl::get_devices(platforms[0], CL_DEVICE_TYPE_ALL)[0];
		println(format!("{:s}", device.name()));

		let ctx = OpenCL::hl::create_context(device);

		let cque = OpenCL::hl::create_command_queue(&ctx, device);
		//let cque = comque.cqueue;
		let A = OpenCL::hl::create_buffer(&ctx, sz*8, CL_MEM_READ_ONLY);
		let B = OpenCL::hl::create_buffer(&ctx, sz*8, CL_MEM_READ_ONLY);
		let C = OpenCL::hl::create_buffer(&ctx, sz*8, CL_MEM_WRITE_ONLY);

		// Copy lists into memory buffers
		clEnqueueWriteBuffer(cque.cqueue,
			A.buffer,
			CL_TRUE,
			0,
			(sz * mem::size_of::<int>()) as libc::size_t,
			vec::raw::to_ptr(vec_a) as *libc::c_void,
			0,
			ptr::null(),
			ptr::null());

		clEnqueueWriteBuffer(cque.cqueue,
			B.buffer,
			CL_TRUE,
			0,
			(sz * mem::size_of::<int>()) as libc::size_t,
			vec::raw::to_ptr(vec_b) as *libc::c_void,
			0,
			ptr::null(),
			ptr::null());

		// Create a program from the kernel and build it
		do ker.as_imm_buf |bytes, len|
		{
			let prog = clCreateProgramWithSource(ctx.ctx,
						1,
						ptr::to_unsafe_ptr(&(bytes as *libc::c_char)),
						ptr::to_unsafe_ptr(&(len as libc::size_t)),
						ptr::to_unsafe_ptr(&r));

			r = clBuildProgram(prog,
				1,
				ptr::to_unsafe_ptr(&device.id),
				ptr::null(),
				cast::transmute(ptr::null::<&fn ()>()),
				ptr::null());

			if r != CL_SUCCESS as cl_int
			{ println(format!("Unable to build program [{:?}].", r)); }

			// Create the OpenCL kernel
			do "vector_add".as_imm_buf() |bytes, _|
			{
				let kernel = clCreateKernel(prog, bytes as *libc::c_char, ptr::to_unsafe_ptr(&r));
				if r != CL_SUCCESS as cl_int
				{ println(format!("Unable to create kernel [{:?}].", r)); }

				// Set the arguments of the kernel
				clSetKernelArg(kernel,
					0,
					mem::size_of::<cl_mem>() as libc::size_t,
					ptr::to_unsafe_ptr(&A.buffer)	 as *libc::c_void);

				clSetKernelArg(kernel,
					1,
					mem::size_of::<cl_mem>() as libc::size_t,
					ptr::to_unsafe_ptr(&B.buffer)	 as *libc::c_void);

				clSetKernelArg(kernel,
					2,
					mem::size_of::<cl_mem>() as libc::size_t,
					ptr::to_unsafe_ptr(&C.buffer)	 as *libc::c_void);

				let global_item_size: libc::size_t = (sz * mem::size_of::<int>()) as libc::size_t;
				let local_item_size:	libc::size_t = 64;

				// Execute the OpenCL kernel on the list
				clEnqueueNDRangeKernel(cque.cqueue,
					kernel,
					1,
					ptr::null(),
					ptr::to_unsafe_ptr(&global_item_size),
					ptr::to_unsafe_ptr(&local_item_size),
					0,
					ptr::null(),
					ptr::null());

				// Now let's read back the new list from the device
				let buf = libc::malloc((sz * mem::size_of::<int>()) as libc::size_t);

				clEnqueueReadBuffer(cque.cqueue,
					C.buffer,
					CL_TRUE,
					0,
					(sz * mem::size_of::<int>()) as libc::size_t,
					buf,
					0,
					ptr::null(),
					ptr::null());

				let vec_c = vec::from_buf(buf as *int, sz);

				libc::free(buf);

				println(format!("	{:?}", vec_a));
				println(format!("+	{:?}", vec_b));
				println(format!("=	{:?}", vec_c));

				// Cleanup
//				clReleaseKernel(kernel);
//				clReleaseProgram(prog);
			}
		}
	}
}
<commit_msg>Fixed work item size buf in test.rs<commit_after>#[feature(globs)];

extern mod OpenCL;
use OpenCL::CL::*;
use OpenCL::CL::ll::*;
use std::ptr;
use std::mem;
use std::libc;
use std::vec;
use std::cast;

#[fixed_stack_segment]
fn main()
{
	unsafe
	{
		let ker =
				~"__kernel void vector_add(__global const long *A, __global const long *B, __global long *C) {
					int i = get_global_id(0);
					C[i] = A[i] + B[i];
				 }";

		let sz: uint = 8;
		let vec_a = ~[0, 1, 2, -3, 4, 5, 6, 7];
		let vec_b = ~[-7, -6, 5, -4, 0, -1, 2, 3];

		let mut r: cl_int = 1;

		let platforms = OpenCL::hl::get_platforms();

		let device = OpenCL::hl::get_devices(platforms[0], CL_DEVICE_TYPE_ALL)[0];
		println(format!("{:s}", device.name()));

		let ctx = OpenCL::hl::create_context(device);

		let cque = OpenCL::hl::create_command_queue(&ctx, device);
		//let cque = comque.cqueue;
		let A = OpenCL::hl::create_buffer(&ctx, sz*8, CL_MEM_READ_ONLY);
		let B = OpenCL::hl::create_buffer(&ctx, sz*8, CL_MEM_READ_ONLY);
		let C = OpenCL::hl::create_buffer(&ctx, sz*8, CL_MEM_WRITE_ONLY);

		// Copy lists into memory buffers
		clEnqueueWriteBuffer(cque.cqueue,
			A.buffer,
			CL_TRUE,
			0,
			(sz * mem::size_of::<int>()) as libc::size_t,
			vec::raw::to_ptr(vec_a) as *libc::c_void,
			0,
			ptr::null(),
			ptr::null());

		clEnqueueWriteBuffer(cque.cqueue,
			B.buffer,
			CL_TRUE,
			0,
			(sz * mem::size_of::<int>()) as libc::size_t,
			vec::raw::to_ptr(vec_b) as *libc::c_void,
			0,
			ptr::null(),
			ptr::null());

		// Create a program from the kernel and build it
		do ker.as_imm_buf |bytes, len|
		{
			let prog = clCreateProgramWithSource(ctx.ctx,
						1,
						ptr::to_unsafe_ptr(&(bytes as *libc::c_char)),
						ptr::to_unsafe_ptr(&(len as libc::size_t)),
						ptr::to_unsafe_ptr(&r));

			r = clBuildProgram(prog,
				1,
				ptr::to_unsafe_ptr(&device.id),
				ptr::null(),
				cast::transmute(ptr::null::<&fn ()>()),
				ptr::null());

			if r != CL_SUCCESS as cl_int
			{ println(format!("Unable to build program [{:?}].", r)); }

			// Create the OpenCL kernel
			do "vector_add".as_imm_buf() |bytes, _|
			{
				let kernel = clCreateKernel(prog, bytes as *libc::c_char, ptr::to_unsafe_ptr(&r));
				if r != CL_SUCCESS as cl_int
				{ println(format!("Unable to create kernel [{:?}].", r)); }

				// Set the arguments of the kernel
				clSetKernelArg(kernel,
					0,
					mem::size_of::<cl_mem>() as libc::size_t,
					ptr::to_unsafe_ptr(&A.buffer)	 as *libc::c_void);

				clSetKernelArg(kernel,
					1,
					mem::size_of::<cl_mem>() as libc::size_t,
					ptr::to_unsafe_ptr(&B.buffer)	 as *libc::c_void);

				clSetKernelArg(kernel,
					2,
					mem::size_of::<cl_mem>() as libc::size_t,
					ptr::to_unsafe_ptr(&C.buffer)	 as *libc::c_void);

				let global_item_size: libc::size_t = (sz) as libc::size_t;
				let local_item_size:	libc::size_t = 64;

				// Execute the OpenCL kernel on the list
				clEnqueueNDRangeKernel(cque.cqueue,
					kernel,
					1,
					ptr::null(),
					ptr::to_unsafe_ptr(&global_item_size),
					ptr::to_unsafe_ptr(&local_item_size),
					0,
					ptr::null(),
					ptr::null());

				// Now let's read back the new list from the device
				let buf = libc::malloc((sz * mem::size_of::<int>()) as libc::size_t);

				clEnqueueReadBuffer(cque.cqueue,
					C.buffer,
					CL_TRUE,
					0,
					(sz * mem::size_of::<int>()) as libc::size_t,
					buf,
					0,
					ptr::null(),
					ptr::null());

				let vec_c = vec::from_buf(buf as *int, sz);

				libc::free(buf);

				println(format!("	{:?}", vec_a));
				println(format!("+	{:?}", vec_b));
				println(format!("=	{:?}", vec_c));

				// Cleanup
//				clReleaseKernel(kernel);
//				clReleaseProgram(prog);
			}
		}
	}
}
<|endoftext|>


<commit_before>pub type in_addr_t = u32;
pub type in_port_t = u16;

pub type socklen_t = u32;
pub type sa_family_t = u16;

s! {
    pub struct in_addr {
        pub s_addr: in_addr_t,
    }

    pub struct in6_addr {
        pub s6_addr: [u8; 16],
        __align: [u32; 0],
    }

    pub struct ip_mreq {
        pub imr_multiaddr: in_addr,
        pub imr_interface: in_addr,
    }

    pub struct ipv6_mreq {
        pub ipv6mr_multiaddr: in6_addr,
        pub ipv6mr_interface: ::c_uint,
    }

    pub struct linger {
        pub l_onoff: ::c_int,
        pub l_linger: ::c_int,
    }

    pub struct sockaddr {
        pub sa_family: sa_family_t,
        pub sa_data: [::c_char; 14],
    }

    pub struct sockaddr_in {
        pub sin_family: sa_family_t,
        pub sin_port: ::in_port_t,
        pub sin_addr: ::in_addr,
        pub sin_zero: [u8; 8],
    }

    pub struct sockaddr_in6 {
        pub sin6_family: sa_family_t,
        pub sin6_port: in_port_t,
        pub sin6_flowinfo: u32,
        pub sin6_addr: ::in6_addr,
        pub sin6_scope_id: u32,
    }

    pub struct sockaddr_storage {
        pub ss_family: sa_family_t,
        pub __ss_padding: [u8; 26],
    }
}

pub const AF_INET: ::c_int = 2;
pub const AF_INET6: ::c_int = 23;

pub const SOCK_STREAM: ::c_int = 1;
pub const SOCK_DGRAM: ::c_int = 2;

pub const IPPROTO_TCP: ::c_int = 6;
pub const IPPROTO_IP: ::c_int = 0;
pub const IPPROTO_IPV6: ::c_int = 41;

pub const TCP_KEEPIDLE: ::c_int = 4;
pub const TCP_NODELAY: ::c_int = 8193;

pub const IP_TTL: ::c_int = 8;
pub const IP_MULTICAST_LOOP: ::c_int = 9;
pub const IP_MULTICAST_TTL: ::c_int = 10;
pub const IP_ADD_MEMBERSHIP: ::c_int = 11;
pub const IP_DROP_MEMBERSHIP: ::c_int = 12;

pub const IPV6_MULTICAST_LOOP: ::c_int = 19;
pub const IPV6_ADD_MEMBERSHIP: ::c_int = 20;
pub const IPV6_DROP_MEMBERSHIP: ::c_int = 21;
pub const IPV6_V6ONLY: ::c_int = 26;

pub const SOL_SOCKET: ::c_int = 65535;

pub const SO_REUSEADDR: ::c_int = 4;
pub const SO_BROADCAST: ::c_int = 6;
pub const SO_KEEPALIVE: ::c_int = 8;
pub const SO_RCVTIMEO: ::c_int = 20;
pub const SO_SNDTIMEO: ::c_int = 21;
pub const SO_LINGER: ::c_int = 128;
pub const SO_SNDBUF: ::c_int = 4097;
pub const SO_RCVBUF: ::c_int = 4098;
pub const SO_ERROR: ::c_int = 4105;

extern {
    pub fn socket(domain: ::c_int, ty: ::c_int, protocol: ::c_int) -> ::c_int;
    pub fn bind(fd: ::c_int, addr: *const sockaddr, len: socklen_t) -> ::c_int;
    pub fn connect(socket: ::c_int, address: *const sockaddr,
                   len: socklen_t) -> ::c_int;
    pub fn listen(socket: ::c_int, backlog: ::c_int) -> ::c_int;
    pub fn getsockname(socket: ::c_int, address: *mut sockaddr,
                       address_len: *mut socklen_t) -> ::c_int;
    pub fn getsockopt(sockfd: ::c_int,
                      level: ::c_int,
                      optname: ::c_int,
                      optval: *mut ::c_void,
                      optlen: *mut ::socklen_t) -> ::c_int;
    pub fn setsockopt(socket: ::c_int, level: ::c_int, name: ::c_int,
                      value: *const ::c_void,
                      option_len: socklen_t) -> ::c_int;
}
<commit_msg>Add a bunch of functions on redox and hope for the best<commit_after>pub type in_addr_t = u32;
pub type in_port_t = u16;

pub type socklen_t = u32;
pub type sa_family_t = u16;

s! {
    pub struct in_addr {
        pub s_addr: in_addr_t,
    }

    pub struct in6_addr {
        pub s6_addr: [u8; 16],
        __align: [u32; 0],
    }

    pub struct ip_mreq {
        pub imr_multiaddr: in_addr,
        pub imr_interface: in_addr,
    }

    pub struct ipv6_mreq {
        pub ipv6mr_multiaddr: in6_addr,
        pub ipv6mr_interface: ::c_uint,
    }

    pub struct linger {
        pub l_onoff: ::c_int,
        pub l_linger: ::c_int,
    }

    pub struct sockaddr {
        pub sa_family: sa_family_t,
        pub sa_data: [::c_char; 14],
    }

    pub struct sockaddr_in {
        pub sin_family: sa_family_t,
        pub sin_port: ::in_port_t,
        pub sin_addr: ::in_addr,
        pub sin_zero: [u8; 8],
    }

    pub struct sockaddr_in6 {
        pub sin6_family: sa_family_t,
        pub sin6_port: in_port_t,
        pub sin6_flowinfo: u32,
        pub sin6_addr: ::in6_addr,
        pub sin6_scope_id: u32,
    }

    pub struct sockaddr_storage {
        pub ss_family: sa_family_t,
        pub __ss_padding: [u8; 26],
    }
}

pub const AF_INET: ::c_int = 2;
pub const AF_INET6: ::c_int = 23;

pub const SOCK_STREAM: ::c_int = 1;
pub const SOCK_DGRAM: ::c_int = 2;

pub const IPPROTO_TCP: ::c_int = 6;
pub const IPPROTO_IP: ::c_int = 0;
pub const IPPROTO_IPV6: ::c_int = 41;

pub const TCP_KEEPIDLE: ::c_int = 4;
pub const TCP_NODELAY: ::c_int = 8193;

pub const IP_TTL: ::c_int = 8;
pub const IP_MULTICAST_LOOP: ::c_int = 9;
pub const IP_MULTICAST_TTL: ::c_int = 10;
pub const IP_ADD_MEMBERSHIP: ::c_int = 11;
pub const IP_DROP_MEMBERSHIP: ::c_int = 12;

pub const IPV6_MULTICAST_LOOP: ::c_int = 19;
pub const IPV6_ADD_MEMBERSHIP: ::c_int = 20;
pub const IPV6_DROP_MEMBERSHIP: ::c_int = 21;
pub const IPV6_V6ONLY: ::c_int = 26;

pub const SOL_SOCKET: ::c_int = 65535;

pub const SO_REUSEADDR: ::c_int = 4;
pub const SO_BROADCAST: ::c_int = 6;
pub const SO_KEEPALIVE: ::c_int = 8;
pub const SO_RCVTIMEO: ::c_int = 20;
pub const SO_SNDTIMEO: ::c_int = 21;
pub const SO_LINGER: ::c_int = 128;
pub const SO_SNDBUF: ::c_int = 4097;
pub const SO_RCVBUF: ::c_int = 4098;
pub const SO_ERROR: ::c_int = 4105;

extern {
    pub fn socket(domain: ::c_int, ty: ::c_int, protocol: ::c_int) -> ::c_int;
    pub fn bind(fd: ::c_int, addr: *const sockaddr, len: socklen_t) -> ::c_int;
    pub fn connect(socket: ::c_int, address: *const sockaddr,
                   len: socklen_t) -> ::c_int;
    pub fn listen(socket: ::c_int, backlog: ::c_int) -> ::c_int;
    pub fn getsockname(socket: ::c_int, address: *mut sockaddr,
                       address_len: *mut socklen_t) -> ::c_int;
    pub fn getsockopt(sockfd: ::c_int,
                      level: ::c_int,
                      optname: ::c_int,
                      optval: *mut ::c_void,
                      optlen: *mut ::socklen_t) -> ::c_int;
    pub fn setsockopt(socket: ::c_int, level: ::c_int, name: ::c_int,
                      value: *const ::c_void,
                      option_len: socklen_t) -> ::c_int;
    pub fn getpeername(socket: ::c_int, address: *mut sockaddr,
                       address_len: *mut socklen_t) -> ::c_int;
    pub fn sendto(socket: ::c_int, buf: *const ::c_void, len: ::size_t,
                  flags: ::c_int, addr: *const sockaddr,
                  addrlen: socklen_t) -> ::ssize_t;
    pub fn send(socket: ::c_int, buf: *const ::c_void, len: ::size_t,
                flags: ::c_int) -> ::ssize_t;
    pub fn recvfrom(socket: ::c_int, buf: *mut ::c_void, len: ::size_t,
                    flags: ::c_int, addr: *mut ::sockaddr,
                    addrlen: *mut ::socklen_t) -> ::ssize_t;
    pub fn recv(socket: ::c_int, buf: *mut ::c_void, len: ::size_t,
                flags: ::c_int) -> ::ssize_t;
}
<|endoftext|>


<commit_before>//! [β-reduction](https://en.wikipedia.org/wiki/Beta_normal_form) for lambda `Term`s

use term::*;
use term::Term::*;
use self::Order::*;

/// Set to `true` to see all the steps of β-reductions. The default is `false`.
pub const SHOW_REDUCTIONS: bool = false;

/// The [evaluation order](https://en.wikipedia.org/wiki/Lambda_calculus#Reduction_strategies) of
/// β-reductions. `Applicative` order will fail to fully reduce expressions containing functions
/// without a normal form, e.g. the Y combinator (they will expand forever). `CallByName` and
/// `CallByValue` orders only produce weak normal forms. The default is `Normal`.
#[derive(Debug, PartialEq)]
pub enum Order {
    /// leftmost outermost
    Normal,
    /// leftmost outermost not inside an abstraction
    CallByName,
    /// leftmost innermost
    Applicative,
    /// leftmost innermost not inside an abstraction
    CallByValue
}

/// Applies two `Term`s with substitution and variable update, consuming the first one in the
/// process.
///
/// # Example
/// ```
/// use lambda_calculus::reduction::apply;
/// use lambda_calculus::parser::parse;
///
/// let lhs    = parse(&"λλ42(λ13)").unwrap();
/// let rhs    = parse(&"λ51").unwrap();
/// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
///
/// assert_eq!(apply(lhs, &rhs), Ok(result));
/// ```
pub fn apply(mut lhs: Term, rhs: &Term) -> Result<Term, Error> {
    _apply(&mut lhs, rhs, 0);

    lhs.unabs()
}

fn _apply(lhs: &mut Term, rhs: &Term, depth: usize) {
    match *lhs {
        Var(i) => if i == depth {
            *lhs = rhs.clone(); // substitute a top-level variable from lhs with rhs
            update_free_variables(lhs, depth - 1, 0); // update indices of free variables from rhs
        } else if i > depth {
            *lhs = Var(i - 1) // decrement a free variable's index
        },
        Abs(_) => {
            _apply(lhs.unabs_ref_mut().unwrap(), rhs, depth + 1)
        },
        App(_, _) => {
            _apply(lhs.lhs_ref_mut().unwrap(), rhs, depth);
            _apply(lhs.rhs_ref_mut().unwrap(), rhs, depth)
        }
    }
}

fn update_free_variables(term: &mut Term, added_depth: usize, own_depth: usize) {
    match *term {
        Var(i) => if i > own_depth {
            *term = Var(i + added_depth)
        },
        Abs(_) => {
            update_free_variables(term.unabs_ref_mut().unwrap(), added_depth, own_depth + 1)
        },
        App(_, _) => {
            update_free_variables(term.lhs_ref_mut().unwrap(), added_depth, own_depth);
            update_free_variables(term.rhs_ref_mut().unwrap(), added_depth, own_depth)
        }
    }
}

impl Term {
    /// Applies `self` to another `Term` and performs substitution, consuming `self` in the process.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::parser::parse;
    ///
    /// let lhs    = parse(&"λλ42(λ13)").unwrap();
    /// let rhs    = parse(&"λ51").unwrap();
    /// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
    ///
    /// assert_eq!(lhs.apply(&rhs), Ok(result));
    /// ```
    pub fn apply(self, rhs: &Term) -> Result<Term, Error> {
        apply(self, rhs)
    }

    /// Reduces an `App`lication by substitution and variable update.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::{app, abs};
    /// use lambda_calculus::term::Term::Var;
    /// use lambda_calculus::arithmetic::zero;
    /// use lambda_calculus::combinators::i;
    ///
    /// assert_eq!(app(i(), zero()).eval(), Ok(zero()));
    /// ```
    pub fn eval(self) -> Result<Term, Error> {
        let (lhs, rhs) = try!(self.unapp());

        apply(lhs, &rhs)
    }

    /// Performs a single β-reduction on `self` with the specified evaluation `Order`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::succ;
    /// use lambda_calculus::reduction::Order;
    ///
    /// let mut succ_one = succ().app(1.into());
    /// succ_one.beta_once(&Order::Normal);
    ///
    /// assert_eq!(succ_one, succ().apply(&1.into()).unwrap());
    /// ```
    pub fn beta_once(&mut self, order: &Order) {
        self.beta_once_indicative(order);
    }

    fn beta_once_indicative(&mut self, order: &Order) -> bool {
        match *order {
            Normal => self._beta_once_normal(0),
            CallByName => self._beta_once_call_by_name(),
            Applicative => self._beta_once_applicative(0),
            CallByValue => self._beta_once_call_by_value(),
        }
    }

    fn eval_with_info(&mut self, depth: u32) {
        if SHOW_REDUCTIONS { print!("    {} reduces to ", show_precedence(self, 0, depth)) };
        let copy = self.clone();
        *self = copy.eval().unwrap();
        if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) }
    }

    // the return value indicates if reduction was performed
    fn _beta_once_normal(&mut self, depth: u32) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref_mut().unwrap()._beta_once_normal(depth + 1),
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    self.eval_with_info(depth);
                    true
                } else {
                    self.lhs_ref_mut().unwrap()._beta_once_normal(depth) ||
                    self.rhs_ref_mut().unwrap()._beta_once_normal(depth)
                }
            }
        }
    }

    // the return value indicates if reduction was performed
    fn _beta_once_call_by_name(&mut self) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => false,
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    self.eval_with_info(0);
                    true
                } else {
                    self.lhs_ref_mut().unwrap()._beta_once_call_by_name() ||
                    self.rhs_ref_mut().unwrap()._beta_once_call_by_name()
                }
            }
        }
    }

    // the return value indicates if reduction was performed
    fn _beta_once_applicative(&mut self, depth: u32) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref_mut().unwrap()._beta_once_applicative(depth + 1),
            App(_, _) => {
                if !self.lhs_ref().unwrap().is_beta_reducible(&Applicative) &&
                   !self.rhs_ref().unwrap().is_beta_reducible(&Applicative) &&
                    self.lhs_ref().unwrap().unabs_ref().is_ok()
                {
                    self.eval_with_info(depth);
                    true
                } else {
                    self.lhs_ref_mut().unwrap()._beta_once_applicative(depth) ||
                    self.rhs_ref_mut().unwrap()._beta_once_applicative(depth)
                }
            }
        }
    }

    // the return value indicates if reduction was performed
    fn _beta_once_call_by_value(&mut self) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => false,
            App(_, _) => {
                if !self.lhs_ref().unwrap().is_beta_reducible(&CallByValue) &&
                   !self.rhs_ref().unwrap().is_beta_reducible(&CallByValue) &&
                    self.lhs_ref().unwrap().unabs_ref().is_ok()
                {
                    self.eval_with_info(0);
                    true
                } else {
                    self.lhs_ref_mut().unwrap()._beta_once_call_by_value() ||
                    self.rhs_ref_mut().unwrap()._beta_once_call_by_value()
                }
            }
        }
    }

    /// Checks whether `self` is β-reducible under the specified evaluation `Order`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::{succ, zero};
    /// use lambda_calculus::reduction::Order;
    ///
    /// let reducible     = succ().app(1.into());
    /// let not_reducible = zero();
    ///
    /// assert!(reducible.is_beta_reducible(&Order::Normal));
    /// assert!(!not_reducible.is_beta_reducible(&Order::Normal));
    /// ```
    pub fn is_beta_reducible(&self, order: &Order) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => match *order {
                Normal | Applicative => self.unabs_ref().unwrap().is_beta_reducible(order),
                _ => false
            },
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    true
                } else {
                    self.lhs_ref().unwrap().is_beta_reducible(order) ||
                    self.rhs_ref().unwrap().is_beta_reducible(order)
                }
            }
        }
    }

    /// Performs full β-reduction on `self` using the specified evaluation `Order`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::pred;
    /// use lambda_calculus::reduction::Order;
    ///
    /// let mut pred_one = pred().app(1.into());
    /// pred_one.beta_full(&Order::Normal);
    ///
    /// assert_eq!(pred_one, 0.into());
    /// ```
    pub fn beta_full(&mut self, order: &Order) {
        loop {
            if SHOW_REDUCTIONS { println!("reducing {}", self) }
            if !self.beta_once_indicative(order) { break }
        }
        if SHOW_REDUCTIONS { println!("    doesn't reduce") }
    }
}

/// Performs full β-reduction on a `Term` using the specified evaluation `Order`, consuming its
/// argument in the process.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::pred;
/// use lambda_calculus::reduction::{beta_full, Order};
///
/// let pred_one = pred().app(1.into());
///
/// assert_eq!(beta_full(pred_one, &Order::Normal), 0.into());
/// ```
pub fn beta_full(mut term: Term, order: &Order) -> Term {
    term.beta_full(order);
    term
}

/// Performs a single β-reduction on a `Term` using the specified evaluation `Order`, consuming its
/// argument in the process.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::succ;
/// use lambda_calculus::reduction::{beta_once, Order};
///
/// let succ_one = succ().app(1.into());
///
/// assert_eq!(beta_once(succ_one, &Order::Normal), succ().apply(&1.into()).unwrap());
/// ```
pub fn beta_once(mut term: Term, order: &Order) -> Term {
    term.beta_once(order);
    term
}

#[cfg(test)]
mod test {
    use super::*;
    use parser::parse;
    use combinators::i;

    #[test]
    fn normal_order() {
        let reduces_instantly = parse(&"(λλ1)((λλλ((32)1))(λλ2))").unwrap();
        assert_eq!(beta_full(reduces_instantly.clone(), &Normal),
                   beta_once(reduces_instantly, &Normal));

        let should_reduce = parse(&"(λ2)((λ111)(λ111))").unwrap();
        assert_eq!(beta_full(should_reduce, &Normal), Var(1))
    }

    #[test]
    fn call_by_name_order() {
        let mut expr = app(abs(Var(1)), app(Var(2), abs(Var(1))));
        expr.beta_once(&CallByName);
        assert_eq!(expr, app(Var(2), i()));
    }

    #[test]
    fn applicative_order() {
        let expr = parse(&"λ1(((λλλ1)1)((λλ21)1))").unwrap();
        assert_eq!(&format!("{}", beta_once(expr, &Applicative)), "λ1((λλ1)((λλ21)1))");

        let expands = parse(&"(λ2)((λ111)(λ111))").unwrap();
        assert_eq!(&format!("{}", beta_once(expands, &Applicative)), "(λ2)((λ111)(λ111)(λ111))")
    }

    #[test]
    fn call_by_value_order() {
        let mut expr = app(i(), app(i(), abs(app(i(), Var(1)))));
        expr.beta_once(&CallByValue);
        assert_eq!(expr, app(i(), abs(app(i(), Var(1)))));
        expr.beta_once(&CallByValue);
        assert_eq!(expr, abs(app(i(), Var(1))));
        assert!(!expr.is_beta_reducible(&CallByValue));
    }
}
<commit_msg>improve CBN and CBV tests<commit_after>//! [β-reduction](https://en.wikipedia.org/wiki/Beta_normal_form) for lambda `Term`s

use term::*;
use term::Term::*;
use self::Order::*;

/// Set to `true` to see all the steps of β-reductions. The default is `false`.
pub const SHOW_REDUCTIONS: bool = false;

/// The [evaluation order](https://en.wikipedia.org/wiki/Lambda_calculus#Reduction_strategies) of
/// β-reductions. `Applicative` order will fail to fully reduce expressions containing functions
/// without a normal form, e.g. the Y combinator (they will expand forever). `CallByName` and
/// `CallByValue` orders only produce weak normal forms. The default is `Normal`.
#[derive(Debug, PartialEq)]
pub enum Order {
    /// leftmost outermost
    Normal,
    /// leftmost outermost not inside an abstraction
    CallByName,
    /// leftmost innermost
    Applicative,
    /// leftmost innermost not inside an abstraction
    CallByValue
}

/// Applies two `Term`s with substitution and variable update, consuming the first one in the
/// process.
///
/// # Example
/// ```
/// use lambda_calculus::reduction::apply;
/// use lambda_calculus::parser::parse;
///
/// let lhs    = parse(&"λλ42(λ13)").unwrap();
/// let rhs    = parse(&"λ51").unwrap();
/// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
///
/// assert_eq!(apply(lhs, &rhs), Ok(result));
/// ```
pub fn apply(mut lhs: Term, rhs: &Term) -> Result<Term, Error> {
    _apply(&mut lhs, rhs, 0);

    lhs.unabs()
}

fn _apply(lhs: &mut Term, rhs: &Term, depth: usize) {
    match *lhs {
        Var(i) => if i == depth {
            *lhs = rhs.clone(); // substitute a top-level variable from lhs with rhs
            update_free_variables(lhs, depth - 1, 0); // update indices of free variables from rhs
        } else if i > depth {
            *lhs = Var(i - 1) // decrement a free variable's index
        },
        Abs(_) => {
            _apply(lhs.unabs_ref_mut().unwrap(), rhs, depth + 1)
        },
        App(_, _) => {
            _apply(lhs.lhs_ref_mut().unwrap(), rhs, depth);
            _apply(lhs.rhs_ref_mut().unwrap(), rhs, depth)
        }
    }
}

fn update_free_variables(term: &mut Term, added_depth: usize, own_depth: usize) {
    match *term {
        Var(i) => if i > own_depth {
            *term = Var(i + added_depth)
        },
        Abs(_) => {
            update_free_variables(term.unabs_ref_mut().unwrap(), added_depth, own_depth + 1)
        },
        App(_, _) => {
            update_free_variables(term.lhs_ref_mut().unwrap(), added_depth, own_depth);
            update_free_variables(term.rhs_ref_mut().unwrap(), added_depth, own_depth)
        }
    }
}

impl Term {
    /// Applies `self` to another `Term` and performs substitution, consuming `self` in the process.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::parser::parse;
    ///
    /// let lhs    = parse(&"λλ42(λ13)").unwrap();
    /// let rhs    = parse(&"λ51").unwrap();
    /// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
    ///
    /// assert_eq!(lhs.apply(&rhs), Ok(result));
    /// ```
    pub fn apply(self, rhs: &Term) -> Result<Term, Error> {
        apply(self, rhs)
    }

    /// Reduces an `App`lication by substitution and variable update.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::{app, abs};
    /// use lambda_calculus::term::Term::Var;
    /// use lambda_calculus::arithmetic::zero;
    /// use lambda_calculus::combinators::i;
    ///
    /// assert_eq!(app(i(), zero()).eval(), Ok(zero()));
    /// ```
    pub fn eval(self) -> Result<Term, Error> {
        let (lhs, rhs) = try!(self.unapp());

        apply(lhs, &rhs)
    }

    /// Performs a single β-reduction on `self` with the specified evaluation `Order`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::succ;
    /// use lambda_calculus::reduction::Order;
    ///
    /// let mut succ_one = succ().app(1.into());
    /// succ_one.beta_once(&Order::Normal);
    ///
    /// assert_eq!(succ_one, succ().apply(&1.into()).unwrap());
    /// ```
    pub fn beta_once(&mut self, order: &Order) {
        self.beta_once_indicative(order);
    }

    fn beta_once_indicative(&mut self, order: &Order) -> bool {
        match *order {
            Normal => self._beta_once_normal(0),
            CallByName => self._beta_once_call_by_name(),
            Applicative => self._beta_once_applicative(0),
            CallByValue => self._beta_once_call_by_value(),
        }
    }

    fn eval_with_info(&mut self, depth: u32) {
        if SHOW_REDUCTIONS { print!("    {} reduces to ", show_precedence(self, 0, depth)) };
        let copy = self.clone();
        *self = copy.eval().unwrap();
        if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) }
    }

    // the return value indicates if reduction was performed
    fn _beta_once_normal(&mut self, depth: u32) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref_mut().unwrap()._beta_once_normal(depth + 1),
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    self.eval_with_info(depth);
                    true
                } else {
                    self.lhs_ref_mut().unwrap()._beta_once_normal(depth) ||
                    self.rhs_ref_mut().unwrap()._beta_once_normal(depth)
                }
            }
        }
    }

    // the return value indicates if reduction was performed
    fn _beta_once_call_by_name(&mut self) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => false,
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    self.eval_with_info(0);
                    true
                } else {
                    self.lhs_ref_mut().unwrap()._beta_once_call_by_name() ||
                    self.rhs_ref_mut().unwrap()._beta_once_call_by_name()
                }
            }
        }
    }

    // the return value indicates if reduction was performed
    fn _beta_once_applicative(&mut self, depth: u32) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref_mut().unwrap()._beta_once_applicative(depth + 1),
            App(_, _) => {
                if !self.lhs_ref().unwrap().is_beta_reducible(&Applicative) &&
                   !self.rhs_ref().unwrap().is_beta_reducible(&Applicative) &&
                    self.lhs_ref().unwrap().unabs_ref().is_ok()
                {
                    self.eval_with_info(depth);
                    true
                } else {
                    self.lhs_ref_mut().unwrap()._beta_once_applicative(depth) ||
                    self.rhs_ref_mut().unwrap()._beta_once_applicative(depth)
                }
            }
        }
    }

    // the return value indicates if reduction was performed
    fn _beta_once_call_by_value(&mut self) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => false,
            App(_, _) => {
                if !self.lhs_ref().unwrap().is_beta_reducible(&CallByValue) &&
                   !self.rhs_ref().unwrap().is_beta_reducible(&CallByValue) &&
                    self.lhs_ref().unwrap().unabs_ref().is_ok()
                {
                    self.eval_with_info(0);
                    true
                } else {
                    self.lhs_ref_mut().unwrap()._beta_once_call_by_value() ||
                    self.rhs_ref_mut().unwrap()._beta_once_call_by_value()
                }
            }
        }
    }

    /// Checks whether `self` is β-reducible under the specified evaluation `Order`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::{succ, zero};
    /// use lambda_calculus::reduction::Order;
    ///
    /// let reducible     = succ().app(1.into());
    /// let not_reducible = zero();
    ///
    /// assert!(reducible.is_beta_reducible(&Order::Normal));
    /// assert!(!not_reducible.is_beta_reducible(&Order::Normal));
    /// ```
    pub fn is_beta_reducible(&self, order: &Order) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => match *order {
                Normal | Applicative => self.unabs_ref().unwrap().is_beta_reducible(order),
                _ => false
            },
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    true
                } else {
                    self.lhs_ref().unwrap().is_beta_reducible(order) ||
                    self.rhs_ref().unwrap().is_beta_reducible(order)
                }
            }
        }
    }

    /// Performs full β-reduction on `self` using the specified evaluation `Order`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::pred;
    /// use lambda_calculus::reduction::Order;
    ///
    /// let mut pred_one = pred().app(1.into());
    /// pred_one.beta_full(&Order::Normal);
    ///
    /// assert_eq!(pred_one, 0.into());
    /// ```
    pub fn beta_full(&mut self, order: &Order) {
        loop {
            if SHOW_REDUCTIONS { println!("reducing {}", self) }
            if !self.beta_once_indicative(order) { break }
        }
        if SHOW_REDUCTIONS { println!("    doesn't reduce") }
    }
}

/// Performs full β-reduction on a `Term` using the specified evaluation `Order`, consuming its
/// argument in the process.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::pred;
/// use lambda_calculus::reduction::{beta_full, Order};
///
/// let pred_one = pred().app(1.into());
///
/// assert_eq!(beta_full(pred_one, &Order::Normal), 0.into());
/// ```
pub fn beta_full(mut term: Term, order: &Order) -> Term {
    term.beta_full(order);
    term
}

/// Performs a single β-reduction on a `Term` using the specified evaluation `Order`, consuming its
/// argument in the process.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::succ;
/// use lambda_calculus::reduction::{beta_once, Order};
///
/// let succ_one = succ().app(1.into());
///
/// assert_eq!(beta_once(succ_one, &Order::Normal), succ().apply(&1.into()).unwrap());
/// ```
pub fn beta_once(mut term: Term, order: &Order) -> Term {
    term.beta_once(order);
    term
}

#[cfg(test)]
mod test {
    use super::*;
    use parser::parse;
    use combinators::i;

    #[test]
    fn normal_order() {
        let reduces_instantly = parse(&"(λλ1)((λλλ((32)1))(λλ2))").unwrap();
        assert_eq!(beta_full(reduces_instantly.clone(), &Normal),
                   beta_once(reduces_instantly, &Normal));

        let should_reduce = parse(&"(λ2)((λ111)(λ111))").unwrap();
        assert_eq!(beta_full(should_reduce, &Normal), Var(1))
    }

    #[test]
    fn call_by_name_order() {
        let mut expr = app(abs(app(i(), Var(1))), app(i(), i()));
        expr.beta_once(&CallByName);
        assert_eq!(expr, app(i(), app(i(), i())));
        expr.beta_once(&CallByName);
        assert_eq!(expr, app(i(), i()));
        expr.beta_once(&CallByName);
        assert_eq!(expr, i());
    }

    #[test]
    fn applicative_order() {
        let expr = parse(&"λ1(((λλλ1)1)((λλ21)1))").unwrap();
        assert_eq!(&format!("{}", beta_once(expr, &Applicative)), "λ1((λλ1)((λλ21)1))");

        let expands = parse(&"(λ2)((λ111)(λ111))").unwrap();
        assert_eq!(&format!("{}", beta_once(expands, &Applicative)), "(λ2)((λ111)(λ111)(λ111))")
    }

    #[test]
    fn call_by_value_order() {
        let mut expr = app(abs(app(i(), Var(1))), app(i(), i()));
        expr.beta_once(&CallByValue);
        assert_eq!(expr, app(abs(app(i(), Var(1))), i()));
        expr.beta_once(&CallByValue);
        assert_eq!(expr, app(i(), i()));
        expr.beta_once(&CallByValue);
        assert_eq!(expr, i());
    }
}
<|endoftext|>


<commit_before>use std::any::Any;
use std::ops::Deref;
use std::path::PathBuf;

use input::Input;
use output::Output;
use service::ServiceAccess;
use service::StaticServices;

use hyper::server::request::Request as HyperRequest;
use hyper::server::response::Response as HyperResponse;
use hyper::uri::RequestUri as HyperRequestUri;
use hyper::method::Method as HyperMethod;

pub struct Route {
    pub url: Pattern,
    pub method: MethodsMask,
    pub handler: Box<Handler + Send + Sync>,
}

impl Route {
    /// Returns the route parameters, or `Err` if this route can't handle the given request.
    pub fn matches(&self, request: &HyperRequest) -> Result<Box<Any>, ()> {
        if !self.method.matches(&request.method) {
            return Err(());
        }

        match request.uri {
            HyperRequestUri::AbsolutePath(ref p) => self.url.parse(p),
            _ => Err(())
        }
    }
}

/// Represents a URL pattern.
///
/// Contains a function that will return the route parameters, or `Err` if it doesn't match the
/// request's URL.
pub struct Pattern(pub Box<Fn(&str) -> Result<Box<Any>, ()> + Send + Sync>);

impl Pattern {
    /// Return `Err` if this pattern doesn't match the given URL, otherwise returns the
    /// route parameters.
    pub fn parse(&self, url: &str) -> Result<Box<Any>, ()> {
        (self.0)(url)
    }
}

/// Describes which methods must be used by the request for a route to be used.
pub struct MethodsMask {
    /// True if the `GET` method matches this mask.
    pub get: bool,
    /// True if the `POST` method matches this mask.
    pub post: bool,
    /// True if the `PUT` method matches this mask.
    pub put: bool,
    /// True if the `DELETE` method matches this mask.
    pub delete: bool,
}

impl MethodsMask {
    /// Parses from a string of the `route!` macro.
    pub fn parse(_: &str) -> MethodsMask {
        // FIXME:
        MethodsMask {
            get: true,
            post: false,
            put: false,
            delete: false,
        }
    }

    /// Returns true if the mask contains the specified method.
    pub fn matches(&self, method: &HyperMethod) -> bool {
        match method {
            &HyperMethod::Get => self.get,
            &HyperMethod::Post => self.post,
            &HyperMethod::Put => self.put,
            &HyperMethod::Delete => self.delete,
            _ => false
        }
    }
}

pub struct Router {
    /// List of the routes to try to match. They will be tried in this order.
    pub routes: Vec<Route>,
}

/// Describes types that can process a route.
pub trait Handler {
    /// Handles a request.
    fn call(&self, HyperRequest, HyperResponse, &StaticServices, route_params: &Box<Any>);
}

macro_rules! impl_handler {
    ($($p:ident),*) => (
        impl<'a, I, O $(, $p)*> Handler for fn(I $(, $p)*) -> O
                                            where I: Input, O: Output $(, $p: ServiceAccess<'a>)*
        {
            #[allow(non_snake_case)]
            fn call(&self, request: HyperRequest, response: HyperResponse,
                    services: &StaticServices, route_params: &Box<Any>)
            {
                let input = match I::process(request) {
                    Ok(i) => i,
                    Err(_) => return        // TODO: handle properly
                };

                // TODO: Properly handling lifetimes here would require HKTs which are not
                //       supported by Rust. Considering that services are never destroyed, it's
                //       ok to cast their lifetime to whatever we want ; however there is a danger
                //       with route parameters and this should be fixed.
                $(
                    let $p = $p::load(unsafe { ::std::mem::transmute(services) },
                                      unsafe { ::std::mem::transmute(route_params) });
                )*

                let output = (*self)(input $(, $p)*);
                output.send(response, services);
            }
        }

        impl<'a, I, O $(, $p)*> Handler for Box<Fn(I $(, $p)*) -> O>
                                            where I: Input, O: Output $(, $p: ServiceAccess<'a>)*
        {
            #[allow(non_snake_case)]
            fn call(&self, request: HyperRequest, response: HyperResponse,
                    services: &StaticServices, route_params: &Box<Any>)
            {
                let input = match I::process(request) {
                    Ok(i) => i,
                    Err(_) => return        // TODO: handle properly
                };

                // TODO: Properly handling lifetimes here would require HKTs which are not
                //       supported by Rust. Considering that services are never destroyed, it's
                //       ok to cast their lifetime to whatever we want ; however there is a danger
                //       with route parameters and this should be fixed.
                $(
                    let $p = $p::load(unsafe { ::std::mem::transmute(services) },
                                      unsafe { ::std::mem::transmute(route_params) });
                )*

                let output = (*self)(input $(, $p)*);
                output.send(response, services);
            }
        }
    );
}

impl_handler!();
impl_handler!(S1);
impl_handler!(S1, S2);
impl_handler!(S1, S2, S3);
impl_handler!(S1, S2, S3, S4);
impl_handler!(S1, S2, S3, S4, S5);
impl_handler!(S1, S2, S3, S4, S5, S6);
impl_handler!(S1, S2, S3, S4, S5, S6, S7);
impl_handler!(S1, S2, S3, S4, S5, S6, S7, S8);
impl_handler!(S1, S2, S3, S4, S5, S6, S7, S8, S9);
impl_handler!(S1, S2, S3, S4, S5, S6, S7, S8, S9, S10);
impl_handler!(S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11);

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Params<'a, T: 'a>(&'a T);

impl<'a, T: 'a> ServiceAccess<'a> for Params<'a, T> where T: Any {
    fn load(_: &'a StaticServices, params: &'a Box<Any>) -> Params<'a, T> {
        Params(params.downcast_ref().unwrap())      // TODO: don't panic
    }
}

impl<'a, T> Deref for Params<'a, T> {
    type Target = T;

    fn deref(&self) -> &T {
        self.0
    }
}

// TODO: fix the trailing '/' problem

#[macro_export]
macro_rules! router {
    (__parse_route $routes:ident [$($ex:tt)*] / $($t:tt)+) => (
        {
            let method = router!(__parse_method $($ex)*);
            router!(__parse_route2 $routes method [/] $($t)+)
        }
    );

    (__parse_route $routes:ident [$($ex:tt)*] $f:tt $($t:tt)*) => (
        router!(__parse_route $routes [$($ex)* $f] $($t)*)
    );

    (__parse_route $routes:ident []) => ();

    (__parse_route2 $routes:ident $method:ident [$($ex:tt)*] => $handler:expr, $($t:tt)*) => (
        {
            let pattern = router!(__parse_pattern $($ex)*);
            let handler = Box::new($handler) as Box<$crate::route::Handler + Send + Sync>;
            $routes.push($crate::route::Route {
                url: pattern,
                method: $method,
                handler: handler,
            });

            router!(__parse_route $routes [] $($t)*);
        }
    );

    (__parse_route2 $routes:ident $method:ident [$($ex:tt)*] => $handler:expr) => (
        router!(__parse_route2 $routes $method [$($ex)*] => $handler,)
    );

    (__parse_route2 $routes:ident $method:ident [$($ex:tt)*] $f:tt $($t:tt)*) => (
        router!(__parse_route2 $routes $method [$($ex)* $f] $($t)*)
    );

    (__parse_pattern $($t:tt)*) => (
        $crate::route::Pattern(Box::new(move |input| {
            router!(__parse_pattern_inner (input.trim_right_matches('/')) () $($t)*)
        }))
    );

    (__parse_pattern_inner ($input:expr) () [ $s:ident ]) => (
        {
            if $input.len() != 0 {
                return Err(());
            }

            Ok(Box::new($s))
        }
    );

    (__parse_pattern_inner ($input:expr) ($(, $mem:ident:$val:expr)+) [ $s:ident ]) => (
        {
            if $input.len() != 0 {
                return Err(());
            }

            Ok(Box::new($s {
                $(
                    $mem: match $val.parse() {
                        Ok(r) => r,
                        Err(_) => return Err(())
                    },
                )*
            }))
        }
    );

    (__parse_pattern_inner ($input:expr) ()) => (
        {
            if $input.len() != 0 {
                return Err(());
            }

            Ok(Box::new(()))
        }
    );

    (__parse_pattern_inner ($input:expr) ($($t:tt)*) / [ $s:ident ]) => (
        router!(__parse_pattern_inner ($input) ($($t)*) [$s])
    );

    (__parse_pattern_inner ($input:expr) ($($t:tt)*) /) => (
        router!(__parse_pattern_inner ($($t)*) ($input))
    );

    (__parse_pattern_inner ($input:expr) ($($e:tt)*) / { $val:ident } $($t:tt)*) => (
        {
            if !$input.starts_with('/') {
                return Err(());
            }

            let end = $input[1 ..].find('/').map(|p| p + 1).unwrap_or($input.len());
            let matched = &$input[1 .. end];

            router!(__parse_pattern_inner (&$input[end ..]) ($($e)* , $val: matched) $($t)*)
        }
    );

    (__parse_pattern_inner ($input:expr) ($($e:tt)*) / $val:ident $($t:tt)*) => (
        {
            let s = concat!("/", stringify!($val));
            if !$input.starts_with(s) {
                return Err(());
            }

            router!(__parse_pattern_inner (&$input[s.len() ..]) ($($e)*) $($t)*)
        }
    );

    (__parse_method $($t:tt)*) => (
        // TODO:
        $crate::route::MethodsMask { get: true, post: false, put: false, delete: false }
    );

    // main route
    ($($t:tt)+) => (
        {
            let mut routes = Vec::new();
            router!(__parse_route routes [] $($t)+);
            $crate::route::Router { routes: routes }
        }
    );
}
<commit_msg>Split router! into two macros<commit_after>use std::any::Any;
use std::ops::Deref;
use std::path::PathBuf;

use input::Input;
use output::Output;
use service::ServiceAccess;
use service::StaticServices;

use hyper::server::request::Request as HyperRequest;
use hyper::server::response::Response as HyperResponse;
use hyper::uri::RequestUri as HyperRequestUri;
use hyper::method::Method as HyperMethod;

pub struct Route {
    pub url: Pattern,
    pub method: MethodsMask,
    pub handler: Box<Handler + Send + Sync>,
}

impl Route {
    /// Returns the route parameters, or `Err` if this route can't handle the given request.
    pub fn matches(&self, request: &HyperRequest) -> Result<Box<Any>, ()> {
        if !self.method.matches(&request.method) {
            return Err(());
        }

        match request.uri {
            HyperRequestUri::AbsolutePath(ref p) => self.url.parse(p),
            _ => Err(())
        }
    }
}

/// Represents a URL pattern.
///
/// Contains a function that will return the route parameters, or `Err` if it doesn't match the
/// request's URL.
pub struct Pattern(pub Box<Fn(&str) -> Result<Box<Any>, ()> + Send + Sync>);

impl Pattern {
    /// Return `Err` if this pattern doesn't match the given URL, otherwise returns the
    /// route parameters.
    pub fn parse(&self, url: &str) -> Result<Box<Any>, ()> {
        (self.0)(url)
    }
}

/// Describes which methods must be used by the request for a route to be used.
pub struct MethodsMask {
    /// True if the `GET` method matches this mask.
    pub get: bool,
    /// True if the `POST` method matches this mask.
    pub post: bool,
    /// True if the `PUT` method matches this mask.
    pub put: bool,
    /// True if the `DELETE` method matches this mask.
    pub delete: bool,
}

impl MethodsMask {
    /// Parses from a string of the `route!` macro.
    pub fn parse(_: &str) -> MethodsMask {
        // FIXME:
        MethodsMask {
            get: true,
            post: false,
            put: false,
            delete: false,
        }
    }

    /// Returns true if the mask contains the specified method.
    pub fn matches(&self, method: &HyperMethod) -> bool {
        match method {
            &HyperMethod::Get => self.get,
            &HyperMethod::Post => self.post,
            &HyperMethod::Put => self.put,
            &HyperMethod::Delete => self.delete,
            _ => false
        }
    }
}

pub struct Router {
    /// List of the routes to try to match. They will be tried in this order.
    pub routes: Vec<Route>,
}

/// Describes types that can process a route.
pub trait Handler {
    /// Handles a request.
    fn call(&self, HyperRequest, HyperResponse, &StaticServices, route_params: &Box<Any>);
}

macro_rules! impl_handler {
    ($($p:ident),*) => (
        impl<'a, I, O $(, $p)*> Handler for fn(I $(, $p)*) -> O
                                            where I: Input, O: Output $(, $p: ServiceAccess<'a>)*
        {
            #[allow(non_snake_case)]
            fn call(&self, request: HyperRequest, response: HyperResponse,
                    services: &StaticServices, route_params: &Box<Any>)
            {
                let input = match I::process(request) {
                    Ok(i) => i,
                    Err(_) => return        // TODO: handle properly
                };

                // TODO: Properly handling lifetimes here would require HKTs which are not
                //       supported by Rust. Considering that services are never destroyed, it's
                //       ok to cast their lifetime to whatever we want ; however there is a danger
                //       with route parameters and this should be fixed.
                $(
                    let $p = $p::load(unsafe { ::std::mem::transmute(services) },
                                      unsafe { ::std::mem::transmute(route_params) });
                )*

                let output = (*self)(input $(, $p)*);
                output.send(response, services);
            }
        }

        impl<'a, I, O $(, $p)*> Handler for Box<Fn(I $(, $p)*) -> O>
                                            where I: Input, O: Output $(, $p: ServiceAccess<'a>)*
        {
            #[allow(non_snake_case)]
            fn call(&self, request: HyperRequest, response: HyperResponse,
                    services: &StaticServices, route_params: &Box<Any>)
            {
                let input = match I::process(request) {
                    Ok(i) => i,
                    Err(_) => return        // TODO: handle properly
                };

                // TODO: Properly handling lifetimes here would require HKTs which are not
                //       supported by Rust. Considering that services are never destroyed, it's
                //       ok to cast their lifetime to whatever we want ; however there is a danger
                //       with route parameters and this should be fixed.
                $(
                    let $p = $p::load(unsafe { ::std::mem::transmute(services) },
                                      unsafe { ::std::mem::transmute(route_params) });
                )*

                let output = (*self)(input $(, $p)*);
                output.send(response, services);
            }
        }
    );
}

impl_handler!();
impl_handler!(S1);
impl_handler!(S1, S2);
impl_handler!(S1, S2, S3);
impl_handler!(S1, S2, S3, S4);
impl_handler!(S1, S2, S3, S4, S5);
impl_handler!(S1, S2, S3, S4, S5, S6);
impl_handler!(S1, S2, S3, S4, S5, S6, S7);
impl_handler!(S1, S2, S3, S4, S5, S6, S7, S8);
impl_handler!(S1, S2, S3, S4, S5, S6, S7, S8, S9);
impl_handler!(S1, S2, S3, S4, S5, S6, S7, S8, S9, S10);
impl_handler!(S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11);

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Params<'a, T: 'a>(&'a T);

impl<'a, T: 'a> ServiceAccess<'a> for Params<'a, T> where T: Any {
    fn load(_: &'a StaticServices, params: &'a Box<Any>) -> Params<'a, T> {
        Params(params.downcast_ref().unwrap())      // TODO: don't panic
    }
}

impl<'a, T> Deref for Params<'a, T> {
    type Target = T;

    fn deref(&self) -> &T {
        self.0
    }
}

// TODO: fix the trailing '/' problem

#[macro_export]
macro_rules! router {
    ($($t:tt)+) => (
        {
            let mut routes = Vec::new();
            router_impl!(__parse_route routes [] $($t)+);
            $crate::route::Router { routes: routes }
        }
    );
}

#[macro_export]
macro_rules! router_impl {
    (__parse_route $routes:ident [$($ex:tt)*] / $($t:tt)+) => (
        {
            let method = router_impl!(__parse_method $($ex)*);
            router_impl!(__parse_route2 $routes method [/] $($t)+)
        }
    );

    (__parse_route $routes:ident [$($ex:tt)*] $f:tt $($t:tt)*) => (
        router_impl!(__parse_route $routes [$($ex)* $f] $($t)*)
    );

    (__parse_route $routes:ident []) => ();

    (__parse_route2 $routes:ident $method:ident [$($ex:tt)*] => $handler:expr, $($t:tt)*) => (
        {
            let pattern = router_impl!(__parse_pattern $($ex)*);
            let handler = Box::new($handler) as Box<$crate::route::Handler + Send + Sync>;
            $routes.push($crate::route::Route {
                url: pattern,
                method: $method,
                handler: handler,
            });

            router_impl!(__parse_route $routes [] $($t)*);
        }
    );

    (__parse_route2 $routes:ident $method:ident [$($ex:tt)*] => $handler:expr) => (
        router_impl!(__parse_route2 $routes $method [$($ex)*] => $handler,)
    );

    (__parse_route2 $routes:ident $method:ident [$($ex:tt)*] $f:tt $($t:tt)*) => (
        router_impl!(__parse_route2 $routes $method [$($ex)* $f] $($t)*)
    );

    (__parse_pattern $($t:tt)*) => (
        $crate::route::Pattern(Box::new(move |input| {
            router_impl!(__parse_pattern_inner (input.trim_right_matches('/')) () $($t)*)
        }))
    );

    (__parse_pattern_inner ($input:expr) () [ $s:ident ]) => (
        {
            if $input.len() != 0 {
                return Err(());
            }

            Ok(Box::new($s))
        }
    );

    (__parse_pattern_inner ($input:expr) ($(, $mem:ident:$val:expr)+) [ $s:ident ]) => (
        {
            if $input.len() != 0 {
                return Err(());
            }

            Ok(Box::new($s {
                $(
                    $mem: match $val.parse() {
                        Ok(r) => r,
                        Err(_) => return Err(())
                    },
                )*
            }))
        }
    );

    (__parse_pattern_inner ($input:expr) ()) => (
        {
            if $input.len() != 0 {
                return Err(());
            }

            Ok(Box::new(()))
        }
    );

    (__parse_pattern_inner ($input:expr) ($($t:tt)*) / [ $s:ident ]) => (
        router_impl!(__parse_pattern_inner ($input) ($($t)*) [$s])
    );

    (__parse_pattern_inner ($input:expr) ($($t:tt)*) /) => (
        router_impl!(__parse_pattern_inner ($($t)*) ($input))
    );

    (__parse_pattern_inner ($input:expr) ($($e:tt)*) / { $val:ident } $($t:tt)*) => (
        {
            if !$input.starts_with('/') {
                return Err(());
            }

            let end = $input[1 ..].find('/').map(|p| p + 1).unwrap_or($input.len());
            let matched = &$input[1 .. end];

            router_impl!(__parse_pattern_inner (&$input[end ..]) ($($e)* , $val: matched) $($t)*)
        }
    );

    (__parse_pattern_inner ($input:expr) ($($e:tt)*) / $val:ident $($t:tt)*) => (
        {
            let s = concat!("/", stringify!($val));
            if !$input.starts_with(s) {
                return Err(());
            }

            router_impl!(__parse_pattern_inner (&$input[s.len() ..]) ($($e)*) $($t)*)
        }
    );

    (__parse_method $($t:tt)*) => (
        // TODO:
        $crate::route::MethodsMask { get: true, post: false, put: false, delete: false }
    );
}
<|endoftext|>


<commit_before>// (c) 2016 Joost Yervante Damad <joost@productize.be>

use std::fmt;

// get from parent
use ERes;
use err;
use read_file;

pub struct Schematic {
    libraries:Vec<String>
}

impl Schematic {
    fn new() -> Schematic {
        Schematic { libraries:vec![] }
    }

    fn add_library(&mut self, s:&str) {
        self.libraries.push(String::from(s))
    }
}

impl fmt::Display for Schematic {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        try!(writeln!(f, "EESchema Schematic File Version 2"));
        for v in &self.libraries[..] {
            try!(writeln!(f, "LIBS:{}", v))
        }
        Ok(())
    }
}

macro_rules! assume_line {
    ($s:expr, $exp:expr) => (
        if $s.eof() {
            return err("end of file reached")
        }
        if $s.here() != $exp {
            return Err(format!("expected '{}', got '{}'", $exp, $s.here()))
        }
        $s.i += 1;
    )
}

struct ParseState {
    i:usize,
    v:Vec<String>,
}

impl ParseState {
    fn here(&self) -> &String {
        return &self.v[self.i]
    }

    fn next(&mut self) {
        self.i += 1
    }

    fn eof(&self) -> bool {
        self.i >= self.v.len()
    }
}

fn parse(s: &str) -> ERes<Schematic> {
    let mut sch = Schematic::new();
    let v:Vec<&str> = s.lines().collect();
    let v = v.iter().map(|x| String::from(*x)).collect();
    let mut p = ParseState {i:0, v: v };
    assume_line!(p, "EESchema Schematic File Version 2");
    while !p.eof() {
        {
          let s = p.here();
          if !s.starts_with("LIBS:") {
              break
          }
            sch.add_library(&s[5..]);
        }
        p.next();
    }
    assume_line!(p, "EELAYER 25 0");
    assume_line!(p, "EELAYER END");
    while !p.eof() {
        p.next()
    }
    Ok(sch)
}

pub fn parse_str(s: &str) -> Schematic {
    parse(s).unwrap()
}

pub fn parse_file(name: &str) -> Schematic {
    let s = read_file(name).unwrap();
    parse(&s[..]).unwrap()
}

<commit_msg>component & descr WIP<commit_after>// (c) 2016 Joost Yervante Damad <joost@productize.be>

use std::fmt;

// get from parent
use ERes;
use err;
use read_file;

#[derive(Debug)]
pub struct Schematic {
    libraries:Vec<String>,
    description:Description,
    components:Vec<Component>,
}

impl Schematic {
    fn new() -> Schematic {
        Schematic {
            libraries:vec![],
            description:Description::new(),
            components:vec![],
        }
    }

    fn add_library(&mut self, s:&str) {
        self.libraries.push(String::from(s))
    }

    fn set_description(&mut self, d:&Description) {
        self.description.clone_from(d)
    }

    fn append_component(&mut self, c:Component) {
        self.components.push(c)
    }

}

impl fmt::Display for Schematic {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        try!(writeln!(f, "EESchema Schematic File Version 2"));
        for v in &self.libraries[..] {
            try!(writeln!(f, "LIBS:{}", v))
        }
        try!(write!(f, "{}", self.description));
        for v in &self.components[..] {
            try!(write!(f, "{}", v))
        }
        Ok(())
    }
}

macro_rules! assume_line {
    ($s:expr, $exp:expr) => (
        if $s.eof() {
            return err("end of file reached")
        }
        if $s.here() != $exp {
            return Err(format!("expected '{}', got '{}'", $exp, $s.here()))
        }
        $s.i += 1;
    )
}

#[derive(Debug)]
struct ParseState {
    i:usize,
    v:Vec<String>,
}

impl ParseState {
    fn here(&self) -> &String {
        return &self.v[self.i]
    }

    fn next(&mut self) -> () {
        self.i += 1
    }

    fn eof(&self) -> bool {
        self.i >= self.v.len()
    }
    fn parse_description(&mut self) -> ERes<Description> {
        Ok(Description::new())
    }
    fn parse_component(&mut self) -> ERes<Component> {
        Ok(Component::new())
    }
}

#[derive(Debug,Clone)]
pub struct Description {
    size:String
}

impl Description {
    fn new() -> Description {
        Description { size:String::from("A4") }
    }
}

impl fmt::Display for Description {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        try!(writeln!(f, "$Descr {}", self.size));
        writeln!(f, "$EndDescr")
    }
}

#[derive(Debug)]
struct Component {
    name:String,
    reference:String,
}

impl Component {
    fn new() -> Component {
        Component {
            name:String::from("EFM32HG310"),
            reference:String::from("U1"),
        }
    }
}

impl fmt::Display for Component {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        try!(writeln!(f, "$Comp"));
        try!(writeln!(f, "L {} {}", self.name, self.reference));
        writeln!(f, "$EndComp")
    }
}

fn parse(s: &str) -> ERes<Schematic> {
    let mut sch = Schematic::new();
    let v:Vec<&str> = s.lines().collect();
    let v = v.iter().map(|x| String::from(*x)).collect();
    let mut p = ParseState {i:0, v: v };
    assume_line!(p, "EESchema Schematic File Version 2");
    while !p.eof() {
        {
          let s = p.here();
          if !s.starts_with("LIBS:") {
              break
          }
            sch.add_library(&s[5..]);
        }
        p.next();
    }
    assume_line!(p, "EELAYER 25 0");
    assume_line!(p, "EELAYER END");
    while !p.eof() {
        match &p.here()[..] {
            "$Descr" => {
                let d = try!(p.parse_description());
                sch.set_description(&d)
            },
            "$Comp" => {
                let c = try!(p.parse_component());
                sch.append_component(c)
            },
            _ => println!("TODO: parse other")
        }
        p.next()
    }
    Ok(sch)
}

pub fn parse_str(s: &str) -> Schematic {
    parse(s).unwrap()
}

pub fn parse_file(name: &str) -> Schematic {
    let s = read_file(name).unwrap();
    parse(&s[..]).unwrap()
}

<|endoftext|>