<commit_before>
use syn::parse::{Parse, ParseStream, Result};

/// An empty type that cannot be parsed.
///
/// Used especially with attributes that don't take positional arguments.
#[derive(Debug)]
pub enum NoParams {}
impl Parse for NoParams {
    fn parse( input: ParseStream ) -> Result<Self> {
        Err( input.error( "Attribute does not accept positional parameters" ) )
    }
}

/// Literal string or 'None' if there should be no value.
#[derive(Debug)]
pub enum StrOption {
    Str( ::syn::LitStr ),
    None
}

impl Parse for StrOption {
    fn parse( input: ParseStream ) -> Result<Self> {

        if input.peek( ::syn::LitStr ) {
            return Ok( StrOption::Str( input.parse()? ) );
        }

        let ident : ::syn::Ident = input.parse()?;
        if ident == "None" {
            return Ok( StrOption::None );
        }

        return Err( input.error( "Expected string or `None`" ) );
    }
}

/// Defines intercom attribute parameter parsing.
///
/// ```
/// intercom_attribute!(
///     SomeAttr< SomeAttrParam, Ident > {
///         param1: Ident,
///         param2: LitStr,
///         param3: Expr,
///     }
/// );
/// ```
///
/// Will define structures to parse attribute params such as:
///
/// ```
/// #[some_attr( Ident1, Ident2, Ident3 )]
/// #[some_attr( param1 = SomeIdent, List, Idents )]
/// #[some_attr( param2 = "literal", param3 = expression() + 1 )]
/// ```
macro_rules! intercom_attribute {

    ( $attr:ident < $attr_param:ident, $params:ident > { $( $name:ident : $type:ident, )* } ) => {

        #[allow(non_camel_case_types)]
        #[derive(Debug)]
        enum $attr_param {
            $( $name ( $type ), )*
            args( $params ),
        }

        impl ::syn::parse::Parse for $attr_param {
            fn parse( input : ::syn::parse::ParseStream ) -> ::syn::parse::Result<Self> {

                if ! input.peek2( Token![=] ) {
                    return Ok( $attr_param::args( input.parse()? ) );
                }

                let ident : ::syn::Ident = input.parse()?;
                let _punct : Token![=] = input.parse()?;
                Ok( match ident.to_string().as_ref() {
                    $(
                        stringify!( $name ) => $attr_param::$name( input.parse()? ),
                    )*
                    other => return Err( input.error( format!( "Unexpected parameter: `{}`", other ) ) )
                } )
            }
        }

        struct $attr( Vec<$attr_param> );
        impl ::syn::parse::Parse for $attr {
            fn parse( input : ::syn::parse::ParseStream ) -> ::syn::parse::Result<Self> {

                // When parsing #[foo(bar)] attributes with syn, syn will include
                // the parenthess in the tts:
                // > ( bar )
                let params = match ::syn::group::parse_parens( &input ) {
                    Ok( parens ) => parens.content.call(
                        ::syn::punctuated::Punctuated::<$attr_param, ::syn::token::Comma>::parse_terminated )?,
                    Err( _ ) => input.call(
                        ::syn::punctuated::Punctuated::<$attr_param, ::syn::token::Comma>::parse_terminated )?,
                };

                Ok( $attr( params.into_iter().collect() ) )
            }
        }

        #[allow(dead_code)]
        impl $attr {
            $(
                pub fn $name( &self ) -> Result< Option< &$type >, String >
                {
                    let v = self.0.iter().filter_map( |p| match p {
                        $attr_param::$name( v ) => Some( v ),
                        _ => None
                    } ).take( 2 ).collect::<Vec<_>>();

                    match v.len() {
                        0 => Ok( None ),
                        1 => Ok( Some( &v[0] ) ),
                        _ => Err( format!(
                                "Multiple {} arguments", stringify!( $name ) ) )
                    }
                }
            )*

            pub fn args<'a>( &'a self ) -> Vec<&'a $params>
            {
                self.0.iter().filter_map( |p| match p {
                    $attr_param::args( v ) => Some( v ),
                    _ => None
                } ).collect()
            }
        }
    }
}

<commit_msg>Fix a clippy issue<commit_after>
use syn::parse::{Parse, ParseStream, Result};

/// An empty type that cannot be parsed.
///
/// Used especially with attributes that don't take positional arguments.
#[derive(Debug)]
pub enum NoParams {}
impl Parse for NoParams {
    fn parse( input: ParseStream ) -> Result<Self> {
        Err( input.error( "Attribute does not accept positional parameters" ) )
    }
}

/// Literal string or 'None' if there should be no value.
#[derive(Debug)]
pub enum StrOption {
    Str( ::syn::LitStr ),
    None
}

impl Parse for StrOption {
    fn parse( input: ParseStream ) -> Result<Self> {

        if input.peek( ::syn::LitStr ) {
            return Ok( StrOption::Str( input.parse()? ) );
        }

        let ident : ::syn::Ident = input.parse()?;
        if ident == "None" {
            return Ok( StrOption::None );
        }

        Err( input.error( "Expected string or `None`" ) )
    }
}

/// Defines intercom attribute parameter parsing.
///
/// ```
/// intercom_attribute!(
///     SomeAttr< SomeAttrParam, Ident > {
///         param1: Ident,
///         param2: LitStr,
///         param3: Expr,
///     }
/// );
/// ```
///
/// Will define structures to parse attribute params such as:
///
/// ```
/// #[some_attr( Ident1, Ident2, Ident3 )]
/// #[some_attr( param1 = SomeIdent, List, Idents )]
/// #[some_attr( param2 = "literal", param3 = expression() + 1 )]
/// ```
macro_rules! intercom_attribute {

    ( $attr:ident < $attr_param:ident, $params:ident > { $( $name:ident : $type:ident, )* } ) => {

        #[allow(non_camel_case_types)]
        #[derive(Debug)]
        enum $attr_param {
            $( $name ( $type ), )*
            args( $params ),
        }

        impl ::syn::parse::Parse for $attr_param {
            fn parse( input : ::syn::parse::ParseStream ) -> ::syn::parse::Result<Self> {

                if ! input.peek2( Token![=] ) {
                    return Ok( $attr_param::args( input.parse()? ) );
                }

                let ident : ::syn::Ident = input.parse()?;
                let _punct : Token![=] = input.parse()?;
                Ok( match ident.to_string().as_ref() {
                    $(
                        stringify!( $name ) => $attr_param::$name( input.parse()? ),
                    )*
                    other => return Err( input.error( format!( "Unexpected parameter: `{}`", other ) ) )
                } )
            }
        }

        struct $attr( Vec<$attr_param> );
        impl ::syn::parse::Parse for $attr {
            fn parse( input : ::syn::parse::ParseStream ) -> ::syn::parse::Result<Self> {

                // When parsing #[foo(bar)] attributes with syn, syn will include
                // the parenthess in the tts:
                // > ( bar )
                let params = match ::syn::group::parse_parens( &input ) {
                    Ok( parens ) => parens.content.call(
                        ::syn::punctuated::Punctuated::<$attr_param, ::syn::token::Comma>::parse_terminated )?,
                    Err( _ ) => input.call(
                        ::syn::punctuated::Punctuated::<$attr_param, ::syn::token::Comma>::parse_terminated )?,
                };

                Ok( $attr( params.into_iter().collect() ) )
            }
        }

        #[allow(dead_code)]
        impl $attr {
            $(
                pub fn $name( &self ) -> Result< Option< &$type >, String >
                {
                    let v = self.0.iter().filter_map( |p| match p {
                        $attr_param::$name( v ) => Some( v ),
                        _ => None
                    } ).take( 2 ).collect::<Vec<_>>();

                    match v.len() {
                        0 => Ok( None ),
                        1 => Ok( Some( &v[0] ) ),
                        _ => Err( format!(
                                "Multiple {} arguments", stringify!( $name ) ) )
                    }
                }
            )*

            pub fn args<'a>( &'a self ) -> Vec<&'a $params>
            {
                self.0.iter().filter_map( |p| match p {
                    $attr_param::args( v ) => Some( v ),
                    _ => None
                } ).collect()
            }
        }
    }
}

<|endoftext|>


<commit_before>extern crate rand;

use std::borrow::Cow;
use std::cmp;
use self::rand::{Rand, Rng};
use parse::{Node, NodeRef, ParsedFile, RangeRef};

#[derive(Debug)]
pub struct FuzzFile<'buf: 'parse, 'parse> {
    root: Cow<'parse, [NodeRef]>,
    nodes: Cow<'parse, [Node<'buf>]>,
    ranges: Cow<'parse, [Vec<NodeRef>]>,
}

#[derive(Clone)]
pub enum FuzzAction {
    DuplicateRange,
    DuplicateRootNode,
    RemoveDelim,
    ShuffleRanges,
    SwapDelim,
    SwapRanges,
}

impl Rand for FuzzAction {
    fn rand<R: Rng>(rng: &mut R) -> Self {
        let actions = vec![
            FuzzAction::DuplicateRange,
            FuzzAction::DuplicateRootNode,
            FuzzAction::RemoveDelim,
            FuzzAction::ShuffleRanges,
            FuzzAction::SwapDelim,
            FuzzAction::SwapRanges,
        ];
        rng.choose(&actions).unwrap().clone()
    }
}

fn rand_indices<R: Rng, T>(mut rng: &mut Rng, x: &[T]) -> (usize, usize) {
    let indices = rand::sample(&mut rng, 0..x.len(), 2);
    (indices[0], indices[1])
}

fn rand_delim<'buf, R: Rng>(mut rng: &mut R, nodes: &[Node<'buf>]) -> Option<(usize, &'buf [u8], RangeRef, &'buf [u8])> {
    let delims: Vec<_> = nodes.iter().enumerate().filter_map(|item| {
        match item {
            (index, &Node::Delim(start_pattern, rangeref, end_pattern)) => {
                Some((index, start_pattern, rangeref, end_pattern))
            },
            _ => None,
        }
    }).collect();
    rng.choose(&delims[..]).cloned()
}

struct SerializeState {
    have_serialized_range: Vec<bool>,
}

impl SerializeState {
    fn new(ranges: &[Vec<NodeRef>]) -> Self {
        SerializeState {
            have_serialized_range: ranges.iter().map(|_| false).collect()
        }
    }

    fn should_serialize(self: &mut Self, rangeref: RangeRef) -> bool {
        if self.have_serialized_range[rangeref] {
            false
        } else {
            self.have_serialized_range[rangeref] = true;
            true
        }
    }

    fn reset(self: &mut Self, rangeref: RangeRef) {
        self.have_serialized_range[rangeref] = false;
    }
}

pub trait SerializeInto {
    fn push(&mut self, &[u8]);
}

impl SerializeInto for Vec<u8> {
    fn push(self: &mut Self, token: &[u8]) {
        self.extend(token);
    }
}

pub struct SliceSerializer<'buf> {
    slice: &'buf mut [u8],
    cur_offset: usize,
}

impl<'buf> SliceSerializer<'buf> {
    pub fn new(slice: &'buf mut [u8]) -> SliceSerializer {
        SliceSerializer {
            slice: slice,
            cur_offset: 0,
        }
    }

    pub fn bytes_written(self: &Self) -> usize {
        self.cur_offset
    }
}

impl<'buf> SerializeInto for SliceSerializer<'buf> {
    fn push(self: &mut Self, token: &[u8]) {
        let remaining = self.slice.len() - self.cur_offset;
        let num_bytes_to_write = cmp::min(remaining, token.len());
        if num_bytes_to_write > 0 {
            self.slice[self.cur_offset..self.cur_offset+num_bytes_to_write].copy_from_slice(&token[..num_bytes_to_write]);
            self.cur_offset += num_bytes_to_write
        }
    }
}

impl<'buf, 'parse> FuzzFile<'buf, 'parse> {
    pub fn new(parsed: &'parse ParsedFile<'buf>) -> Self {
        FuzzFile {
            root: Cow::from(parsed.root.as_slice()),
            nodes: Cow::from(parsed.nodes.as_slice()),
            ranges: Cow::from(parsed.ranges.as_slice()),
        }
    }

    fn serialize_noderef<S: SerializeInto>(self: &Self, noderef: NodeRef, mut state: &mut SerializeState, mut out: &mut S) {
        match self.nodes[noderef] {
            Node::Delim(prefix, rangeref, postfix) => {
                out.push(prefix);
                if state.should_serialize(rangeref) {
                    for noderef in &self.ranges[rangeref] {
                        self.serialize_noderef(*noderef, &mut state, out)
                    }
                    state.reset(rangeref);
                }
                out.push(postfix);
            },
            Node::Range(rangeref) => {
                if state.should_serialize(rangeref) {
                    for noderef in &self.ranges[rangeref] {
                        self.serialize_noderef(*noderef, &mut state, out)
                    }
                    state.reset(rangeref);
                }
            },
            Node::Token(token) => out.push(token),
        }
    }

    pub fn serialize<S: SerializeInto>(self: &Self, out: &mut S) {
        let mut state = SerializeState::new(&self.ranges[..]);

        for noderef in self.root.iter() {
            self.serialize_noderef(*noderef, &mut state, out);
        }
    }

    pub fn swap_ranges<R: Rng>(self: &mut Self, rng: &mut R) -> bool {
        match self.ranges.len() {
            0 => false,
            _ => {
                let mut ranges = self.ranges.to_mut();
                let (index0, index1) = rand_indices::<R, _>(rng, ranges);
                ranges.swap(index0, index1);
                true
            }
        }
    }

    pub fn shuffle_range<R: Rng>(self: &mut Self, rng: &mut R) -> bool {
        match rng.choose_mut(self.ranges.to_mut()) {
            Some(range) => {
                rng.shuffle(range);
                true
            },
            None => false,
        }
    }

    pub fn duplicate_range<R: Rng>(self: &mut Self, rng: &mut R) -> bool {
        match rng.choose_mut(self.ranges.to_mut()) {
            Some(range) => {
                let num_duplications = rng.gen_range(1, 4);
                let mut extension = Vec::new();
                for _ in 0..num_duplications {
                    extension.extend(&range[..])
                }
                range.extend(&extension[..]);
                true
            },
            None => false,
        }
    }

    pub fn duplicate_root_node<R: Rng>(self: &mut Self, rng: &mut R) -> bool {
        match self.nodes.len() {
            0 => false,
            _ => {
                let mut nodes = self.nodes.to_mut();
                let (src_index, dst_index) = rand_indices::<R, _>(rng, nodes);

                let dup_node = nodes[dst_index].clone();
                let noderef = nodes.len();
                nodes.push(dup_node);

                let ranges = self.ranges.to_mut();
                let range = vec![noderef, src_index];
                let rangeref = ranges.len();
                ranges.push(range);

                nodes[dst_index] = Node::Range(rangeref);
                true
            }
        }
    }

    pub fn remove_delim<R: Rng>(self: &mut Self, mut rng: &mut R) -> bool {
        match rand_delim(&mut rng, &self.nodes[..]) {
            Some((index, _, rangeref, _)) => {
                let mut nodes = self.nodes.to_mut();
                nodes[index] = Node::Range(rangeref);
                true
            },
            None => false,
        }
    }

    pub fn swap_delim<R: Rng>(self: &mut Self, mut rng: &mut R) -> bool {
        match rand_delim(&mut rng, &self.nodes[..]) {
            Some((index, start_pattern, rangeref, end_pattern)) => {
                let mut nodes = self.nodes.to_mut();
                nodes[index] = Node::Delim(end_pattern, rangeref, start_pattern);
                true
            },
            None => false,
        }
    }
}

pub fn fuzz_one<'buf, 'parse, R: Rng>(parsed: &'parse ParsedFile<'buf>, mut rng: &mut R, mutations: usize) -> Option<FuzzFile<'buf, 'parse>> {
    let mut ff = FuzzFile::new(parsed);
    let mut did_mutate = false;
    for _ in 0..mutations {
        did_mutate |= match rng.gen() {
            FuzzAction::DuplicateRange => ff.duplicate_range(&mut rng),
            FuzzAction::DuplicateRootNode => ff.duplicate_root_node(&mut rng),
            FuzzAction::RemoveDelim => ff.remove_delim(&mut rng),
            FuzzAction::ShuffleRanges => ff.shuffle_range(&mut rng),
            FuzzAction::SwapDelim => ff.swap_delim(&mut rng),
            FuzzAction::SwapRanges => ff.swap_ranges(&mut rng),
        }
    }

    if did_mutate {
        Some(ff)
    } else {
        None
    }
}
<commit_msg>Fix out of bounds index in rand_indices.<commit_after>extern crate rand;

use std::borrow::Cow;
use std::cmp;
use self::rand::{Rand, Rng};
use parse::{Node, NodeRef, ParsedFile, RangeRef};

#[derive(Debug)]
pub struct FuzzFile<'buf: 'parse, 'parse> {
    root: Cow<'parse, [NodeRef]>,
    nodes: Cow<'parse, [Node<'buf>]>,
    ranges: Cow<'parse, [Vec<NodeRef>]>,
}

#[derive(Clone)]
pub enum FuzzAction {
    DuplicateRange,
    DuplicateRootNode,
    RemoveDelim,
    ShuffleRanges,
    SwapDelim,
    SwapRanges,
}

impl Rand for FuzzAction {
    fn rand<R: Rng>(rng: &mut R) -> Self {
        let actions = vec![
            FuzzAction::DuplicateRange,
            FuzzAction::DuplicateRootNode,
            FuzzAction::RemoveDelim,
            FuzzAction::ShuffleRanges,
            FuzzAction::SwapDelim,
            FuzzAction::SwapRanges,
        ];
        rng.choose(&actions).unwrap().clone()
    }
}

fn rand_indices<R: Rng, T>(mut rng: &mut Rng, x: &[T]) -> Option<(usize, usize)> {
    if x.len() > 1 {
        let indices = rand::sample(&mut rng, 0..x.len(), 2);
        Some((indices[0], indices[1]))
    } else {
        None
    }
}

fn rand_delim<'buf, R: Rng>(mut rng: &mut R, nodes: &[Node<'buf>]) -> Option<(usize, &'buf [u8], RangeRef, &'buf [u8])> {
    let delims: Vec<_> = nodes.iter().enumerate().filter_map(|item| {
        match item {
            (index, &Node::Delim(start_pattern, rangeref, end_pattern)) => {
                Some((index, start_pattern, rangeref, end_pattern))
            },
            _ => None,
        }
    }).collect();
    rng.choose(&delims[..]).cloned()
}

struct SerializeState {
    have_serialized_range: Vec<bool>,
}

impl SerializeState {
    fn new(ranges: &[Vec<NodeRef>]) -> Self {
        SerializeState {
            have_serialized_range: ranges.iter().map(|_| false).collect()
        }
    }

    fn should_serialize(self: &mut Self, rangeref: RangeRef) -> bool {
        if self.have_serialized_range[rangeref] {
            false
        } else {
            self.have_serialized_range[rangeref] = true;
            true
        }
    }

    fn reset(self: &mut Self, rangeref: RangeRef) {
        self.have_serialized_range[rangeref] = false;
    }
}

pub trait SerializeInto {
    fn push(&mut self, &[u8]);
}

impl SerializeInto for Vec<u8> {
    fn push(self: &mut Self, token: &[u8]) {
        self.extend(token);
    }
}

pub struct SliceSerializer<'buf> {
    slice: &'buf mut [u8],
    cur_offset: usize,
}

impl<'buf> SliceSerializer<'buf> {
    pub fn new(slice: &'buf mut [u8]) -> SliceSerializer {
        SliceSerializer {
            slice: slice,
            cur_offset: 0,
        }
    }

    pub fn bytes_written(self: &Self) -> usize {
        self.cur_offset
    }
}

impl<'buf> SerializeInto for SliceSerializer<'buf> {
    fn push(self: &mut Self, token: &[u8]) {
        let remaining = self.slice.len() - self.cur_offset;
        let num_bytes_to_write = cmp::min(remaining, token.len());
        if num_bytes_to_write > 0 {
            self.slice[self.cur_offset..self.cur_offset+num_bytes_to_write].copy_from_slice(&token[..num_bytes_to_write]);
            self.cur_offset += num_bytes_to_write
        }
    }
}

impl<'buf, 'parse> FuzzFile<'buf, 'parse> {
    pub fn new(parsed: &'parse ParsedFile<'buf>) -> Self {
        FuzzFile {
            root: Cow::from(parsed.root.as_slice()),
            nodes: Cow::from(parsed.nodes.as_slice()),
            ranges: Cow::from(parsed.ranges.as_slice()),
        }
    }

    fn serialize_noderef<S: SerializeInto>(self: &Self, noderef: NodeRef, mut state: &mut SerializeState, mut out: &mut S) {
        match self.nodes[noderef] {
            Node::Delim(prefix, rangeref, postfix) => {
                out.push(prefix);
                if state.should_serialize(rangeref) {
                    for noderef in &self.ranges[rangeref] {
                        self.serialize_noderef(*noderef, &mut state, out)
                    }
                    state.reset(rangeref);
                }
                out.push(postfix);
            },
            Node::Range(rangeref) => {
                if state.should_serialize(rangeref) {
                    for noderef in &self.ranges[rangeref] {
                        self.serialize_noderef(*noderef, &mut state, out)
                    }
                    state.reset(rangeref);
                }
            },
            Node::Token(token) => out.push(token),
        }
    }

    pub fn serialize<S: SerializeInto>(self: &Self, out: &mut S) {
        let mut state = SerializeState::new(&self.ranges[..]);

        for noderef in self.root.iter() {
            self.serialize_noderef(*noderef, &mut state, out);
        }
    }

    pub fn swap_ranges<R: Rng>(self: &mut Self, rng: &mut R) -> bool {
        match rand_indices::<R, _>(rng, &self.ranges[..]) {
            Some((index0, index1)) => {
                let mut ranges = self.ranges.to_mut();
                ranges.swap(index0, index1);
                true
            },
            None => false,
        }
    }

    pub fn shuffle_range<R: Rng>(self: &mut Self, rng: &mut R) -> bool {
        match rng.choose_mut(self.ranges.to_mut()) {
            Some(range) => {
                rng.shuffle(range);
                true
            },
            None => false,
        }
    }

    pub fn duplicate_range<R: Rng>(self: &mut Self, rng: &mut R) -> bool {
        match rng.choose_mut(self.ranges.to_mut()) {
            Some(range) => {
                let num_duplications = rng.gen_range(1, 4);
                let mut extension = Vec::new();
                for _ in 0..num_duplications {
                    extension.extend(&range[..])
                }
                range.extend(&extension[..]);
                true
            },
            None => false,
        }
    }

    pub fn duplicate_root_node<R: Rng>(self: &mut Self, rng: &mut R) -> bool {
        match rand_indices::<R, _>(rng, &self.ranges[..]) {
            Some((src_index, dst_index)) => {
                let mut nodes = self.nodes.to_mut();

                let dup_node = nodes[dst_index].clone();
                let noderef = nodes.len();
                nodes.push(dup_node);

                let ranges = self.ranges.to_mut();
                let range = vec![noderef, src_index];
                let rangeref = ranges.len();
                ranges.push(range);

                nodes[dst_index] = Node::Range(rangeref);
                true
            },
            None => false,
        }
    }

    pub fn remove_delim<R: Rng>(self: &mut Self, mut rng: &mut R) -> bool {
        match rand_delim(&mut rng, &self.nodes[..]) {
            Some((index, _, rangeref, _)) => {
                let mut nodes = self.nodes.to_mut();
                nodes[index] = Node::Range(rangeref);
                true
            },
            None => false,
        }
    }

    pub fn swap_delim<R: Rng>(self: &mut Self, mut rng: &mut R) -> bool {
        match rand_delim(&mut rng, &self.nodes[..]) {
            Some((index, start_pattern, rangeref, end_pattern)) => {
                let mut nodes = self.nodes.to_mut();
                nodes[index] = Node::Delim(end_pattern, rangeref, start_pattern);
                true
            },
            None => false,
        }
    }
}

pub fn fuzz_one<'buf, 'parse, R: Rng>(parsed: &'parse ParsedFile<'buf>, mut rng: &mut R, mutations: usize) -> Option<FuzzFile<'buf, 'parse>> {
    let mut ff = FuzzFile::new(parsed);
    let mut did_mutate = false;
    for _ in 0..mutations {
        did_mutate |= match rng.gen() {
            FuzzAction::DuplicateRange => ff.duplicate_range(&mut rng),
            FuzzAction::DuplicateRootNode => ff.duplicate_root_node(&mut rng),
            FuzzAction::RemoveDelim => ff.remove_delim(&mut rng),
            FuzzAction::ShuffleRanges => ff.shuffle_range(&mut rng),
            FuzzAction::SwapDelim => ff.swap_delim(&mut rng),
            FuzzAction::SwapRanges => ff.swap_ranges(&mut rng),
        }
    }

    if did_mutate {
        Some(ff)
    } else {
        None
    }
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::iter::FromIterator;
use std::sync::Arc;
use calx_grid::{Dir6, HexFov};
use calx_ecs::Entity;
use calx_resource::Resource;
use world::World;
use components::{Alignment, BrainState};
use stats::{Intrinsic, Stats};
use location::Location;
use brush::Brush;
use terrain;
use FovStatus;
use fov::SightFov;
use world::Ecs;

/// Immutable querying of game world state.
pub trait Query {
    /// Return the location of an entity.
    ///
    /// Returns the location of the containing entity for entities inside
    /// containers. It is possible for entities to not have a location.
    fn location(&self, e: Entity) -> Option<Location>;

    /// Return the player entity if one exists.
    fn player(&self) -> Option<Entity>;

    /// Return the AI state of an entity.
    fn brain_state(&self, e: Entity) -> Option<BrainState>;

    /// Return current time of the world logic clock.
    fn tick(&self) -> u64;

    /// Return world RNG seed
    fn rng_seed(&self) -> u32;

    /// Return whether the entity is a mobile object (eg. active creature).
    fn is_mob(&self, e: Entity) -> bool;

    /// Return the value for how a mob will react to other mobs.
    fn alignment(&self, e: Entity) -> Option<Alignment>;

    /// Return terrain at location.
    fn terrain(&self, loc: Location) -> Arc<terrain::Tile>;

    /// If location contains a portal, return the destination of the portal.
    fn portal(&self, loc: Location) -> Option<Location>;

    /// Return whether the entity can move in a direction.
    fn can_step(&self, e: Entity, dir: Dir6) -> bool {
        self.location(e).map_or(false, |loc| self.can_enter(e, loc + dir.to_v2()))
    }

    /// Return current health of an entity.
    fn hp(&self, e: Entity) -> i32;

    /// Return maximum health of an entity.
    fn max_hp(&self, e: Entity) -> i32 { self.stats(e).power }

    /// Return field of view for a location.
    fn fov_status(&self, loc: Location) -> Option<FovStatus>;

    /// Return visual brush for an entity.
    fn entity_brush(&self, e: Entity) -> Option<Resource<Brush>>;

    // XXX: Would be nicer if entities_at returned an iterator. Probably want to wait for impl
    // Trait return types before jumping to this.

    /// Return an iterator to entities at the given location.
    fn entities_at(&self, loc: Location) -> Vec<Entity>;

    /// Return reference to the world entity component system.
    fn ecs<'a>(&'a self) -> &'a Ecs;

    /// Return whether location blocks line of sight.
    fn blocks_sight(&self, loc: Location) -> bool { self.terrain(loc).blocks_sight() }

    /// Return a portal if it can be seen through.
    fn visible_portal(&self, loc: Location) -> Option<Location> {
        // Only void-form is transparent to portals.
        if self.terrain(loc).form == terrain::Form::Void { self.portal(loc) } else { None }
    }

    /// Return whether the entity can occupy a location.
    fn can_enter(&self, e: Entity, loc: Location) -> bool {
        if self.is_mob(e) && self.has_mobs(loc) {
            // Can only have one mob per cell.
            return false;
        }
        if self.terrain(loc).is_door() && !self.has_intrinsic(e, Intrinsic::Hands) {
            // Can't open doors without hands.
            return false;
        }
        if self.blocks_walk(loc) {
            return false;
        }
        true
    }

    /// Return the (composite) stats for an entity.
    ///
    /// Will return the default value for the Stats type (additive identity in the stat algebra)
    /// for entities that have no stats component defined.
    fn stats(&self, e: Entity) -> Stats;

    /// Return whether the entity blocks movement of other entities.
    fn is_blocking_entity(&self, e: Entity) -> bool { self.is_mob(e) }

    /// Return whether the location obstructs entity movement.
    fn blocks_walk(&self, loc: Location) -> bool {
        if self.terrain(loc).blocks_walk() {
            return true;
        }
        if self.entities_at(loc)
               .into_iter()
               .any(|e| self.is_blocking_entity(e)) {
            return true;
        }
        false
    }

    /// Return whether a location contains mobs.
    fn has_mobs(&self, loc: Location) -> bool { self.mob_at(loc).is_some() }

    /// Return mob (if any) at given position.
    fn mob_at(&self, loc: Location) -> Option<Entity> {
        self.entities_at(loc).into_iter().find(|&e| self.is_mob(e))
    }

    /// Return the base stats of the entity. Does not include any added effects.
    ///
    /// You usually want to use the `stats` method instead of this one.
    fn base_stats(&self, e: Entity) -> Stats;

    /// Return whether the entity has a specific intrinsic property (eg. poison resistance).
    fn has_intrinsic(&self, e: Entity, intrinsic: Intrinsic) -> bool {
        self.stats(e).intrinsics & intrinsic as u32 != 0
    }

    /// Return if the entity is a mob that should get an update this frame
    /// based on its speed properties. Does not check for status effects like
    /// sleep that might prevent actual action.
    fn ticks_this_frame(&self, e: Entity) -> bool {
        if !self.is_mob(e) || !self.is_alive(e) {
            return false;
        }

        // Go through a cycle of 5 phases to get 4 possible speeds.
        // System idea from Jeff Lait.
        let phase = self.tick() % 5;
        match phase {
            0 => return true,
            1 => return self.has_intrinsic(e, Intrinsic::Fast),
            2 => return true,
            3 => return self.has_intrinsic(e, Intrinsic::Quick),
            4 => return !self.has_intrinsic(e, Intrinsic::Slow),
            _ => panic!("Invalid action phase"),
        }
    }

    /// Return whether the entity is dead and should be removed from the world.
    fn is_alive(&self, e: Entity) -> bool { self.location(e).is_some() }

    /// Return true if the game has ended and the player can make no further
    /// actions.
    fn game_over(&self) -> bool { self.player().is_none() }

    /// Return whether an entity is the player avatar mob.
    fn is_player(&self, e: Entity) -> bool {
        // TODO: Should this just check self.flags.player?
        self.brain_state(e) == Some(BrainState::PlayerControl) && self.is_alive(e)
    }

    /// Return whether the entity is an awake mob.
    fn is_active(&self, e: Entity) -> bool {
        match self.brain_state(e) {
            Some(BrainState::Asleep) => false,
            Some(_) => true,
            _ => false,
        }
    }

    /// Return whether the entity is a mob that will act this frame.
    fn acts_this_frame(&self, e: Entity) -> bool {
        if !self.is_active(e) {
            return false;
        }
        return self.ticks_this_frame(e);
    }

    /// Look for targets to shoot in a direction.
    fn find_target(&self, shooter: Entity, dir: Dir6, range: usize) -> Option<Entity> {
        let origin = self.location(shooter).unwrap();
        for i in 1..(range + 1) {
            let loc = origin + dir.to_v2() * (i as i32);
            if self.terrain(loc).blocks_shot() {
                break;
            }
            if let Some(e) = self.mob_at(loc) {
                if self.is_hostile_to(shooter, e) {
                    return Some(e);
                }
            }
        }
        None
    }

    /// Return whether the entity wants to fight the other entity.
    fn is_hostile_to(&self, e: Entity, other: Entity) -> bool {
        match (self.alignment(e), self.alignment(other)) {
            (Some(Alignment::Chaotic), Some(_)) => true,
            (Some(_), Some(Alignment::Chaotic)) => true,
            (Some(Alignment::Evil), Some(Alignment::Good)) => true,
            (Some(Alignment::Good), Some(Alignment::Evil)) => true,
            _ => false,
        }
    }

    /// Return whether the entity should have an idle animation.
    fn is_bobbing(&self, e: Entity) -> bool { self.is_active(e) && !self.is_player(e) }

    /// Return the field of view chart for visible tiles.
    fn sight_fov(w: &World, origin: Location, range: u32) -> HashSet<Location> {
        HashSet::from_iter(HexFov::new(SightFov::new(w, range, origin))
                               .map(|(pos, a)| a.origin + pos))
    }
}
<commit_msg>Remove redundant check for blocking entities<commit_after>use std::collections::HashSet;
use std::iter::FromIterator;
use std::sync::Arc;
use calx_grid::{Dir6, HexFov};
use calx_ecs::Entity;
use calx_resource::Resource;
use world::World;
use components::{Alignment, BrainState};
use stats::{Intrinsic, Stats};
use location::Location;
use brush::Brush;
use terrain;
use FovStatus;
use fov::SightFov;
use world::Ecs;

/// Immutable querying of game world state.
pub trait Query {
    /// Return the location of an entity.
    ///
    /// Returns the location of the containing entity for entities inside
    /// containers. It is possible for entities to not have a location.
    fn location(&self, e: Entity) -> Option<Location>;

    /// Return the player entity if one exists.
    fn player(&self) -> Option<Entity>;

    /// Return the AI state of an entity.
    fn brain_state(&self, e: Entity) -> Option<BrainState>;

    /// Return current time of the world logic clock.
    fn tick(&self) -> u64;

    /// Return world RNG seed
    fn rng_seed(&self) -> u32;

    /// Return whether the entity is a mobile object (eg. active creature).
    fn is_mob(&self, e: Entity) -> bool;

    /// Return the value for how a mob will react to other mobs.
    fn alignment(&self, e: Entity) -> Option<Alignment>;

    /// Return terrain at location.
    fn terrain(&self, loc: Location) -> Arc<terrain::Tile>;

    /// If location contains a portal, return the destination of the portal.
    fn portal(&self, loc: Location) -> Option<Location>;

    /// Return whether the entity can move in a direction.
    fn can_step(&self, e: Entity, dir: Dir6) -> bool {
        self.location(e).map_or(false, |loc| self.can_enter(e, loc + dir.to_v2()))
    }

    /// Return current health of an entity.
    fn hp(&self, e: Entity) -> i32;

    /// Return maximum health of an entity.
    fn max_hp(&self, e: Entity) -> i32 { self.stats(e).power }

    /// Return field of view for a location.
    fn fov_status(&self, loc: Location) -> Option<FovStatus>;

    /// Return visual brush for an entity.
    fn entity_brush(&self, e: Entity) -> Option<Resource<Brush>>;

    // XXX: Would be nicer if entities_at returned an iterator. Probably want to wait for impl
    // Trait return types before jumping to this.

    /// Return an iterator to entities at the given location.
    fn entities_at(&self, loc: Location) -> Vec<Entity>;

    /// Return reference to the world entity component system.
    fn ecs<'a>(&'a self) -> &'a Ecs;

    /// Return whether location blocks line of sight.
    fn blocks_sight(&self, loc: Location) -> bool { self.terrain(loc).blocks_sight() }

    /// Return a portal if it can be seen through.
    fn visible_portal(&self, loc: Location) -> Option<Location> {
        // Only void-form is transparent to portals.
        if self.terrain(loc).form == terrain::Form::Void { self.portal(loc) } else { None }
    }

    /// Return whether the entity can occupy a location.
    fn can_enter(&self, e: Entity, loc: Location) -> bool {
        if self.terrain(loc).is_door() && !self.has_intrinsic(e, Intrinsic::Hands) {
            // Can't open doors without hands.
            return false;
        }
        if self.blocks_walk(loc) {
            return false;
        }
        true
    }

    /// Return the (composite) stats for an entity.
    ///
    /// Will return the default value for the Stats type (additive identity in the stat algebra)
    /// for entities that have no stats component defined.
    fn stats(&self, e: Entity) -> Stats;

    /// Return whether the entity blocks movement of other entities.
    fn is_blocking_entity(&self, e: Entity) -> bool { self.is_mob(e) }

    /// Return whether the location obstructs entity movement.
    fn blocks_walk(&self, loc: Location) -> bool {
        if self.terrain(loc).blocks_walk() {
            return true;
        }
        if self.entities_at(loc)
               .into_iter()
               .any(|e| self.is_blocking_entity(e)) {
            return true;
        }
        false
    }

    /// Return whether a location contains mobs.
    fn has_mobs(&self, loc: Location) -> bool { self.mob_at(loc).is_some() }

    /// Return mob (if any) at given position.
    fn mob_at(&self, loc: Location) -> Option<Entity> {
        self.entities_at(loc).into_iter().find(|&e| self.is_mob(e))
    }

    /// Return the base stats of the entity. Does not include any added effects.
    ///
    /// You usually want to use the `stats` method instead of this one.
    fn base_stats(&self, e: Entity) -> Stats;

    /// Return whether the entity has a specific intrinsic property (eg. poison resistance).
    fn has_intrinsic(&self, e: Entity, intrinsic: Intrinsic) -> bool {
        self.stats(e).intrinsics & intrinsic as u32 != 0
    }

    /// Return if the entity is a mob that should get an update this frame
    /// based on its speed properties. Does not check for status effects like
    /// sleep that might prevent actual action.
    fn ticks_this_frame(&self, e: Entity) -> bool {
        if !self.is_mob(e) || !self.is_alive(e) {
            return false;
        }

        // Go through a cycle of 5 phases to get 4 possible speeds.
        // System idea from Jeff Lait.
        let phase = self.tick() % 5;
        match phase {
            0 => return true,
            1 => return self.has_intrinsic(e, Intrinsic::Fast),
            2 => return true,
            3 => return self.has_intrinsic(e, Intrinsic::Quick),
            4 => return !self.has_intrinsic(e, Intrinsic::Slow),
            _ => panic!("Invalid action phase"),
        }
    }

    /// Return whether the entity is dead and should be removed from the world.
    fn is_alive(&self, e: Entity) -> bool { self.location(e).is_some() }

    /// Return true if the game has ended and the player can make no further
    /// actions.
    fn game_over(&self) -> bool { self.player().is_none() }

    /// Return whether an entity is the player avatar mob.
    fn is_player(&self, e: Entity) -> bool {
        // TODO: Should this just check self.flags.player?
        self.brain_state(e) == Some(BrainState::PlayerControl) && self.is_alive(e)
    }

    /// Return whether the entity is an awake mob.
    fn is_active(&self, e: Entity) -> bool {
        match self.brain_state(e) {
            Some(BrainState::Asleep) => false,
            Some(_) => true,
            _ => false,
        }
    }

    /// Return whether the entity is a mob that will act this frame.
    fn acts_this_frame(&self, e: Entity) -> bool {
        if !self.is_active(e) {
            return false;
        }
        return self.ticks_this_frame(e);
    }

    /// Look for targets to shoot in a direction.
    fn find_target(&self, shooter: Entity, dir: Dir6, range: usize) -> Option<Entity> {
        let origin = self.location(shooter).unwrap();
        for i in 1..(range + 1) {
            let loc = origin + dir.to_v2() * (i as i32);
            if self.terrain(loc).blocks_shot() {
                break;
            }
            if let Some(e) = self.mob_at(loc) {
                if self.is_hostile_to(shooter, e) {
                    return Some(e);
                }
            }
        }
        None
    }

    /// Return whether the entity wants to fight the other entity.
    fn is_hostile_to(&self, e: Entity, other: Entity) -> bool {
        match (self.alignment(e), self.alignment(other)) {
            (Some(Alignment::Chaotic), Some(_)) => true,
            (Some(_), Some(Alignment::Chaotic)) => true,
            (Some(Alignment::Evil), Some(Alignment::Good)) => true,
            (Some(Alignment::Good), Some(Alignment::Evil)) => true,
            _ => false,
        }
    }

    /// Return whether the entity should have an idle animation.
    fn is_bobbing(&self, e: Entity) -> bool { self.is_active(e) && !self.is_player(e) }

    /// Return the field of view chart for visible tiles.
    fn sight_fov(w: &World, origin: Location, range: u32) -> HashSet<Location> {
        HashSet::from_iter(HexFov::new(SightFov::new(w, range, origin))
                               .map(|(pos, a)| a.origin + pos))
    }
}
<|endoftext|>


<commit_before>use ide_db::{base_db::FileId, defs::Definition, search::FileReference};
use syntax::{
    algo::find_node_at_range,
    ast::{self, HasArgList},
    AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, T,
};

use SyntaxKind::WHITESPACE;

use crate::{
    assist_context::AssistBuilder, utils::next_prev, AssistContext, AssistId, AssistKind, Assists,
};

// Assist: remove_unused_param
//
// Removes unused function parameter.
//
// ```
// fn frobnicate(x: i32$0) {}
//
// fn main() {
//     frobnicate(92);
// }
// ```
// ->
// ```
// fn frobnicate() {}
//
// fn main() {
//     frobnicate();
// }
// ```
pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let param: ast::Param = ctx.find_node_at_offset()?;
    let ident_pat = match param.pat()? {
        ast::Pat::IdentPat(it) => it,
        _ => return None,
    };
    let func = param.syntax().ancestors().find_map(ast::Fn::cast)?;
    let is_self_present =
        param.syntax().parent()?.children().find_map(ast::SelfParam::cast).is_some();

    // check if fn is in impl Trait for ..
    if func
        .syntax()
        .parent() // AssocItemList
        .and_then(|x| x.parent())
        .and_then(ast::Impl::cast)
        .map_or(false, |imp| imp.trait_().is_some())
    {
        cov_mark::hit!(trait_impl);
        return None;
    }

    let mut param_position = func.param_list()?.params().position(|it| it == param)?;
    // param_list() does not take the self param into consideration, hence this additional check
    // is required. For associated functions, param_position is incremented here. For inherent
    // calls we revet the increment below, in process_usage, as those calls will not have an
    // explicit self parameter.
    if is_self_present {
        param_position += 1;
    }
    let fn_def = {
        let func = ctx.sema.to_def(&func)?;
        Definition::Function(func)
    };

    let param_def = {
        let local = ctx.sema.to_def(&ident_pat)?;
        Definition::Local(local)
    };
    if param_def.usages(&ctx.sema).at_least_one() {
        cov_mark::hit!(keep_used);
        return None;
    }
    acc.add(
        AssistId("remove_unused_param", AssistKind::Refactor),
        "Remove unused parameter",
        param.syntax().text_range(),
        |builder| {
            builder.delete(range_to_remove(param.syntax()));
            for (file_id, references) in fn_def.usages(&ctx.sema).all() {
                process_usages(ctx, builder, file_id, references, param_position, is_self_present);
            }
        },
    )
}

fn process_usages(
    ctx: &AssistContext,
    builder: &mut AssistBuilder,
    file_id: FileId,
    references: Vec<FileReference>,
    arg_to_remove: usize,
    is_self_present: bool,
) {
    let source_file = ctx.sema.parse(file_id);
    builder.edit_file(file_id);
    for usage in references {
        if let Some(text_range) = process_usage(&source_file, usage, arg_to_remove, is_self_present)
        {
            builder.delete(text_range);
        }
    }
}

fn process_usage(
    source_file: &SourceFile,
    FileReference { range, .. }: FileReference,
    mut arg_to_remove: usize,
    is_self_present: bool,
) -> Option<TextRange> {
    let call_expr_opt: Option<ast::CallExpr> = find_node_at_range(source_file.syntax(), range);
    if let Some(call_expr) = call_expr_opt {
        let call_expr_range = call_expr.expr()?.syntax().text_range();
        if !call_expr_range.contains_range(range) {
            return None;
        }

        let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?;
        return Some(range_to_remove(arg.syntax()));
    }

    let method_call_expr_opt: Option<ast::MethodCallExpr> =
        find_node_at_range(source_file.syntax(), range);
    if let Some(method_call_expr) = method_call_expr_opt {
        let method_call_expr_range = method_call_expr.name_ref()?.syntax().text_range();
        if !method_call_expr_range.contains_range(range) {
            return None;
        }

        if is_self_present {
            arg_to_remove -= 1;
        }

        let arg = method_call_expr.arg_list()?.args().nth(arg_to_remove)?;
        return Some(range_to_remove(arg.syntax()));
    }

    None
}

pub(crate) fn range_to_remove(node: &SyntaxNode) -> TextRange {
    let up_to_comma = next_prev().find_map(|dir| {
        node.siblings_with_tokens(dir)
            .filter_map(|it| it.into_token())
            .find(|it| it.kind() == T![,])
            .map(|it| (dir, it))
    });
    if let Some((dir, token)) = up_to_comma {
        if node.next_sibling().is_some() {
            let up_to_space = token
                .siblings_with_tokens(dir)
                .skip(1)
                .take_while(|it| it.kind() == WHITESPACE)
                .last()
                .and_then(|it| it.into_token());
            return node
                .text_range()
                .cover(up_to_space.map_or(token.text_range(), |it| it.text_range()));
        }
        node.text_range().cover(token.text_range())
    } else {
        node.text_range()
    }
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable};

    use super::*;

    #[test]
    fn remove_unused() {
        check_assist(
            remove_unused_param,
            r#"
fn a() { foo(9, 2) }
fn foo(x: i32, $0y: i32) { x; }
fn b() { foo(9, 2,) }
"#,
            r#"
fn a() { foo(9) }
fn foo(x: i32) { x; }
fn b() { foo(9, ) }
"#,
        );
    }

    #[test]
    fn remove_unused_first_param() {
        check_assist(
            remove_unused_param,
            r#"
fn foo($0x: i32, y: i32) { y; }
fn a() { foo(1, 2) }
fn b() { foo(1, 2,) }
"#,
            r#"
fn foo(y: i32) { y; }
fn a() { foo(2) }
fn b() { foo(2,) }
"#,
        );
    }

    #[test]
    fn remove_unused_single_param() {
        check_assist(
            remove_unused_param,
            r#"
fn foo($0x: i32) { 0; }
fn a() { foo(1) }
fn b() { foo(1, ) }
"#,
            r#"
fn foo() { 0; }
fn a() { foo() }
fn b() { foo( ) }
"#,
        );
    }

    #[test]
    fn remove_unused_surrounded_by_parms() {
        check_assist(
            remove_unused_param,
            r#"
fn foo(x: i32, $0y: i32, z: i32) { x; }
fn a() { foo(1, 2, 3) }
fn b() { foo(1, 2, 3,) }
"#,
            r#"
fn foo(x: i32, z: i32) { x; }
fn a() { foo(1, 3) }
fn b() { foo(1, 3,) }
"#,
        );
    }

    #[test]
    fn remove_unused_qualified_call() {
        check_assist(
            remove_unused_param,
            r#"
mod bar { pub fn foo(x: i32, $0y: i32) { x; } }
fn b() { bar::foo(9, 2) }
"#,
            r#"
mod bar { pub fn foo(x: i32) { x; } }
fn b() { bar::foo(9) }
"#,
        );
    }

    #[test]
    fn remove_unused_turbofished_func() {
        check_assist(
            remove_unused_param,
            r#"
pub fn foo<T>(x: T, $0y: i32) { x; }
fn b() { foo::<i32>(9, 2) }
"#,
            r#"
pub fn foo<T>(x: T) { x; }
fn b() { foo::<i32>(9) }
"#,
        );
    }

    #[test]
    fn remove_unused_generic_unused_param_func() {
        check_assist(
            remove_unused_param,
            r#"
pub fn foo<T>(x: i32, $0y: T) { x; }
fn b() { foo::<i32>(9, 2) }
fn b2() { foo(9, 2) }
"#,
            r#"
pub fn foo<T>(x: i32) { x; }
fn b() { foo::<i32>(9) }
fn b2() { foo(9) }
"#,
        );
    }

    #[test]
    fn keep_used() {
        cov_mark::check!(keep_used);
        check_assist_not_applicable(
            remove_unused_param,
            r#"
fn foo(x: i32, $0y: i32) { y; }
fn main() { foo(9, 2) }
"#,
        );
    }

    #[test]
    fn trait_impl() {
        cov_mark::check!(trait_impl);
        check_assist_not_applicable(
            remove_unused_param,
            r#"
trait Trait {
    fn foo(x: i32);
}
impl Trait for () {
    fn foo($0x: i32) {}
}
"#,
        );
    }

    #[test]
    fn remove_across_files() {
        check_assist(
            remove_unused_param,
            r#"
//- /main.rs
fn foo(x: i32, $0y: i32) { x; }

mod foo;

//- /foo.rs
use super::foo;

fn bar() {
    let _ = foo(1, 2);
}
"#,
            r#"
//- /main.rs
fn foo(x: i32) { x; }

mod foo;

//- /foo.rs
use super::foo;

fn bar() {
    let _ = foo(1);
}
"#,
        )
    }

    #[test]
    fn test_remove_method_param() {
        check_assist(
            remove_unused_param,
            r#"
struct S;
impl S { fn f(&self, $0_unused: i32) {} }
fn main() {
    S.f(92);
    S.f();
    S.f(93, 92);
    S::f(&S, 92);
}
"#,
            r#"
struct S;
impl S { fn f(&self) {} }
fn main() {
    S.f();
    S.f();
    S.f(92);
    S::f(&S);
}
"#,
        )
    }
}
<commit_msg>fix: Prevent panic in Remove Unused Parameter assist<commit_after>use ide_db::{base_db::FileId, defs::Definition, search::FileReference};
use syntax::{
    algo::find_node_at_range,
    ast::{self, HasArgList},
    AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, T,
};

use SyntaxKind::WHITESPACE;

use crate::{
    assist_context::AssistBuilder, utils::next_prev, AssistContext, AssistId, AssistKind, Assists,
};

// Assist: remove_unused_param
//
// Removes unused function parameter.
//
// ```
// fn frobnicate(x: i32$0) {}
//
// fn main() {
//     frobnicate(92);
// }
// ```
// ->
// ```
// fn frobnicate() {}
//
// fn main() {
//     frobnicate();
// }
// ```
pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let param: ast::Param = ctx.find_node_at_offset()?;
    let ident_pat = match param.pat()? {
        ast::Pat::IdentPat(it) => it,
        _ => return None,
    };
    let func = param.syntax().ancestors().find_map(ast::Fn::cast)?;
    let is_self_present =
        param.syntax().parent()?.children().find_map(ast::SelfParam::cast).is_some();

    // check if fn is in impl Trait for ..
    if func
        .syntax()
        .parent() // AssocItemList
        .and_then(|x| x.parent())
        .and_then(ast::Impl::cast)
        .map_or(false, |imp| imp.trait_().is_some())
    {
        cov_mark::hit!(trait_impl);
        return None;
    }

    let mut param_position = func.param_list()?.params().position(|it| it == param)?;
    // param_list() does not take the self param into consideration, hence this additional check
    // is required. For associated functions, param_position is incremented here. For inherent
    // calls we revet the increment below, in process_usage, as those calls will not have an
    // explicit self parameter.
    if is_self_present {
        param_position += 1;
    }
    let fn_def = {
        let func = ctx.sema.to_def(&func)?;
        Definition::Function(func)
    };

    let param_def = {
        let local = ctx.sema.to_def(&ident_pat)?;
        Definition::Local(local)
    };
    if param_def.usages(&ctx.sema).at_least_one() {
        cov_mark::hit!(keep_used);
        return None;
    }
    acc.add(
        AssistId("remove_unused_param", AssistKind::Refactor),
        "Remove unused parameter",
        param.syntax().text_range(),
        |builder| {
            builder.delete(range_to_remove(param.syntax()));
            for (file_id, references) in fn_def.usages(&ctx.sema).all() {
                process_usages(ctx, builder, file_id, references, param_position, is_self_present);
            }
        },
    )
}

fn process_usages(
    ctx: &AssistContext,
    builder: &mut AssistBuilder,
    file_id: FileId,
    references: Vec<FileReference>,
    arg_to_remove: usize,
    is_self_present: bool,
) {
    let source_file = ctx.sema.parse(file_id);
    builder.edit_file(file_id);
    let possible_ranges = references
        .into_iter()
        .filter_map(|usage| process_usage(&source_file, usage, arg_to_remove, is_self_present));

    let mut ranges_to_delete: Vec<TextRange> = vec![];
    for range in possible_ranges {
        if !ranges_to_delete.iter().any(|it| it.contains_range(range)) {
            ranges_to_delete.push(range)
        }
    }

    for range in ranges_to_delete {
        builder.delete(range)
    }
}

fn process_usage(
    source_file: &SourceFile,
    FileReference { range, .. }: FileReference,
    mut arg_to_remove: usize,
    is_self_present: bool,
) -> Option<TextRange> {
    let call_expr_opt: Option<ast::CallExpr> = find_node_at_range(source_file.syntax(), range);
    if let Some(call_expr) = call_expr_opt {
        let call_expr_range = call_expr.expr()?.syntax().text_range();
        if !call_expr_range.contains_range(range) {
            return None;
        }

        let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?;
        return Some(range_to_remove(arg.syntax()));
    }

    let method_call_expr_opt: Option<ast::MethodCallExpr> =
        find_node_at_range(source_file.syntax(), range);
    if let Some(method_call_expr) = method_call_expr_opt {
        let method_call_expr_range = method_call_expr.name_ref()?.syntax().text_range();
        if !method_call_expr_range.contains_range(range) {
            return None;
        }

        if is_self_present {
            arg_to_remove -= 1;
        }

        let arg = method_call_expr.arg_list()?.args().nth(arg_to_remove)?;
        return Some(range_to_remove(arg.syntax()));
    }

    None
}

pub(crate) fn range_to_remove(node: &SyntaxNode) -> TextRange {
    let up_to_comma = next_prev().find_map(|dir| {
        node.siblings_with_tokens(dir)
            .filter_map(|it| it.into_token())
            .find(|it| it.kind() == T![,])
            .map(|it| (dir, it))
    });
    if let Some((dir, token)) = up_to_comma {
        if node.next_sibling().is_some() {
            let up_to_space = token
                .siblings_with_tokens(dir)
                .skip(1)
                .take_while(|it| it.kind() == WHITESPACE)
                .last()
                .and_then(|it| it.into_token());
            return node
                .text_range()
                .cover(up_to_space.map_or(token.text_range(), |it| it.text_range()));
        }
        node.text_range().cover(token.text_range())
    } else {
        node.text_range()
    }
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable};

    use super::*;

    #[test]
    fn remove_unused() {
        check_assist(
            remove_unused_param,
            r#"
fn a() { foo(9, 2) }
fn foo(x: i32, $0y: i32) { x; }
fn b() { foo(9, 2,) }
"#,
            r#"
fn a() { foo(9) }
fn foo(x: i32) { x; }
fn b() { foo(9, ) }
"#,
        );
    }

    #[test]
    fn remove_unused_first_param() {
        check_assist(
            remove_unused_param,
            r#"
fn foo($0x: i32, y: i32) { y; }
fn a() { foo(1, 2) }
fn b() { foo(1, 2,) }
"#,
            r#"
fn foo(y: i32) { y; }
fn a() { foo(2) }
fn b() { foo(2,) }
"#,
        );
    }

    #[test]
    fn remove_unused_single_param() {
        check_assist(
            remove_unused_param,
            r#"
fn foo($0x: i32) { 0; }
fn a() { foo(1) }
fn b() { foo(1, ) }
"#,
            r#"
fn foo() { 0; }
fn a() { foo() }
fn b() { foo( ) }
"#,
        );
    }

    #[test]
    fn remove_unused_surrounded_by_parms() {
        check_assist(
            remove_unused_param,
            r#"
fn foo(x: i32, $0y: i32, z: i32) { x; }
fn a() { foo(1, 2, 3) }
fn b() { foo(1, 2, 3,) }
"#,
            r#"
fn foo(x: i32, z: i32) { x; }
fn a() { foo(1, 3) }
fn b() { foo(1, 3,) }
"#,
        );
    }

    #[test]
    fn remove_unused_qualified_call() {
        check_assist(
            remove_unused_param,
            r#"
mod bar { pub fn foo(x: i32, $0y: i32) { x; } }
fn b() { bar::foo(9, 2) }
"#,
            r#"
mod bar { pub fn foo(x: i32) { x; } }
fn b() { bar::foo(9) }
"#,
        );
    }

    #[test]
    fn remove_unused_turbofished_func() {
        check_assist(
            remove_unused_param,
            r#"
pub fn foo<T>(x: T, $0y: i32) { x; }
fn b() { foo::<i32>(9, 2) }
"#,
            r#"
pub fn foo<T>(x: T) { x; }
fn b() { foo::<i32>(9) }
"#,
        );
    }

    #[test]
    fn remove_unused_generic_unused_param_func() {
        check_assist(
            remove_unused_param,
            r#"
pub fn foo<T>(x: i32, $0y: T) { x; }
fn b() { foo::<i32>(9, 2) }
fn b2() { foo(9, 2) }
"#,
            r#"
pub fn foo<T>(x: i32) { x; }
fn b() { foo::<i32>(9) }
fn b2() { foo(9) }
"#,
        );
    }

    #[test]
    fn keep_used() {
        cov_mark::check!(keep_used);
        check_assist_not_applicable(
            remove_unused_param,
            r#"
fn foo(x: i32, $0y: i32) { y; }
fn main() { foo(9, 2) }
"#,
        );
    }

    #[test]
    fn trait_impl() {
        cov_mark::check!(trait_impl);
        check_assist_not_applicable(
            remove_unused_param,
            r#"
trait Trait {
    fn foo(x: i32);
}
impl Trait for () {
    fn foo($0x: i32) {}
}
"#,
        );
    }

    #[test]
    fn remove_across_files() {
        check_assist(
            remove_unused_param,
            r#"
//- /main.rs
fn foo(x: i32, $0y: i32) { x; }

mod foo;

//- /foo.rs
use super::foo;

fn bar() {
    let _ = foo(1, 2);
}
"#,
            r#"
//- /main.rs
fn foo(x: i32) { x; }

mod foo;

//- /foo.rs
use super::foo;

fn bar() {
    let _ = foo(1);
}
"#,
        )
    }

    #[test]
    fn test_remove_method_param() {
        check_assist(
            remove_unused_param,
            r#"
struct S;
impl S { fn f(&self, $0_unused: i32) {} }
fn main() {
    S.f(92);
    S.f();
    S.f(93, 92);
    S::f(&S, 92);
}
"#,
            r#"
struct S;
impl S { fn f(&self) {} }
fn main() {
    S.f();
    S.f();
    S.f(92);
    S::f(&S);
}
"#,
        )
    }

    #[test]
    fn nested_call() {
        check_assist(
            remove_unused_param,
            r#"
fn foo(x: i32, $0y: i32) -> i32 {
    x
}

fn bar() {
    foo(1, foo(2, 3));
}
"#,
            r#"
fn foo(x: i32) -> i32 {
    x
}

fn bar() {
    foo(1);
}
"#,
        )
    }
}
<|endoftext|>


<commit_before>use std::sync::{Arc, atomic::{Ordering, AtomicBool}};
use std::net::SocketAddr;
use parking_lot::Mutex;
use tokio::prelude::*;

use blockchain::Blockchain;
use producer::Minter;

use super::peer::PeerType;
use super::client::*;
use super::rpc::*;

pub struct PeerPool {
    peer_addresses: Vec<SocketAddr>,
    peers: Mutex<Vec<PeerState>>
}

impl PeerPool {
    pub fn new(addrs: &[&str]) -> PeerPool {
        let peer_addresses = addrs.into_iter().map(|s| {
            (*s).parse().map_err(|e| {
                format!("failed to parse address: {} {}", *s, e)
            }).unwrap()
        }).collect::<Vec<SocketAddr>>();
        let peers = Mutex::new(Vec::with_capacity(peer_addresses.len()));

        PeerPool {
            peer_addresses,
            peers
        }
    }

    pub fn start(&self, blockchain: &Arc<Blockchain>, minter: &Arc<Option<Minter>>) {
        assert!(self.peers.lock().is_empty(), "peer pool already started");
        for addr in self.peer_addresses.clone() {
            let (tx, rx) = connect_loop(addr, PeerType::NODE);
            let state = PeerState {
                tx,
                rx,
                connected: Arc::new(AtomicBool::new(false))
            };
            let blockchain = Arc::clone(blockchain);
            let minter = Arc::clone(minter);
            self.handle_client_peer(addr, blockchain, minter, state);
        }
    }

    fn handle_client_peer(&self,
                            addr: SocketAddr,
                            blockchain: Arc<Blockchain>,
                            minter: Arc<Option<Minter>>,
                            state: PeerState) {
        macro_rules! quick_send {
            ($state:expr, $id:expr, $msg:expr) => {
                $state.tx.send(ClientEvent::Message(Box::new(RpcPayload {
                    id: $id,
                    msg: Some($msg)
                })))
            };
            ($state:expr, $id:expr) => {
                $state.tx.send(ClientEvent::Message(Box::new(RpcPayload {
                    id: $id,
                    msg: None
                })))
            };
        }

        self.peers.lock().push(state.clone());
        ::tokio::spawn(state.rx.clone().for_each(move |evt| {
            match evt {
                ClientEvent::Connect => {
                    state.connected.store(true, Ordering::Release);
                },
                ClientEvent::Disconnect => {
                    state.connected.store(false, Ordering::Release);
                },
                ClientEvent::Message(rpc) => {
                    let id = rpc.id;
                    let msg = match rpc.msg {
                        Some(msg) => msg,
                        None => return Ok(())
                    };
                    match msg {
                        RpcMsg::Handshake(_) => {
                            warn!("[{}] Invalid handshake message sent from peer", addr);
                        }
                        RpcMsg::Error(_) => {},
                        RpcMsg::Event(evt) => {
                            if let Some(minter) = &*minter {
                                match *evt {
                                    RpcEvent::Block(block) => {
                                        let _ = minter.add_block(block);
                                    },
                                    RpcEvent::Tx(tx) => {
                                        let _ = minter.add_tx(tx);
                                    }
                                }
                            }
                        },
                        RpcMsg::Broadcast(tx) => {
                            if let Some(minter) = &*minter {
                                match minter.add_tx(tx) {
                                    Ok(_) => {
                                        quick_send!(state, id).wait().unwrap();
                                    },
                                    Err(s) => {
                                        quick_send!(state, id, RpcMsg::Error(s)).wait().unwrap();
                                    }
                                }
                            }
                        },
                        RpcMsg::Properties(var) => {
                            if var.req().is_some() {
                                let props = blockchain.get_properties();
                                let var = RpcVariant::Res(props);
                                quick_send!(state, id, RpcMsg::Properties(var)).wait().unwrap();
                            }
                        },
                        RpcMsg::Block(var) => {
                            if let Some(height) = var.req() {
                                let block = match blockchain.get_block(height) {
                                    Some(block) => Some((&*block).clone()),
                                    None => None
                                };
                                let var = Box::new(RpcVariant::Res(block));
                                quick_send!(state, id, RpcMsg::Block(var)).wait().unwrap();
                            }
                        },
                        RpcMsg::Balance(var) => {
                            if let Some(addr) = var.req() {
                                let bal = blockchain.get_balance(&addr);
                                let var = RpcVariant::Res(bal);
                                quick_send!(state, id, RpcMsg::Balance(var)).wait().unwrap();
                            }
                        },
                        RpcMsg::TotalFee(var) => {
                            if let Some(addr) = var.req() {
                                let fee = blockchain.get_total_fee(&addr);
                                match fee {
                                    Some(fee) => {
                                        let var = RpcVariant::Res(fee);
                                        quick_send!(state, id, RpcMsg::TotalFee(var)).wait().unwrap();
                                    },
                                    None => {
                                        let err = "failed to retrieve total fee".to_string();
                                        quick_send!(state, id, RpcMsg::Error(err)).wait().unwrap();
                                    }
                                }
                            }
                        }
                    }
                }
            }
            Ok(())
        }));
    }
}

#[derive(Clone)]
struct PeerState {
    tx: ClientSender,
    rx: ClientReceiver,
    connected: Arc<AtomicBool>
}
<commit_msg>Add get_block function to PeerPool API<commit_after>use std::sync::{Arc, atomic::{Ordering, AtomicUsize, AtomicBool}};
use std::net::SocketAddr;
use parking_lot::Mutex;
use tokio::prelude::*;

use blockchain::{Blockchain, SignedBlock};
use producer::Minter;

use super::peer::PeerType;
use super::client::*;
use super::rpc::*;

pub struct PeerPool {
    peer_addresses: Vec<SocketAddr>,
    peers: Mutex<Vec<PeerState>>,
    peer_iter_count: AtomicUsize
}

impl PeerPool {
    pub fn new(addrs: &[&str]) -> PeerPool {
        let peer_addresses = addrs.into_iter().map(|s| {
            (*s).parse().map_err(|e| {
                format!("failed to parse address: {} {}", *s, e)
            }).unwrap()
        }).collect::<Vec<SocketAddr>>();
        let peers = Mutex::new(Vec::with_capacity(peer_addresses.len()));
        let peer_iter_count = AtomicUsize::new(0);

        PeerPool {
            peer_addresses,
            peers,
            peer_iter_count
        }
    }

    pub fn start(&self, blockchain: &Arc<Blockchain>, minter: &Arc<Option<Minter>>) {
        assert!(self.peers.lock().is_empty(), "peer pool already started");
        for addr in self.peer_addresses.clone() {
            let (tx, rx) = connect_loop(addr, PeerType::NODE);
            let state = PeerState {
                tx,
                rx,
                connected: Arc::new(AtomicBool::new(false)),
                id: Arc::new(AtomicUsize::new(1))
            };
            let blockchain = Arc::clone(blockchain);
            let minter = Arc::clone(minter);
            self.handle_client_peer(addr, blockchain, minter, state);
        }
    }

    pub fn get_block(&self, height: u64) -> Option<impl Future<Item = Option<SignedBlock>, Error = ()>> {
        let peer = self.get_next_peer();
        if let Some(peer) = peer {
            let payload = Box::new(RpcPayload {
                id: peer.id.fetch_add(1, Ordering::AcqRel) as u32,
                msg: Some(RpcMsg::Block(Box::new(RpcVariant::Req(height))))
            });

            let fut = peer.tx.send(ClientEvent::Message(payload)).and_then(|rx| {
                Ok(rx.unwrap())
            }).and_then(|rx| {
                rx
            }).map(|payload| {
                match payload.msg {
                    Some(msg) => {
                        match msg {
                            RpcMsg::Block(var) => match var.res() {
                                Some(block) => block,
                                None => None
                            },
                            _ => None
                        }
                    },
                    None => None
                }
            }).map_err(|_| {
                ()
            });
            return Some(fut)
        }
        None
    }

    fn handle_client_peer(&self,
                            addr: SocketAddr,
                            blockchain: Arc<Blockchain>,
                            minter: Arc<Option<Minter>>,
                            state: PeerState) {
        macro_rules! quick_send {
            ($state:expr, $id:expr, $msg:expr) => {
                $state.tx.send(ClientEvent::Message(Box::new(RpcPayload {
                    id: $id,
                    msg: Some($msg)
                })))
            };
            ($state:expr, $id:expr) => {
                $state.tx.send(ClientEvent::Message(Box::new(RpcPayload {
                    id: $id,
                    msg: None
                })))
            };
        }

        self.peers.lock().push(state.clone());
        ::tokio::spawn(state.rx.clone().for_each(move |evt| {
            match evt {
                ClientEvent::Connect => {
                    state.connected.store(true, Ordering::Release);
                },
                ClientEvent::Disconnect => {
                    state.connected.store(false, Ordering::Release);
                },
                ClientEvent::Message(rpc) => {
                    let id = rpc.id;
                    let msg = match rpc.msg {
                        Some(msg) => msg,
                        None => return Ok(())
                    };
                    match msg {
                        RpcMsg::Handshake(_) => {
                            warn!("[{}] Invalid handshake message sent from peer", addr);
                        }
                        RpcMsg::Error(_) => {},
                        RpcMsg::Event(evt) => {
                            if let Some(minter) = &*minter {
                                match *evt {
                                    RpcEvent::Block(block) => {
                                        let _ = minter.add_block(block);
                                    },
                                    RpcEvent::Tx(tx) => {
                                        let _ = minter.add_tx(tx);
                                    }
                                }
                            }
                        },
                        RpcMsg::Broadcast(tx) => {
                            if let Some(minter) = &*minter {
                                match minter.add_tx(tx) {
                                    Ok(_) => {
                                        quick_send!(state, id).wait().unwrap();
                                    },
                                    Err(s) => {
                                        quick_send!(state, id, RpcMsg::Error(s)).wait().unwrap();
                                    }
                                }
                            }
                        },
                        RpcMsg::Properties(var) => {
                            if var.req().is_some() {
                                let props = blockchain.get_properties();
                                let var = RpcVariant::Res(props);
                                quick_send!(state, id, RpcMsg::Properties(var)).wait().unwrap();
                            }
                        },
                        RpcMsg::Block(var) => {
                            if let Some(height) = var.req() {
                                let block = match blockchain.get_block(height) {
                                    Some(block) => Some((&*block).clone()),
                                    None => None
                                };
                                let var = Box::new(RpcVariant::Res(block));
                                quick_send!(state, id, RpcMsg::Block(var)).wait().unwrap();
                            }
                        },
                        RpcMsg::Balance(var) => {
                            if let Some(addr) = var.req() {
                                let bal = blockchain.get_balance(&addr);
                                let var = RpcVariant::Res(bal);
                                quick_send!(state, id, RpcMsg::Balance(var)).wait().unwrap();
                            }
                        },
                        RpcMsg::TotalFee(var) => {
                            if let Some(addr) = var.req() {
                                let fee = blockchain.get_total_fee(&addr);
                                match fee {
                                    Some(fee) => {
                                        let var = RpcVariant::Res(fee);
                                        quick_send!(state, id, RpcMsg::TotalFee(var)).wait().unwrap();
                                    },
                                    None => {
                                        let err = "failed to retrieve total fee".to_string();
                                        quick_send!(state, id, RpcMsg::Error(err)).wait().unwrap();
                                    }
                                }
                            }
                        }
                    }
                }
            }
            Ok(())
        }));
    }

    fn get_next_peer(&self) -> Option<PeerState> {
        let peers = self.peers.lock();
        let len = peers.len();
        if len < 1 { return None }

        let first_count = self.peer_iter_count.fetch_add(1, Ordering::AcqRel);
        loop {
            let count = self.peer_iter_count.fetch_add(1, Ordering::AcqRel);
            let peer = &peers[count % len];
            if peer.connected.load(Ordering::Acquire) { return Some(peer.clone()) }
            if count == first_count { break; }
        }

        None
    }
}

#[derive(Clone)]
struct PeerState {
    tx: ClientSender,
    rx: ClientReceiver,
    connected: Arc<AtomicBool>,
    id: Arc<AtomicUsize>
}
<|endoftext|>


<commit_before>use business::usecase::{NewEntry, UpdateEntry};
use entities::Entry;

pub fn email_confirmation_email(u_id: &str) -> String {
    format!("Na du Weltverbesserer*,\nwir freuen uns dass du bei der Karte von Morgen mit dabei bist!\n\nBitte bestätige deine Email-Adresse hier:\nhttps://prototyp.kartevonmorgen.org/#/?confirm_email={}.\n\neuphorische Grüße\ndas Karte von Morgen-Team",
    u_id)
}

pub fn new_entry_email(e: &NewEntry, id: &str, categories: Vec<String>) -> String {
    let intro_sentence = "ein neuer Eintrag auf der Karte von Morgen wurde erstellt";
    let entry = Entry {
        id : id.into(),
        title:e.title.clone(),
        description:e.description.clone(),
        street:e.street.clone(),
        zip:e.zip.clone(),
        city:e.city.clone(),
        country :e.country.clone(),
        email:e.email.clone(),
        telephone:e.telephone.clone(),
        homepage:e.homepage.clone(),
        categories:e.categories.clone(),
        lat:0.0,
        lng:0.0,
        created:0,
        version:0,
        license:None
    };
    entry_email(&entry, categories, &e.tags, intro_sentence)
}

pub fn changed_entry_email(e: &UpdateEntry, categories: Vec<String>) -> String {
    let intro_sentence = "folgender Eintrag der Karte von Morgen wurde verändert";
    let entry = Entry {
        id : e.id.clone(),
        title:e.title.clone(),
        description:e.description.clone(),
        street:e.street.clone(),
        zip:e.zip.clone(),
        city:e.city.clone(),
        country :e.country.clone(),
        email:e.email.clone(),
        telephone:e.telephone.clone(),
        homepage:e.homepage.clone(),
        categories:e.categories.clone(),
        lat:0.0,
        lng:0.0,
        created:0,
        version:0,
        license:None
    };
    entry_email(&entry, categories, &e.tags, intro_sentence)
}

pub fn entry_email(e: &Entry, categories: Vec<String>, tags: &Vec<String>, intro_sentence: &str) -> String{
    let category = if categories.len() > 0 { categories[0].clone() } else { "".to_string() };
    let address = vec![
        e.street.clone().unwrap_or("".into()),
        vec![e.zip.clone().unwrap_or("".into()),
            e.city.clone().unwrap_or("".into())].join(" "),
        e.country.clone().unwrap_or("".into())]
        .join(", ");

    format!(
"Hallo,
{introSentence}:\n
{title} ({category})
{description}\n
    Tags: {tags}
    Adresse: {address}
    Webseite: {homepage}
    Email-Adresse: {email}
    Telefon: {telephone}\n
Eintrag anschauen oder bearbeiten:
https://prototyp.kartevonmorgen.org/#/?entry={id}\n
Du kannst dein Abonnement des Kartenbereichs abbestellen indem du dich auf https://prototyp.kartevonmorgen.org einloggst.\n
euphorische Grüße
das Karte von Morgen-Team",
        introSentence = intro_sentence,
        title = &e.title,
        id = &e.id,
        description= &e.description,
        address = address,
        email = e.email.clone().unwrap_or("".into()),
        telephone = e.telephone.clone().unwrap_or("".into()),
        homepage = e.homepage.clone().unwrap_or("".into()),
        category = category,
        tags = tags.join(", "))
}<commit_msg>new(emails): change links in emails from prototype to main app<commit_after>use business::usecase::{NewEntry, UpdateEntry};
use entities::Entry;

pub fn email_confirmation_email(u_id: &str) -> String {
    format!("Na du Weltverbesserer*,\nwir freuen uns dass du bei der Karte von Morgen mit dabei bist!\n\nBitte bestätige deine Email-Adresse hier:\nhttps://prototyp.kartevonmorgen.org/#/?confirm_email={}.\n\neuphorische Grüße\ndas Karte von Morgen-Team",
    u_id)
}

pub fn new_entry_email(e: &NewEntry, id: &str, categories: Vec<String>) -> String {
    let intro_sentence = "ein neuer Eintrag auf der Karte von Morgen wurde erstellt";
    let entry = Entry {
        id : id.into(),
        title:e.title.clone(),
        description:e.description.clone(),
        street:e.street.clone(),
        zip:e.zip.clone(),
        city:e.city.clone(),
        country :e.country.clone(),
        email:e.email.clone(),
        telephone:e.telephone.clone(),
        homepage:e.homepage.clone(),
        categories:e.categories.clone(),
        lat:0.0,
        lng:0.0,
        created:0,
        version:0,
        license:None
    };
    entry_email(&entry, categories, &e.tags, intro_sentence)
}

pub fn changed_entry_email(e: &UpdateEntry, categories: Vec<String>) -> String {
    let intro_sentence = "folgender Eintrag der Karte von Morgen wurde verändert";
    let entry = Entry {
        id : e.id.clone(),
        title:e.title.clone(),
        description:e.description.clone(),
        street:e.street.clone(),
        zip:e.zip.clone(),
        city:e.city.clone(),
        country :e.country.clone(),
        email:e.email.clone(),
        telephone:e.telephone.clone(),
        homepage:e.homepage.clone(),
        categories:e.categories.clone(),
        lat:0.0,
        lng:0.0,
        created:0,
        version:0,
        license:None
    };
    entry_email(&entry, categories, &e.tags, intro_sentence)
}

pub fn entry_email(e: &Entry, categories: Vec<String>, tags: &Vec<String>, intro_sentence: &str) -> String{
    let category = if categories.len() > 0 { categories[0].clone() } else { "".to_string() };
    let address = vec![
        e.street.clone().unwrap_or("".into()),
        vec![e.zip.clone().unwrap_or("".into()),
            e.city.clone().unwrap_or("".into())].join(" "),
        e.country.clone().unwrap_or("".into())]
        .join(", ");

    format!(
"Hallo,
{introSentence}:\n
{title} ({category})
{description}\n
    Tags: {tags}
    Adresse: {address}
    Webseite: {homepage}
    Email-Adresse: {email}
    Telefon: {telephone}\n
Eintrag anschauen oder bearbeiten:
https://kartevonmorgen.org/#/?entry={id}\n
Du kannst dein Abonnement des Kartenbereichs abbestellen indem du dich auf https://kartevonmorgen.org einloggst.\n
euphorische Grüße
das Karte von Morgen-Team",
        introSentence = intro_sentence,
        title = &e.title,
        id = &e.id,
        description= &e.description,
        address = address,
        email = e.email.clone().unwrap_or("".into()),
        telephone = e.telephone.clone().unwrap_or("".into()),
        homepage = e.homepage.clone().unwrap_or("".into()),
        category = category,
        tags = tags.join(", "))
}<|endoftext|>


<commit_before>use std::{error, sync::Arc, thread, time::Duration};

// Logging
use log::{error, info, LevelFilter};
use simple_logger::SimpleLogger;

// Network related
use ureq::{Agent, Response};
use url::Url;

// Parse cmdline arguments
use clap::Parser;

// The main config
#[derive(Debug, Parser)]
#[clap(
    version,
    about = "Service for sending requests to the watchcat backend."
)]
struct Config {
    /// Url where to send requests
    #[clap(short, parse(try_from_str = Url::parse), long, env)]
    url: Url,

    /// HTTP method to use
    #[clap(long, default_value = "POST", env)]
    method: String,

    /// Secret key to use
    #[clap(long, env, hide_env_values = true)]
    key: Option<String>,

    /// Repeat request after interval, with units 'ms', 's', 'm', 'h', e.g. 2m30s
    #[clap(long, parse(try_from_str = humantime::parse_duration), env)]
    repeat: Option<Duration>,

    /// Timeout for http request in seconds
    #[clap(long, env)]
    timeout: Option<u16>,

    /// Check dns every x seconds before first request, for faster inital signal in case of long allowed timeout
    #[clap(long, env)]
    checkdns: Option<u16>,

    /// Do not send uptime in heartbeat requests
    #[clap(long, env)]
    nouptime: bool,

    /// Verbose mode
    #[clap(short, long)]
    verbose: bool,
}

fn main() {
    let cfg = Config::parse(); // Parse arguments

    // Initialize logger
    SimpleLogger::new().init().unwrap();
    if cfg.verbose {
        log::set_max_level(LevelFilter::Info);
    } else {
        log::set_max_level(LevelFilter::Warn);
    }

    info!("{:?}", &cfg);

    // Create HTTP agent
    let mut agent_builder = ureq::builder();
    let user_agent = &*format!("watchcat-service/{}", env!("CARGO_PKG_VERSION"));

    // Add timeout if specified
    if let Some(http_timeout) = cfg.timeout {
        agent_builder = agent_builder.timeout(Duration::from_secs(http_timeout.into()));
    }

    let agent = agent_builder
        .tls_connector(Arc::new(native_tls::TlsConnector::new().unwrap()))
        .build();

    // Preform DNS pre check loop if configured
    match cfg.checkdns {
        Some(interval) if interval > 0 => check_dns(interval, cfg.url.domain().unwrap()),
        _ => (),
    }

    loop {
        // Send the HTTP request
        let resp = send_request(&agent, &cfg, user_agent);
        match resp {
            Ok(resp) => {
                let status = resp.status();
                match resp.into_string() {
                    Ok(resp_str) => info!("Response {status}: \"{resp_str}\""),
                    Err(err) => error!("Error receiving response: {err:?}"),
                }
            }
            Err(err) => error!("Error during request: {err}"),
        }

        if let Some(repeat_interval) = cfg.repeat {
            thread::sleep(repeat_interval);
        } else {
            break;
        }
    }
}

/// Try to resolve the domains DNS entry in a loop until it is succesful.
fn check_dns(interval: u16, domain: &str) {
    info!("Checking DNS");

    loop {
        match dns_lookup::lookup_host(domain) {
            Ok(dns_entries) => {
                info!(
                    "DNS lookup successful: {domain} -> {ip}",
                    ip = dns_entries[0]
                );
                break;
            }
            Err(err) => {
                info!("DNS lookup failed '{err}', retrying in {interval} seconds");
                thread::sleep(Duration::from_secs(interval.into()));
            }
        }
    }
}

/// Send a HTTP request
fn send_request(
    agent: &Agent,
    cfg: &Config,
    user_agent: &str,
) -> Result<Response, Box<dyn error::Error>> {
    let mut request = agent.request(cfg.method.as_str(), cfg.url.as_str());

    // Add current uptime as query parameter
    if !cfg.nouptime {
        request = request.query("uptime", uptime_lib::get()?.as_secs().to_string().as_str());
    }

    // Add the secret key as authorization header
    if let Some(key) = &cfg.key {
        request = request.set("Authorization", format!("Bearer {key}").as_str());
    }

    request = request.set("Content-Length", "0");
    request = request.set("User-Agent", user_agent);

    Ok(request.call()?)
}
<commit_msg>Set default timeout<commit_after>use std::{error, sync::Arc, thread, time::Duration};

// Logging
use log::{error, info, LevelFilter};
use simple_logger::SimpleLogger;

// Network related
use ureq::{Agent, Response};
use url::Url;

// Parse cmdline arguments
use clap::Parser;

// The main config
#[derive(Debug, Parser)]
#[clap(
    version,
    about = "Service for sending requests to the watchcat backend."
)]
struct Config {
    /// Url where to send requests
    #[clap(short, parse(try_from_str = Url::parse), long, env)]
    url: Url,

    /// HTTP method to use
    #[clap(long, default_value = "POST", env)]
    method: String,

    /// Secret key to use
    #[clap(long, env, hide_env_values = true)]
    key: Option<String>,

    /// Repeat request after interval, with units 'ms', 's', 'm', 'h', e.g. 2m30s
    #[clap(long, parse(try_from_str = humantime::parse_duration), env)]
    repeat: Option<Duration>,

    /// Timeout for http request in seconds
    #[clap(long, default_value = "30", env)]
    timeout: u16,

    /// Check dns every x seconds before first request, for faster inital signal in case of long allowed timeout
    #[clap(long, env)]
    checkdns: Option<u16>,

    /// Do not send uptime in heartbeat requests
    #[clap(long, env)]
    nouptime: bool,

    /// Verbose mode
    #[clap(short, long)]
    verbose: bool,
}

fn main() {
    let cfg = Config::parse(); // Parse arguments

    // Initialize logger
    SimpleLogger::new().init().unwrap();
    if cfg.verbose {
        log::set_max_level(LevelFilter::Info);
    } else {
        log::set_max_level(LevelFilter::Warn);
    }

    info!("{:?}", &cfg);

    // Create HTTP agent
    let agent = ureq::builder()
        .user_agent(&*format!("watchcat-service/{}", env!("CARGO_PKG_VERSION")))
        .timeout(Duration::from_secs(cfg.timeout.into()))
        .tls_connector(Arc::new(native_tls::TlsConnector::new().unwrap()))
        .build();

    // Preform DNS pre check loop if configured
    match cfg.checkdns {
        Some(interval) if interval > 0 => check_dns(interval, cfg.url.domain().unwrap()),
        _ => (),
    }

    loop {
        // Send the HTTP request
        let resp = send_request(&agent, &cfg);
        match resp {
            Ok(resp) => {
                let status = resp.status();
                match resp.into_string() {
                    Ok(resp_str) => info!("Response {status}: \"{resp_str}\""),
                    Err(err) => error!("Error receiving response: {err:?}"),
                }
            }
            Err(err) => error!("Error during request: {err}"),
        }

        if let Some(repeat_interval) = cfg.repeat {
            thread::sleep(repeat_interval);
        } else {
            break;
        }
    }
}

/// Try to resolve the domains DNS entry in a loop until it is succesful.
fn check_dns(interval: u16, domain: &str) {
    info!("Checking DNS");

    loop {
        match dns_lookup::lookup_host(domain) {
            Ok(dns_entries) => {
                info!(
                    "DNS lookup successful: {domain} -> {ip}",
                    ip = dns_entries[0]
                );
                break;
            }
            Err(err) => {
                info!("DNS lookup failed '{err}', retrying in {interval} seconds");
                thread::sleep(Duration::from_secs(interval.into()));
            }
        }
    }
}

/// Send a HTTP request
fn send_request(agent: &Agent, cfg: &Config) -> Result<Response, Box<dyn error::Error>> {
    let mut request = agent.request(cfg.method.as_str(), cfg.url.as_str());

    // Add current uptime as query parameter
    if !cfg.nouptime {
        request = request.query("uptime", uptime_lib::get()?.as_secs().to_string().as_str());
    }

    // Add the secret key as authorization header
    if let Some(key) = &cfg.key {
        request = request.set("Authorization", format!("Bearer {key}").as_str());
    }

    request = request.set("Content-Length", "0");

    Ok(request.call()?)
}
<|endoftext|>