<commit_before>// Copyright 2015 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Macros for implementing PipelineInit and PipelineData.

#[macro_export]
macro_rules! gfx_pipeline_inner {
    {
        $( $field:ident: $ty:ty, )*
    } => {
        use $crate::pso::{DataLink, DataBind, Descriptor, InitError, RawDataSet};

        #[derive(Clone, Debug)]
        pub struct Data<R: $crate::Resources> {
            $( pub $field: <$ty as DataBind<R>>::Data, )*
        }

        pub struct Meta {
            $( $field: $ty, )*
        }

        pub struct Init<'a> {
            $( pub $field: <$ty as DataLink<'a>>::Init, )*
        }

        impl<'a> $crate::pso::PipelineInit for Init<'a> {
            type Meta = Meta;
            fn link_to(&self, desc: &mut Descriptor, info: &$crate::ProgramInfo) -> Result<Self::Meta, InitError> {
                let mut meta = Meta {
                    $( $field: <$ty as DataLink<'a>>::new(), )*
                };
                // v#
                for at in &info.vertex_attributes {
                    $(
                        match meta.$field.link_input(at, &self.$field) {
                            Some(Ok(d)) => {
                                assert!(meta.$field.is_active());
                                desc.attributes[at.slot as usize] = Some(d);
                                continue;
                            },
                            Some(Err(fm)) => return Err(
                                InitError::VertexImport(at.slot, Some(fm))
                            ),
                            None => (),
                        }
                    )*
                    return Err(InitError::VertexImport(at.slot, None));
                }
                // c#
                for cb in &info.constant_buffers {
                    $(
                        match meta.$field.link_constant_buffer(cb, &self.$field) {
                            Some(Ok(())) => {
                                assert!(meta.$field.is_active());
                                continue;
                            },
                            Some(Err(_)) => return Err(
                                InitError::ConstantBuffer(cb.slot, Some(()))
                            ),
                            None => (),
                        }
                    )*
                    return Err(InitError::ConstantBuffer(cb.slot, None));
                }
                // global constants
                for gc in &info.globals {
                    $(
                        match meta.$field.link_global_constant(gc, &self.$field) {
                            Some(Ok(())) => {
                                assert!(meta.$field.is_active());
                                continue;
                            },
                            Some(Err(_)) => return Err(
                                InitError::GlobalConstant(gc.location, Some(()))
                            ),
                            None => (),
                        }
                    )*
                    return Err(InitError::GlobalConstant(gc.location, None));
                }
                // t#
                for srv in &info.textures {
                    $(
                        match meta.$field.link_resource_view(srv, &self.$field) {
                            Some(Ok(())) => {
                                assert!(meta.$field.is_active());
                                continue;
                            },
                            Some(Err(_)) => return Err(
                                InitError::ResourceView(srv.slot, Some(()))
                            ),
                            None => (),
                        }
                    )*
                    return Err(InitError::ResourceView(srv.slot, None));
                }
                // u#
                for uav in &info.unordereds {
                    $(
                        match meta.$field.link_unordered_view(uav, &self.$field) {
                            Some(Ok(())) => {
                                assert!(meta.$field.is_active());
                                continue;
                            },
                            Some(Err(_)) => return Err(
                                InitError::UnorderedView(uav.slot, Some(()))
                            ),
                            None => (),
                        }
                    )*
                    return Err(InitError::UnorderedView(uav.slot, None));
                }
                // s#
                for sm in &info.samplers {
                    $(
                        match meta.$field.link_sampler(sm, &self.$field) {
                            Some(()) => {
                                assert!(meta.$field.is_active());
                                continue;
                            },
                            None => (),
                        }
                    )*
                    return Err(InitError::Sampler(sm.slot, None));
                }
                // color targets
                for out in &info.outputs {
                    $(
                        match meta.$field.link_output(out, &self.$field) {
                            Some(Ok(d)) => {
                                assert!(meta.$field.is_active());
                                desc.color_targets[out.slot as usize] = Some(d);
                                continue;
                            },
                            Some(Err(fm)) => return Err(
                                InitError::PixelExport(out.slot, Some(fm))
                            ),
                            None => (),
                        }
                    )*
                    return Err(InitError::PixelExport(out.slot, None));
                }
                if !info.knows_outputs {
                    use $crate::core::shade as s;
                    let mut out = s::OutputVar {
                        name: String::new(),
                        slot: 0,
                        base_type: s::BaseType::F32,
                        container: s::ContainerType::Vector(4),
                    };
                    $(
                        match meta.$field.link_output(&out, &self.$field) {
                            Some(Ok(d)) => {
                                assert!(meta.$field.is_active());
                                desc.color_targets[out.slot as usize] = Some(d);
                                out.slot += 1;
                            },
                            Some(Err(fm)) => return Err(
                                InitError::PixelExport(out.slot, Some(fm))
                            ),
                            None => (),
                        }
                    )*
                }
                // depth-stencil, scissor
                for _ in 0 .. 1 {
                    $(
                        match meta.$field.link_depth_stencil(&self.$field) {
                            Some(d) => {
                                assert!(meta.$field.is_active());
                                desc.depth_stencil = Some(d);
                                continue;
                            },
                            None => (),
                        }
                        if meta.$field.link_scissor() {
                            assert!(meta.$field.is_active());
                            desc.scissor = true;
                            continue;
                        }
                    )*
                }
                // done
                Ok(meta)
            }
        }

        impl<R: $crate::Resources> $crate::pso::PipelineData<R> for Data<R> {
            type Meta = Meta;
            fn bake_to(&self, out: &mut RawDataSet<R>, meta: &Self::Meta, man: &mut $crate::handle::Manager<R>) {
                $(
                    meta.$field.bind_to(out, &self.$field, man);
                )*
            }
        }

        pub struct Bundle<R: $crate::Resources> {
            pub slice: $crate::Slice<R>,
            pub pso: $crate::PipelineState<R, Meta>,
            pub data: Data<R>,
        }

        pub fn bundle<R: $crate::Resources>(slice: $crate::Slice<R>,
                      pso: $crate::PipelineState<R, Meta>, data: Data<R>)
                      -> Bundle<R>
        {
            Bundle {
                slice: slice,
                pso: pso,
                data: data,
            }
        }

        impl<R: $crate::Resources> Bundle<R> {
            pub fn encode<C>(&self, encoder: &mut $crate::Encoder<R, C>) where
                C: $crate::CommandBuffer<R> {
                encoder.draw(&self.slice, &self.pso, &self.data);
            }
        }
    }
}

#[macro_export]
macro_rules! gfx_pipeline_base {
    ($module:ident {
        $( $field:ident: $ty:ty, )*
    }) => {
        pub mod $module {
            use $crate;
            #[allow(unused_imports)]
            use super::*;
            gfx_pipeline_inner!{ $(
                $field: $ty,
            )*}
        }
    }
}

#[macro_export]
macro_rules! gfx_pipeline {
    ($module:ident {
        $( $field:ident: $ty:ty = $value:expr, )*
    }) => {
        pub mod $module {
            use $crate;
            #[allow(unused_imports)]
            use super::*;
            gfx_pipeline_inner!{ $(
                $field: $ty,
            )*}
            pub fn new() -> Init<'static> {
                Init {
                    $( $field: $value, )*
                }
            }
        }
    }
}
<commit_msg>Disable warnings about unused bundle.<commit_after>// Copyright 2015 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Macros for implementing PipelineInit and PipelineData.

#[macro_export]
macro_rules! gfx_pipeline_inner {
    {
        $( $field:ident: $ty:ty, )*
    } => {
        use $crate::pso::{DataLink, DataBind, Descriptor, InitError, RawDataSet};

        #[derive(Clone, Debug)]
        pub struct Data<R: $crate::Resources> {
            $( pub $field: <$ty as DataBind<R>>::Data, )*
        }

        pub struct Meta {
            $( $field: $ty, )*
        }

        pub struct Init<'a> {
            $( pub $field: <$ty as DataLink<'a>>::Init, )*
        }

        impl<'a> $crate::pso::PipelineInit for Init<'a> {
            type Meta = Meta;
            fn link_to(&self, desc: &mut Descriptor, info: &$crate::ProgramInfo) -> Result<Self::Meta, InitError> {
                let mut meta = Meta {
                    $( $field: <$ty as DataLink<'a>>::new(), )*
                };
                // v#
                for at in &info.vertex_attributes {
                    $(
                        match meta.$field.link_input(at, &self.$field) {
                            Some(Ok(d)) => {
                                assert!(meta.$field.is_active());
                                desc.attributes[at.slot as usize] = Some(d);
                                continue;
                            },
                            Some(Err(fm)) => return Err(
                                InitError::VertexImport(at.slot, Some(fm))
                            ),
                            None => (),
                        }
                    )*
                    return Err(InitError::VertexImport(at.slot, None));
                }
                // c#
                for cb in &info.constant_buffers {
                    $(
                        match meta.$field.link_constant_buffer(cb, &self.$field) {
                            Some(Ok(())) => {
                                assert!(meta.$field.is_active());
                                continue;
                            },
                            Some(Err(_)) => return Err(
                                InitError::ConstantBuffer(cb.slot, Some(()))
                            ),
                            None => (),
                        }
                    )*
                    return Err(InitError::ConstantBuffer(cb.slot, None));
                }
                // global constants
                for gc in &info.globals {
                    $(
                        match meta.$field.link_global_constant(gc, &self.$field) {
                            Some(Ok(())) => {
                                assert!(meta.$field.is_active());
                                continue;
                            },
                            Some(Err(_)) => return Err(
                                InitError::GlobalConstant(gc.location, Some(()))
                            ),
                            None => (),
                        }
                    )*
                    return Err(InitError::GlobalConstant(gc.location, None));
                }
                // t#
                for srv in &info.textures {
                    $(
                        match meta.$field.link_resource_view(srv, &self.$field) {
                            Some(Ok(())) => {
                                assert!(meta.$field.is_active());
                                continue;
                            },
                            Some(Err(_)) => return Err(
                                InitError::ResourceView(srv.slot, Some(()))
                            ),
                            None => (),
                        }
                    )*
                    return Err(InitError::ResourceView(srv.slot, None));
                }
                // u#
                for uav in &info.unordereds {
                    $(
                        match meta.$field.link_unordered_view(uav, &self.$field) {
                            Some(Ok(())) => {
                                assert!(meta.$field.is_active());
                                continue;
                            },
                            Some(Err(_)) => return Err(
                                InitError::UnorderedView(uav.slot, Some(()))
                            ),
                            None => (),
                        }
                    )*
                    return Err(InitError::UnorderedView(uav.slot, None));
                }
                // s#
                for sm in &info.samplers {
                    $(
                        match meta.$field.link_sampler(sm, &self.$field) {
                            Some(()) => {
                                assert!(meta.$field.is_active());
                                continue;
                            },
                            None => (),
                        }
                    )*
                    return Err(InitError::Sampler(sm.slot, None));
                }
                // color targets
                for out in &info.outputs {
                    $(
                        match meta.$field.link_output(out, &self.$field) {
                            Some(Ok(d)) => {
                                assert!(meta.$field.is_active());
                                desc.color_targets[out.slot as usize] = Some(d);
                                continue;
                            },
                            Some(Err(fm)) => return Err(
                                InitError::PixelExport(out.slot, Some(fm))
                            ),
                            None => (),
                        }
                    )*
                    return Err(InitError::PixelExport(out.slot, None));
                }
                if !info.knows_outputs {
                    use $crate::core::shade as s;
                    let mut out = s::OutputVar {
                        name: String::new(),
                        slot: 0,
                        base_type: s::BaseType::F32,
                        container: s::ContainerType::Vector(4),
                    };
                    $(
                        match meta.$field.link_output(&out, &self.$field) {
                            Some(Ok(d)) => {
                                assert!(meta.$field.is_active());
                                desc.color_targets[out.slot as usize] = Some(d);
                                out.slot += 1;
                            },
                            Some(Err(fm)) => return Err(
                                InitError::PixelExport(out.slot, Some(fm))
                            ),
                            None => (),
                        }
                    )*
                }
                // depth-stencil, scissor
                for _ in 0 .. 1 {
                    $(
                        match meta.$field.link_depth_stencil(&self.$field) {
                            Some(d) => {
                                assert!(meta.$field.is_active());
                                desc.depth_stencil = Some(d);
                                continue;
                            },
                            None => (),
                        }
                        if meta.$field.link_scissor() {
                            assert!(meta.$field.is_active());
                            desc.scissor = true;
                            continue;
                        }
                    )*
                }
                // done
                Ok(meta)
            }
        }

        impl<R: $crate::Resources> $crate::pso::PipelineData<R> for Data<R> {
            type Meta = Meta;
            fn bake_to(&self, out: &mut RawDataSet<R>, meta: &Self::Meta, man: &mut $crate::handle::Manager<R>) {
                $(
                    meta.$field.bind_to(out, &self.$field, man);
                )*
            }
        }

        #[allow(dead_code)]
        pub struct Bundle<R: $crate::Resources> {
            pub slice: $crate::Slice<R>,
            pub pso: $crate::PipelineState<R, Meta>,
            pub data: Data<R>,
        }

        #[allow(dead_code)]
        pub fn bundle<R: $crate::Resources>(slice: $crate::Slice<R>,
                      pso: $crate::PipelineState<R, Meta>, data: Data<R>)
                      -> Bundle<R>
        {
            Bundle {
                slice: slice,
                pso: pso,
                data: data,
            }
        }

        #[allow(dead_code)]
        impl<R: $crate::Resources> Bundle<R> {
            pub fn encode<C>(&self, encoder: &mut $crate::Encoder<R, C>) where
                C: $crate::CommandBuffer<R> {
                encoder.draw(&self.slice, &self.pso, &self.data);
            }
        }
    }
}

#[macro_export]
macro_rules! gfx_pipeline_base {
    ($module:ident {
        $( $field:ident: $ty:ty, )*
    }) => {
        pub mod $module {
            use $crate;
            #[allow(unused_imports)]
            use super::*;
            gfx_pipeline_inner!{ $(
                $field: $ty,
            )*}
        }
    }
}

#[macro_export]
macro_rules! gfx_pipeline {
    ($module:ident {
        $( $field:ident: $ty:ty = $value:expr, )*
    }) => {
        pub mod $module {
            use $crate;
            #[allow(unused_imports)]
            use super::*;
            gfx_pipeline_inner!{ $(
                $field: $ty,
            )*}
            pub fn new() -> Init<'static> {
                Init {
                    $( $field: $value, )*
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use super::common::{whitespace, INVALID_TITLE_CHARS};
use nom::branch::alt;
use nom::bytes::complete::take_while;
use nom::character::complete::char;
use nom::combinator::{map, recognize};
use nom::multi::separated_list1;
use nom::sequence::tuple;
use nom::IResult;

const SEPARATOR_CHAR: u8 = b'-';

/// Variant of the default parser that looks for episodes fitting a `<title> - <episode>` format.
///
/// ### Implementation Note
///
/// Internally, this variant reverses the supplied string before and after parsing, as it makes it much easier to parse titles correctly.
pub mod title_and_episode {
    use super::{reverse, separator_opt, title, whitespace};
    use crate::local::detect::common::replace_whitespace;
    use nom::combinator::map;
    use nom::sequence::{separated_pair, tuple};
    use nom::IResult;

    pub fn parse<S>(input: S) -> Option<(String, u32)>
    where
        S: AsRef<str>,
    {
        let input = input.as_ref().chars().rev().collect::<String>();

        let (_, (_, _, (title, episode))) =
            tuple((reverse::tags, whitespace, title_and_episode))(&input).ok()?;

        let title = title.chars().rev().collect::<String>();
        let cleaned = replace_whitespace(title);

        Some((cleaned, episode))
    }

    fn title_and_episode(input: &str) -> IResult<&str, (&str, u32)> {
        let result = separated_pair(reverse::episode, separator_opt, title);
        map(result, |(ep, title)| (title, ep))(input)
    }
}

/// Variant of the default parser that looks for episodes fitting a `<title> - <episode> - <desc>` format.
pub mod title_episode_desc {
    use super::{reverse, separator_opt, title, whitespace};
    use crate::local::detect::common::replace_whitespace;
    use nom::bytes::complete::take_till;
    use nom::character::is_digit;
    use nom::combinator::map;
    use nom::sequence::tuple;
    use nom::IResult;

    pub fn parse<S>(input: S) -> Option<(String, u32)>
    where
        S: AsRef<str>,
    {
        let input = input.as_ref().chars().rev().collect::<String>();

        let (_, (_, _, (title, episode))) =
            tuple((reverse::tags, whitespace, title_and_episode))(&input).ok()?;

        let title = title.chars().rev().collect::<String>();
        let cleaned = replace_whitespace(title);

        Some((cleaned, episode))
    }

    fn title_and_episode(input: &str) -> IResult<&str, (&str, u32)> {
        let until_digit = take_till(|c: char| is_digit(c as u8));
        let title_episode = tuple((until_digit, reverse::episode, separator_opt, title));

        map(title_episode, |(_, episode, _, title)| (title, episode))(input)
    }
}

/// Variant of the default parser that looks for episodes fitting a `<episode> - <title>` format.
pub mod episode_and_title {
    use super::{separator_opt, title, whitespace};
    use crate::local::detect::common::{replace_whitespace, tags};
    use nom::character::complete::{char, digit1, one_of};
    use nom::combinator::{map, map_res, opt};
    use nom::sequence::{separated_pair, tuple};
    use nom::IResult;

    pub fn parse<S>(input: S) -> Option<(String, u32)>
    where
        S: AsRef<str>,
    {
        let input = input.as_ref();

        let (_, (_, _, (episode, title))) =
            tuple((tags, whitespace, episode_and_title))(input).ok()?;

        let title = replace_whitespace(title);

        Some((title, episode))
    }

    fn episode_and_title(input: &str) -> IResult<&str, (u32, &str)> {
        separated_pair(episode, separator_opt, title)(input)
    }

    fn episode(input: &str) -> IResult<&str, u32> {
        let ep = map_res(digit1, |s: &str| s.parse::<u32>());

        let season_marker = tuple((char('S'), digit1));
        let ep_marker = tuple((opt(season_marker), char('E')));
        let version_suffix = map(tuple((one_of("vV"), digit1)), |_| ());

        let parsed_episode = tuple((opt(ep_marker), ep, opt(version_suffix)));

        map(parsed_episode, |(_, ep, _)| ep)(input)
    }
}

fn title(input: &str) -> IResult<&str, &str> {
    use nom::{error::ErrorKind, Err};

    let title = take_while(|ch| {
        let ch = ch as u8;
        !INVALID_TITLE_CHARS.contains(&ch) && ch != SEPARATOR_CHAR
    });

    let mut result = separated_list1(separator, title);
    let (slice, fragments) = result(input)?;

    let has_digit_fragment = fragments
        .into_iter()
        .any(|frag| !frag.chars().any(|ch| ch.is_alphabetic()));

    if has_digit_fragment {
        return Err(Err::Error((slice, ErrorKind::SeparatedList)));
    }

    recognize(result)(input)
}

fn separator(input: &str) -> IResult<&str, ()> {
    let dash_char = map(char(SEPARATOR_CHAR as char), |_| ());
    map(tuple((whitespace, dash_char, whitespace)), |_| ())(input)
}

fn separator_opt(input: &str) -> IResult<&str, ()> {
    alt((separator, whitespace))(input)
}

mod reverse {
    use super::whitespace;
    use nom::branch::alt;
    use nom::bytes::complete::{is_not, tag_no_case};
    use nom::character::complete::{char, digit1, one_of};
    use nom::combinator::{map, map_res, opt};
    use nom::multi::many0;
    use nom::sequence::{delimited, tuple};
    use nom::IResult;

    pub fn tags(input: &str) -> IResult<&str, ()> {
        map(many0(tag), |_| ())(input)
    }

    pub fn tag(input: &str) -> IResult<&str, ()> {
        let surrounding = tuple((whitespace, metadata_block, whitespace));
        map(surrounding, |_| ())(input)
    }

    pub fn metadata_block(input: &str) -> IResult<&str, &str> {
        alt((brackets, parens))(input)
    }

    pub fn parens(input: &str) -> IResult<&str, &str> {
        delimited(char(')'), is_not("("), char('('))(input)
    }

    pub fn brackets(input: &str) -> IResult<&str, &str> {
        delimited(char(']'), is_not("["), char('['))(input)
    }

    pub fn episode(input: &str) -> IResult<&str, u32> {
        let ep = map_res(digit1, |s: &str| {
            let rev = s.chars().rev().collect::<String>();
            rev.parse::<u32>()
        });

        // These look for one of the following formats:
        // S<season>E<episode>
        // Ep <episode>
        // Episode <episode>
        let prefix = {
            let season_marker = map(tuple((one_of("Ee"), digit1, one_of("Ss"))), |_| ());
            let ep_prefix = map(
                tuple((
                    whitespace,
                    // Reverse of "isode"
                    opt(tag_no_case("edosi")),
                    // Reverse of "ep"
                    tag_no_case("pe"),
                )),
                |_| (),
            );
            let e_prefix = map(one_of("Ee"), |_| ());
            alt((season_marker, ep_prefix, e_prefix))
        };

        let version_suffix = map(tuple((digit1, one_of("vV"))), |_| ());
        let parsed_episode = tuple((opt(version_suffix), ep, opt(prefix)));

        map(parsed_episode, |(_, ep, _)| ep)(input)
    }
}
<commit_msg>anime: Update documentation for title_episode_desc module<commit_after>use super::common::{whitespace, INVALID_TITLE_CHARS};
use nom::branch::alt;
use nom::bytes::complete::take_while;
use nom::character::complete::char;
use nom::combinator::{map, recognize};
use nom::multi::separated_list1;
use nom::sequence::tuple;
use nom::IResult;

const SEPARATOR_CHAR: u8 = b'-';

/// Variant of the default parser that looks for episodes fitting a `<title> - <episode>` format.
///
/// ### Implementation Note
///
/// Internally, this variant reverses the supplied string before and after parsing, as it makes it much easier to parse titles correctly.
pub mod title_and_episode {
    use super::{reverse, separator_opt, title, whitespace};
    use crate::local::detect::common::replace_whitespace;
    use nom::combinator::map;
    use nom::sequence::{separated_pair, tuple};
    use nom::IResult;

    pub fn parse<S>(input: S) -> Option<(String, u32)>
    where
        S: AsRef<str>,
    {
        let input = input.as_ref().chars().rev().collect::<String>();

        let (_, (_, _, (title, episode))) =
            tuple((reverse::tags, whitespace, title_and_episode))(&input).ok()?;

        let title = title.chars().rev().collect::<String>();
        let cleaned = replace_whitespace(title);

        Some((cleaned, episode))
    }

    fn title_and_episode(input: &str) -> IResult<&str, (&str, u32)> {
        let result = separated_pair(reverse::episode, separator_opt, title);
        map(result, |(ep, title)| (title, ep))(input)
    }
}

/// Variant of the default parser that looks for episodes fitting a `<title> - <episode> - <desc>` format.
///
/// ### Implementation Note
///
/// Internally, this variant reverses the supplied string before and after parsing, as it makes it much easier to parse titles correctly.
pub mod title_episode_desc {
    use super::{reverse, separator_opt, title, whitespace};
    use crate::local::detect::common::replace_whitespace;
    use nom::bytes::complete::take_till;
    use nom::character::is_digit;
    use nom::combinator::map;
    use nom::sequence::tuple;
    use nom::IResult;

    pub fn parse<S>(input: S) -> Option<(String, u32)>
    where
        S: AsRef<str>,
    {
        let input = input.as_ref().chars().rev().collect::<String>();

        let (_, (_, _, (title, episode))) =
            tuple((reverse::tags, whitespace, title_and_episode))(&input).ok()?;

        let title = title.chars().rev().collect::<String>();
        let cleaned = replace_whitespace(title);

        Some((cleaned, episode))
    }

    fn title_and_episode(input: &str) -> IResult<&str, (&str, u32)> {
        let until_digit = take_till(|c: char| is_digit(c as u8));
        let title_episode = tuple((until_digit, reverse::episode, separator_opt, title));

        map(title_episode, |(_, episode, _, title)| (title, episode))(input)
    }
}

/// Variant of the default parser that looks for episodes fitting a `<episode> - <title>` format.
pub mod episode_and_title {
    use super::{separator_opt, title, whitespace};
    use crate::local::detect::common::{replace_whitespace, tags};
    use nom::character::complete::{char, digit1, one_of};
    use nom::combinator::{map, map_res, opt};
    use nom::sequence::{separated_pair, tuple};
    use nom::IResult;

    pub fn parse<S>(input: S) -> Option<(String, u32)>
    where
        S: AsRef<str>,
    {
        let input = input.as_ref();

        let (_, (_, _, (episode, title))) =
            tuple((tags, whitespace, episode_and_title))(input).ok()?;

        let title = replace_whitespace(title);

        Some((title, episode))
    }

    fn episode_and_title(input: &str) -> IResult<&str, (u32, &str)> {
        separated_pair(episode, separator_opt, title)(input)
    }

    fn episode(input: &str) -> IResult<&str, u32> {
        let ep = map_res(digit1, |s: &str| s.parse::<u32>());

        let season_marker = tuple((char('S'), digit1));
        let ep_marker = tuple((opt(season_marker), char('E')));
        let version_suffix = map(tuple((one_of("vV"), digit1)), |_| ());

        let parsed_episode = tuple((opt(ep_marker), ep, opt(version_suffix)));

        map(parsed_episode, |(_, ep, _)| ep)(input)
    }
}

fn title(input: &str) -> IResult<&str, &str> {
    use nom::{error::ErrorKind, Err};

    let title = take_while(|ch| {
        let ch = ch as u8;
        !INVALID_TITLE_CHARS.contains(&ch) && ch != SEPARATOR_CHAR
    });

    let mut result = separated_list1(separator, title);
    let (slice, fragments) = result(input)?;

    let has_digit_fragment = fragments
        .into_iter()
        .any(|frag| !frag.chars().any(|ch| ch.is_alphabetic()));

    if has_digit_fragment {
        return Err(Err::Error((slice, ErrorKind::SeparatedList)));
    }

    recognize(result)(input)
}

fn separator(input: &str) -> IResult<&str, ()> {
    let dash_char = map(char(SEPARATOR_CHAR as char), |_| ());
    map(tuple((whitespace, dash_char, whitespace)), |_| ())(input)
}

fn separator_opt(input: &str) -> IResult<&str, ()> {
    alt((separator, whitespace))(input)
}

mod reverse {
    use super::whitespace;
    use nom::branch::alt;
    use nom::bytes::complete::{is_not, tag_no_case};
    use nom::character::complete::{char, digit1, one_of};
    use nom::combinator::{map, map_res, opt};
    use nom::multi::many0;
    use nom::sequence::{delimited, tuple};
    use nom::IResult;

    pub fn tags(input: &str) -> IResult<&str, ()> {
        map(many0(tag), |_| ())(input)
    }

    pub fn tag(input: &str) -> IResult<&str, ()> {
        let surrounding = tuple((whitespace, metadata_block, whitespace));
        map(surrounding, |_| ())(input)
    }

    pub fn metadata_block(input: &str) -> IResult<&str, &str> {
        alt((brackets, parens))(input)
    }

    pub fn parens(input: &str) -> IResult<&str, &str> {
        delimited(char(')'), is_not("("), char('('))(input)
    }

    pub fn brackets(input: &str) -> IResult<&str, &str> {
        delimited(char(']'), is_not("["), char('['))(input)
    }

    pub fn episode(input: &str) -> IResult<&str, u32> {
        let ep = map_res(digit1, |s: &str| {
            let rev = s.chars().rev().collect::<String>();
            rev.parse::<u32>()
        });

        // These look for one of the following formats:
        // S<season>E<episode>
        // Ep <episode>
        // Episode <episode>
        let prefix = {
            let season_marker = map(tuple((one_of("Ee"), digit1, one_of("Ss"))), |_| ());
            let ep_prefix = map(
                tuple((
                    whitespace,
                    // Reverse of "isode"
                    opt(tag_no_case("edosi")),
                    // Reverse of "ep"
                    tag_no_case("pe"),
                )),
                |_| (),
            );
            let e_prefix = map(one_of("Ee"), |_| ());
            alt((season_marker, ep_prefix, e_prefix))
        };

        let version_suffix = map(tuple((digit1, one_of("vV"))), |_| ());
        let parsed_episode = tuple((opt(version_suffix), ep, opt(prefix)));

        map(parsed_episode, |(_, ep, _)| ep)(input)
    }
}
<|endoftext|>


<commit_before>use rand;
use rand::distributions::{IndependentSample, Range};

use std::{cmp, thread, time};
use std::fmt::Debug;

#[derive(Debug, PartialEq, Eq)]
pub enum RetryAction {
    OKAY,
    WAIT
}

pub trait RetryPolicy: Debug + PartialEq + Eq {
    fn max_tries(&self) -> usize;
    fn current_tries(&self) -> usize;

    fn fail(&mut self);
    fn succeed(&mut self);

    fn can_try(&self) -> Option<RetryAction> {
        if self.current_tries() >= self.max_tries() {
            None
        } else {
            Some(RetryAction::OKAY)
        }
    }

    fn is_down(&self) -> bool;
}

#[derive(Debug, PartialEq, Eq)]
pub enum RetryPolicyWrapper {
    ExponentialBackoff(ExponentialBackoffPolicy)
}

#[derive(Debug, PartialEq, Eq)]
pub struct ExponentialBackoffPolicy {
    max_tries: usize,
    current_tries: usize,
    last_try: time::Instant,
    wait: time::Duration
}

impl ExponentialBackoffPolicy {
    pub fn new(max_tries: usize) -> Self {
        ExponentialBackoffPolicy {
            max_tries,
            current_tries: 0,
            last_try: time::Instant::now(),
            wait: time::Duration::default()
        }
    }
}

impl RetryPolicy for ExponentialBackoffPolicy {
    fn max_tries(&self) -> usize {
        self.max_tries
    }

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

    fn fail(&mut self) {
        let max_secs = cmp::max(1, 1 << self.current_tries);
        let wait = if max_secs == 1 {
            1
        } else {
            let mut rng = rand::thread_rng();
            let range = Range::new(1, max_secs);
            range.ind_sample(&mut rng)
        };

        self.wait = time::Duration::from_secs(wait);
        self.last_try = time::Instant::now();
        self.current_tries += 1;
    }

    fn succeed(&mut self) {
        self.wait = time::Duration::default();
        self.last_try = time::Instant::now();
        self.current_tries = 0;
    }

    fn can_try(&self) -> Option<RetryAction> {
        if self.current_tries() >= self.max_tries() {
            return None;
        }

        let action = if self.last_try.elapsed().gt(&self.wait) {
            RetryAction::OKAY
        } else {
            RetryAction::WAIT
        };

        Some(action)
    }

    fn is_down(&self) -> bool {
      self.current_tries() >= self.max_tries()
    }
}

impl Into<RetryPolicyWrapper> for ExponentialBackoffPolicy {
    fn into(self) -> RetryPolicyWrapper {
        RetryPolicyWrapper::ExponentialBackoff(self)
    }
}

impl RetryPolicy for RetryPolicyWrapper {
    fn max_tries(&self) -> usize {
        match *self {
            RetryPolicyWrapper::ExponentialBackoff(ref policy) => policy
        }.max_tries()
    }

    fn current_tries(&self) -> usize {
        match *self {
            RetryPolicyWrapper::ExponentialBackoff(ref policy) => policy
        }.current_tries()
    }

    fn fail(&mut self) {
        match *self {
            RetryPolicyWrapper::ExponentialBackoff(ref mut policy) => policy
        }.fail()
    }

    fn succeed(&mut self) {
        match *self {
            RetryPolicyWrapper::ExponentialBackoff(ref mut policy) => policy
        }.succeed()
    }

    fn can_try(&self) -> Option<RetryAction> {
        match *self {
            RetryPolicyWrapper::ExponentialBackoff(ref policy) => policy
        }.can_try()
    }

    fn is_down(&self) -> bool {
        match *self {
            RetryPolicyWrapper::ExponentialBackoff(ref policy) => policy
        }.is_down()
    }
}

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

    use super::{RetryAction, RetryPolicy, ExponentialBackoffPolicy};

    const MAX_FAILS: usize = 10;

    #[test]
    fn no_fail() {
        let policy = ExponentialBackoffPolicy::new(MAX_FAILS);
        let can_try = policy.can_try();

        assert_eq!(Some(RetryAction::OKAY), can_try)
    }

    #[test]
    fn single_fail() {
        let mut policy = ExponentialBackoffPolicy::new(MAX_FAILS);
        policy.fail();
        let can_try = policy.can_try();

        // The wait will be >= 1s, so we'll be WAIT by the time we do the assert
        assert_eq!(Some(RetryAction::WAIT), can_try)
    }

    #[test]
    fn max_fails() {
        let mut policy = ExponentialBackoffPolicy::new(MAX_FAILS);

        for _ in 0..MAX_FAILS {
            policy.fail();
        }

        let can_try = policy.can_try();

        assert_eq!(None, can_try)
    }

    #[test]
    fn recover_from_fail() {
        let mut policy = ExponentialBackoffPolicy::new(MAX_FAILS);

        // Stop just before total failure
        for _ in 0..(MAX_FAILS - 1) {
            policy.fail();
        }

        policy.succeed();
        policy.fail();
        policy.fail();
        policy.fail();

        let can_try = policy.can_try();

        assert_eq!(Some(RetryAction::WAIT), can_try)
    }
}<commit_msg>do not augment the current tries when we're already in back off wait<commit_after>use rand;
use rand::distributions::{IndependentSample, Range};

use std::{cmp, thread, time};
use std::fmt::Debug;

#[derive(Debug, PartialEq, Eq)]
pub enum RetryAction {
    OKAY,
    WAIT
}

pub trait RetryPolicy: Debug + PartialEq + Eq {
    fn max_tries(&self) -> usize;
    fn current_tries(&self) -> usize;

    fn fail(&mut self);
    fn succeed(&mut self);

    fn can_try(&self) -> Option<RetryAction> {
        if self.current_tries() >= self.max_tries() {
            None
        } else {
            Some(RetryAction::OKAY)
        }
    }

    fn is_down(&self) -> bool;
}

#[derive(Debug, PartialEq, Eq)]
pub enum RetryPolicyWrapper {
    ExponentialBackoff(ExponentialBackoffPolicy)
}

#[derive(Debug, PartialEq, Eq)]
pub struct ExponentialBackoffPolicy {
    max_tries: usize,
    current_tries: usize,
    last_try: time::Instant,
    wait: time::Duration
}

impl ExponentialBackoffPolicy {
    pub fn new(max_tries: usize) -> Self {
        ExponentialBackoffPolicy {
            max_tries,
            current_tries: 0,
            last_try: time::Instant::now(),
            wait: time::Duration::default()
        }
    }
}

impl RetryPolicy for ExponentialBackoffPolicy {
    fn max_tries(&self) -> usize {
        self.max_tries
    }

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

    fn fail(&mut self) {
        if self.last_try.elapsed().lt(&self.wait) {
          //we're already in back off
          return;
        }

        let max_secs = cmp::max(1, 1 << self.current_tries);
        let wait = if max_secs == 1 {
            1
        } else {
            let mut rng = rand::thread_rng();
            let range = Range::new(1, max_secs);
            range.ind_sample(&mut rng)
        };

        self.wait = time::Duration::from_secs(wait);
        self.last_try = time::Instant::now();
        self.current_tries += 1;
    }

    fn succeed(&mut self) {
        self.wait = time::Duration::default();
        self.last_try = time::Instant::now();
        self.current_tries = 0;
    }

    fn can_try(&self) -> Option<RetryAction> {
        if self.current_tries() >= self.max_tries() {
            return None;
        }

        let action = if self.last_try.elapsed().gt(&self.wait) {
            RetryAction::OKAY
        } else {
            RetryAction::WAIT
        };

        Some(action)
    }

    fn is_down(&self) -> bool {
      self.current_tries() >= self.max_tries()
    }
}

impl Into<RetryPolicyWrapper> for ExponentialBackoffPolicy {
    fn into(self) -> RetryPolicyWrapper {
        RetryPolicyWrapper::ExponentialBackoff(self)
    }
}

impl RetryPolicy for RetryPolicyWrapper {
    fn max_tries(&self) -> usize {
        match *self {
            RetryPolicyWrapper::ExponentialBackoff(ref policy) => policy
        }.max_tries()
    }

    fn current_tries(&self) -> usize {
        match *self {
            RetryPolicyWrapper::ExponentialBackoff(ref policy) => policy
        }.current_tries()
    }

    fn fail(&mut self) {
        match *self {
            RetryPolicyWrapper::ExponentialBackoff(ref mut policy) => policy
        }.fail()
    }

    fn succeed(&mut self) {
        match *self {
            RetryPolicyWrapper::ExponentialBackoff(ref mut policy) => policy
        }.succeed()
    }

    fn can_try(&self) -> Option<RetryAction> {
        match *self {
            RetryPolicyWrapper::ExponentialBackoff(ref policy) => policy
        }.can_try()
    }

    fn is_down(&self) -> bool {
        match *self {
            RetryPolicyWrapper::ExponentialBackoff(ref policy) => policy
        }.is_down()
    }
}

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

    use super::{RetryAction, RetryPolicy, ExponentialBackoffPolicy};

    const MAX_FAILS: usize = 10;

    #[test]
    fn no_fail() {
        let policy = ExponentialBackoffPolicy::new(MAX_FAILS);
        let can_try = policy.can_try();

        assert_eq!(Some(RetryAction::OKAY), can_try)
    }

    #[test]
    fn single_fail() {
        let mut policy = ExponentialBackoffPolicy::new(MAX_FAILS);
        policy.fail();
        let can_try = policy.can_try();

        // The wait will be >= 1s, so we'll be WAIT by the time we do the assert
        assert_eq!(Some(RetryAction::WAIT), can_try)
    }

    #[test]
    fn max_fails() {
        let mut policy = ExponentialBackoffPolicy::new(MAX_FAILS);

        for _ in 0..MAX_FAILS {
            policy.fail();
        }

        let can_try = policy.can_try();

        assert_eq!(None, can_try)
    }

    #[test]
    fn recover_from_fail() {
        let mut policy = ExponentialBackoffPolicy::new(MAX_FAILS);

        // Stop just before total failure
        for _ in 0..(MAX_FAILS - 1) {
            policy.fail();
        }

        policy.succeed();
        policy.fail();
        policy.fail();
        policy.fail();

        let can_try = policy.can_try();

        assert_eq!(Some(RetryAction::WAIT), can_try)
    }
}
<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::result::Result as RResult;

pub use clap::App;

use clap::{Arg, ArgMatches};
use log;
use log::LogLevelFilter;

use configuration::Configuration;
use error::RuntimeError;
use error::RuntimeErrorKind;
use logger::ImagLogger;

use libimagstore::store::Store;

pub struct Runtime<'a> {
    rtp: PathBuf,
    configuration: Configuration,
    cli_matches: ArgMatches<'a, 'a>,
    store: Store,
}

impl<'a> Runtime<'a> {

    /**
     * Gets the CLI spec for the program and retreives the config file path (or uses the default on
     * in $HOME/.imag/config, $XDG_CONFIG_DIR/imag/config or from env("$IMAG_CONFIG")
     * and builds the Runtime object with it.
     *
     * The cli_spec object should be initially build with the ::get_default_cli_builder() function.
     *
     */
    pub fn new(cli_spec: App<'a, 'a, 'a, 'a, 'a, 'a>) -> Result<Runtime<'a>, RuntimeError> {
        let matches = cli_spec.get_matches();
        let rtp : PathBuf = matches.value_of("runtimepath").unwrap_or("~/.imag/").into();
        let storepath = matches.value_of("storepath")
                                .map(PathBuf::from)
                                .unwrap_or({
                                    let mut spath = rtp.clone();
                                    spath.push("store");
                                    spath
                                });
        Store::new(storepath).map(|store| {
            Runtime {
                cli_matches: matches,
                configuration: Configuration::new(&rtp).unwrap_or(Configuration::default()),
                rtp: rtp,
                store: store,
            }
        })
        .map_err(|e| {
            RuntimeError::new(RuntimeErrorKind::Instantiate, Some(Box::new(e)))
        })
    }

    /**
     * Get a commandline-interface builder object from `clap`
     *
     * This commandline interface builder object already contains some predefined interface flags:
     *   * -v | --verbose for verbosity
     *   * --debug for debugging
     *   * -c <file> | --config <file> for alternative configuration file
     *   * -r <path> | --rtp <path> for alternative runtimepath
     *   * --store <path> for alternative store path
     * Each has the appropriate help text included.
     *
     * The `appname` shall be "imag-<command>".
     */
    pub fn get_default_cli_builder(appname: &'a str,
                                   version: &'a str,
                                   about: &'a str)
        -> App<'a, 'a, 'a, 'a, 'a, 'a>
    {
        App::new(appname)
            .version(version)
            .author("Matthias Beyer <mail@beyermatthias.de>")
            .about(about)
            .arg(Arg::with_name("verbosity")
                .short("v")
                .long("verbose")
                .help("Enables verbosity")
                .required(false)
                .takes_value(false))

            .arg(Arg::with_name("debugging")
                .long("debug")
                .help("Enables debugging output")
                .required(false)
                .takes_value(false))

            .arg(Arg::with_name("config")
                .short("c")
                .long("config")
                .help("Path to alternative config file")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name("runtimepath")
                .short("r")
                .long("rtp")
                .help("Alternative runtimepath")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name("storepath")
                .long("store")
                .help("Alternative storepath. Must be specified as full path, can be outside of the RTP")
                .required(false)
                .takes_value(true))
    }

    /**
     * Initialize the internal logger
     */
    pub fn init_logger(&self) {
        let lvl = if self.is_debugging() {
            LogLevelFilter::Debug
        } else if self.is_verbose() {
            LogLevelFilter::Info
        } else {
            LogLevelFilter::Error
        };

        log::set_logger(|max_log_lvl| {
            max_log_lvl.set(lvl);
            debug!("Init logger with {}", lvl);
            Box::new(ImagLogger::new(lvl.to_log_level().unwrap()))
        })
        .map_err(|_| {
            panic!("Could not setup logger");
        })
        .ok();
    }

    /**
     * Get the verbosity flag value
     */
    pub fn is_verbose(&self) -> bool {
        self.cli_matches.is_present("verbosity")
    }

    /**
     * Get the debugging flag value
     */
    pub fn is_debugging(&self) -> bool {
        self.cli_matches.is_present("debugging")
    }

    /**
     * Get the runtimepath
     */
    pub fn rtp(&self) -> &PathBuf {
        &self.rtp
    }

    /**
     * Get the commandline interface matches
     */
    pub fn cli(&self) -> &ArgMatches {
        &self.cli_matches
    }

    /**
     * Get the store object
     */
    pub fn store(&self) -> &Store {
        &self.store
    }

}

<commit_msg>Fix: Use env(HOME) instead of "~"<commit_after>use std::path::PathBuf;
use std::result::Result as RResult;

pub use clap::App;

use clap::{Arg, ArgMatches};
use log;
use log::LogLevelFilter;

use configuration::Configuration;
use error::RuntimeError;
use error::RuntimeErrorKind;
use logger::ImagLogger;

use libimagstore::store::Store;

pub struct Runtime<'a> {
    rtp: PathBuf,
    configuration: Configuration,
    cli_matches: ArgMatches<'a, 'a>,
    store: Store,
}

impl<'a> Runtime<'a> {

    /**
     * Gets the CLI spec for the program and retreives the config file path (or uses the default on
     * in $HOME/.imag/config, $XDG_CONFIG_DIR/imag/config or from env("$IMAG_CONFIG")
     * and builds the Runtime object with it.
     *
     * The cli_spec object should be initially build with the ::get_default_cli_builder() function.
     *
     */
    pub fn new(cli_spec: App<'a, 'a, 'a, 'a, 'a, 'a>) -> Result<Runtime<'a>, RuntimeError> {
        use std::env;

        let matches = cli_spec.get_matches();
        let rtp : PathBuf = matches.value_of("runtimepath")
            .map(PathBuf::from)
            .unwrap_or_else(|| {
                env::var("HOME")
                    .map(PathBuf::from)
                    .map(|mut p| { p.push(".imag"); p})
                    .unwrap_or_else(|_| {
                        panic!("You seem to be $HOME-less. Please get a $HOME before using this software. We are sorry for you and hope you have some accommodation anyways.");
                    })
            });
        let storepath = matches.value_of("storepath")
                                .map(PathBuf::from)
                                .unwrap_or({
                                    let mut spath = rtp.clone();
                                    spath.push("store");
                                    spath
                                });
        Store::new(storepath).map(|store| {
            Runtime {
                cli_matches: matches,
                configuration: Configuration::new(&rtp).unwrap_or(Configuration::default()),
                rtp: rtp,
                store: store,
            }
        })
        .map_err(|e| {
            RuntimeError::new(RuntimeErrorKind::Instantiate, Some(Box::new(e)))
        })
    }

    /**
     * Get a commandline-interface builder object from `clap`
     *
     * This commandline interface builder object already contains some predefined interface flags:
     *   * -v | --verbose for verbosity
     *   * --debug for debugging
     *   * -c <file> | --config <file> for alternative configuration file
     *   * -r <path> | --rtp <path> for alternative runtimepath
     *   * --store <path> for alternative store path
     * Each has the appropriate help text included.
     *
     * The `appname` shall be "imag-<command>".
     */
    pub fn get_default_cli_builder(appname: &'a str,
                                   version: &'a str,
                                   about: &'a str)
        -> App<'a, 'a, 'a, 'a, 'a, 'a>
    {
        App::new(appname)
            .version(version)
            .author("Matthias Beyer <mail@beyermatthias.de>")
            .about(about)
            .arg(Arg::with_name("verbosity")
                .short("v")
                .long("verbose")
                .help("Enables verbosity")
                .required(false)
                .takes_value(false))

            .arg(Arg::with_name("debugging")
                .long("debug")
                .help("Enables debugging output")
                .required(false)
                .takes_value(false))

            .arg(Arg::with_name("config")
                .short("c")
                .long("config")
                .help("Path to alternative config file")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name("runtimepath")
                .short("r")
                .long("rtp")
                .help("Alternative runtimepath")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name("storepath")
                .long("store")
                .help("Alternative storepath. Must be specified as full path, can be outside of the RTP")
                .required(false)
                .takes_value(true))
    }

    /**
     * Initialize the internal logger
     */
    pub fn init_logger(&self) {
        let lvl = if self.is_debugging() {
            LogLevelFilter::Debug
        } else if self.is_verbose() {
            LogLevelFilter::Info
        } else {
            LogLevelFilter::Error
        };

        log::set_logger(|max_log_lvl| {
            max_log_lvl.set(lvl);
            debug!("Init logger with {}", lvl);
            Box::new(ImagLogger::new(lvl.to_log_level().unwrap()))
        })
        .map_err(|_| {
            panic!("Could not setup logger");
        })
        .ok();
    }

    /**
     * Get the verbosity flag value
     */
    pub fn is_verbose(&self) -> bool {
        self.cli_matches.is_present("verbosity")
    }

    /**
     * Get the debugging flag value
     */
    pub fn is_debugging(&self) -> bool {
        self.cli_matches.is_present("debugging")
    }

    /**
     * Get the runtimepath
     */
    pub fn rtp(&self) -> &PathBuf {
        &self.rtp
    }

    /**
     * Get the commandline interface matches
     */
    pub fn cli(&self) -> &ArgMatches {
        &self.cli_matches
    }

    /**
     * Get the store object
     */
    pub fn store(&self) -> &Store {
        &self.store
    }

}

<|endoftext|>


<commit_before>use std::collections::hash_map;
use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;

use crate::reflect::map::ReflectMap;
use crate::reflect::map::ReflectMapIter;
use crate::reflect::map::ReflectMapIterTrait;
use crate::reflect::runtime_types::RuntimeType;
use crate::reflect::ProtobufValue;
use crate::reflect::ReflectValueBox;
use crate::reflect::ReflectValueRef;
use crate::reflect::RuntimeTypeBox;

#[derive(Debug, Clone)]
enum Maps {
    U32(HashMap<u32, ReflectValueBox>),
    I32(HashMap<i32, ReflectValueBox>),
    U64(HashMap<u64, ReflectValueBox>),
    I64(HashMap<i64, ReflectValueBox>),
    Bool(HashMap<bool, ReflectValueBox>),
    String(HashMap<String, ReflectValueBox>),
}

impl Maps {
    fn len(&self) -> usize {
        match self {
            Maps::U32(m) => m.len(),
            Maps::I32(m) => m.len(),
            Maps::U64(m) => m.len(),
            Maps::I64(m) => m.len(),
            Maps::Bool(m) => m.len(),
            Maps::String(m) => m.len(),
        }
    }

    fn is_empty(&self) -> bool {
        match self {
            Maps::U32(m) => m.is_empty(),
            Maps::I32(m) => m.is_empty(),
            Maps::U64(m) => m.is_empty(),
            Maps::I64(m) => m.is_empty(),
            Maps::Bool(m) => m.is_empty(),
            Maps::String(m) => m.is_empty(),
        }
    }

    fn clear(&mut self) {
        match self {
            Maps::U32(m) => m.clear(),
            Maps::I32(m) => m.clear(),
            Maps::U64(m) => m.clear(),
            Maps::I64(m) => m.clear(),
            Maps::Bool(m) => m.clear(),
            Maps::String(m) => m.clear(),
        }
    }

    fn key_type(&self) -> RuntimeTypeBox {
        match self {
            Maps::U32(..) => RuntimeTypeBox::U32,
            Maps::I32(..) => RuntimeTypeBox::I32,
            Maps::U64(..) => RuntimeTypeBox::U64,
            Maps::I64(..) => RuntimeTypeBox::I64,
            Maps::Bool(..) => RuntimeTypeBox::Bool,
            Maps::String(..) => RuntimeTypeBox::String,
        }
    }
}

#[derive(Clone)]
pub(crate) struct DynamicMap {
    /// Type of value.
    ///
    /// Type of key is defined by the maps key.
    value: RuntimeTypeBox,
    maps: Maps,
}

impl fmt::Debug for DynamicMap {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.maps, f)
    }
}

impl DynamicMap {
    pub fn new(key: RuntimeTypeBox, value: RuntimeTypeBox) -> DynamicMap {
        DynamicMap {
            value,
            maps: match key {
                RuntimeTypeBox::U32 => Maps::U32(HashMap::new()),
                RuntimeTypeBox::I32 => Maps::I32(HashMap::new()),
                RuntimeTypeBox::U64 => Maps::U64(HashMap::new()),
                RuntimeTypeBox::I64 => Maps::I64(HashMap::new()),
                RuntimeTypeBox::Bool => Maps::Bool(HashMap::new()),
                RuntimeTypeBox::String => Maps::String(HashMap::new()),
                t => panic!("type cannot be hashmap key: {}", t),
            },
        }
    }
}

struct DynamicMapIterImpl<'a, K: ProtobufValue + Eq + Hash + 'static> {
    iter: hash_map::Iter<'a, K, ReflectValueBox>,
    value: &'a RuntimeTypeBox,
}

impl<'a, K: ProtobufValue + Eq + Hash + 'static> ReflectMapIterTrait<'a>
    for DynamicMapIterImpl<'a, K>
{
    fn next(&mut self) -> Option<(ReflectValueRef<'a>, ReflectValueRef<'a>)> {
        self.iter
            .next()
            .map(|(k, v)| (K::as_ref(k), v.as_value_ref()))
    }

    fn key_type(&self) -> RuntimeTypeBox {
        K::RuntimeType::runtime_type_box()
    }

    fn value_type(&self) -> RuntimeTypeBox {
        self.value.clone()
    }
}

impl ReflectMap for DynamicMap {
    fn reflect_iter(&self) -> ReflectMapIter {
        match &self.maps {
            Maps::U32(m) => ReflectMapIter::new(DynamicMapIterImpl {
                iter: m.iter(),
                value: &self.value,
            }),
            Maps::I32(m) => ReflectMapIter::new(DynamicMapIterImpl {
                iter: m.iter(),
                value: &self.value,
            }),
            Maps::U64(m) => ReflectMapIter::new(DynamicMapIterImpl {
                iter: m.iter(),
                value: &self.value,
            }),
            Maps::I64(m) => ReflectMapIter::new(DynamicMapIterImpl {
                iter: m.iter(),
                value: &self.value,
            }),
            Maps::Bool(m) => ReflectMapIter::new(DynamicMapIterImpl {
                iter: m.iter(),
                value: &self.value,
            }),
            Maps::String(m) => ReflectMapIter::new(DynamicMapIterImpl {
                iter: m.iter(),
                value: &self.value,
            }),
        }
    }

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

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

    fn get<'a>(&'a self, key: ReflectValueRef) -> Option<ReflectValueRef<'a>> {
        match (&self.maps, key) {
            (Maps::U32(m), ReflectValueRef::U32(v)) => m.get(&v),
            (Maps::U64(m), ReflectValueRef::U64(v)) => m.get(&v),
            (Maps::I32(m), ReflectValueRef::I32(v)) => m.get(&v),
            (Maps::I64(m), ReflectValueRef::I64(v)) => m.get(&v),
            (Maps::Bool(m), ReflectValueRef::Bool(v)) => m.get(&v),
            (Maps::String(m), ReflectValueRef::String(v)) => m.get(&*v),
            _ => None,
        }
        .map(ReflectValueBox::as_value_ref)
    }

    fn insert(&mut self, key: ReflectValueBox, value: ReflectValueBox) {
        assert!(value.get_type() == self.value);
        match (&mut self.maps, &key) {
            (Maps::U32(m), ReflectValueBox::U32(k)) => m.insert(*k, value),
            (Maps::U64(m), ReflectValueBox::U64(k)) => m.insert(*k, value),
            (Maps::I32(m), ReflectValueBox::I32(k)) => m.insert(*k, value),
            (Maps::I64(m), ReflectValueBox::I64(k)) => m.insert(*k, value),
            (Maps::Bool(m), ReflectValueBox::Bool(k)) => m.insert(*k, value),
            (Maps::String(m), _) => match key {
                ReflectValueBox::String(k) => m.insert(k, value),
                _ => panic!("wrong key type"),
            },
            _ => panic!("wrong key type"),
        };
    }

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

    fn key_type(&self) -> RuntimeTypeBox {
        self.maps.key_type()
    }

    fn value_type(&self) -> RuntimeTypeBox {
        self.value.clone()
    }
}
<commit_msg>Better Debug for DynamicMap<commit_after>use std::collections::hash_map;
use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;

use crate::reflect::map::ReflectMap;
use crate::reflect::map::ReflectMapIter;
use crate::reflect::map::ReflectMapIterTrait;
use crate::reflect::runtime_types::RuntimeType;
use crate::reflect::ProtobufValue;
use crate::reflect::ReflectValueBox;
use crate::reflect::ReflectValueRef;
use crate::reflect::RuntimeTypeBox;

#[derive(Clone)]
enum Maps {
    U32(HashMap<u32, ReflectValueBox>),
    I32(HashMap<i32, ReflectValueBox>),
    U64(HashMap<u64, ReflectValueBox>),
    I64(HashMap<i64, ReflectValueBox>),
    Bool(HashMap<bool, ReflectValueBox>),
    String(HashMap<String, ReflectValueBox>),
}

impl fmt::Debug for Maps {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Maps::U32(map) => fmt::Debug::fmt(map, f),
            Maps::I32(map) => fmt::Debug::fmt(map, f),
            Maps::U64(map) => fmt::Debug::fmt(map, f),
            Maps::I64(map) => fmt::Debug::fmt(map, f),
            Maps::Bool(map) => fmt::Debug::fmt(map, f),
            Maps::String(map) => fmt::Debug::fmt(map, f),
        }
    }
}

impl Maps {
    fn len(&self) -> usize {
        match self {
            Maps::U32(m) => m.len(),
            Maps::I32(m) => m.len(),
            Maps::U64(m) => m.len(),
            Maps::I64(m) => m.len(),
            Maps::Bool(m) => m.len(),
            Maps::String(m) => m.len(),
        }
    }

    fn is_empty(&self) -> bool {
        match self {
            Maps::U32(m) => m.is_empty(),
            Maps::I32(m) => m.is_empty(),
            Maps::U64(m) => m.is_empty(),
            Maps::I64(m) => m.is_empty(),
            Maps::Bool(m) => m.is_empty(),
            Maps::String(m) => m.is_empty(),
        }
    }

    fn clear(&mut self) {
        match self {
            Maps::U32(m) => m.clear(),
            Maps::I32(m) => m.clear(),
            Maps::U64(m) => m.clear(),
            Maps::I64(m) => m.clear(),
            Maps::Bool(m) => m.clear(),
            Maps::String(m) => m.clear(),
        }
    }

    fn key_type(&self) -> RuntimeTypeBox {
        match self {
            Maps::U32(..) => RuntimeTypeBox::U32,
            Maps::I32(..) => RuntimeTypeBox::I32,
            Maps::U64(..) => RuntimeTypeBox::U64,
            Maps::I64(..) => RuntimeTypeBox::I64,
            Maps::Bool(..) => RuntimeTypeBox::Bool,
            Maps::String(..) => RuntimeTypeBox::String,
        }
    }
}

#[derive(Clone)]
pub(crate) struct DynamicMap {
    /// Type of value.
    ///
    /// Type of key is defined by the maps key.
    value: RuntimeTypeBox,
    maps: Maps,
}

impl fmt::Debug for DynamicMap {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.maps, f)
    }
}

impl DynamicMap {
    pub fn new(key: RuntimeTypeBox, value: RuntimeTypeBox) -> DynamicMap {
        DynamicMap {
            value,
            maps: match key {
                RuntimeTypeBox::U32 => Maps::U32(HashMap::new()),
                RuntimeTypeBox::I32 => Maps::I32(HashMap::new()),
                RuntimeTypeBox::U64 => Maps::U64(HashMap::new()),
                RuntimeTypeBox::I64 => Maps::I64(HashMap::new()),
                RuntimeTypeBox::Bool => Maps::Bool(HashMap::new()),
                RuntimeTypeBox::String => Maps::String(HashMap::new()),
                t => panic!("type cannot be hashmap key: {}", t),
            },
        }
    }
}

struct DynamicMapIterImpl<'a, K: ProtobufValue + Eq + Hash + 'static> {
    iter: hash_map::Iter<'a, K, ReflectValueBox>,
    value: &'a RuntimeTypeBox,
}

impl<'a, K: ProtobufValue + Eq + Hash + 'static> ReflectMapIterTrait<'a>
    for DynamicMapIterImpl<'a, K>
{
    fn next(&mut self) -> Option<(ReflectValueRef<'a>, ReflectValueRef<'a>)> {
        self.iter
            .next()
            .map(|(k, v)| (K::as_ref(k), v.as_value_ref()))
    }

    fn key_type(&self) -> RuntimeTypeBox {
        K::RuntimeType::runtime_type_box()
    }

    fn value_type(&self) -> RuntimeTypeBox {
        self.value.clone()
    }
}

impl ReflectMap for DynamicMap {
    fn reflect_iter(&self) -> ReflectMapIter {
        match &self.maps {
            Maps::U32(m) => ReflectMapIter::new(DynamicMapIterImpl {
                iter: m.iter(),
                value: &self.value,
            }),
            Maps::I32(m) => ReflectMapIter::new(DynamicMapIterImpl {
                iter: m.iter(),
                value: &self.value,
            }),
            Maps::U64(m) => ReflectMapIter::new(DynamicMapIterImpl {
                iter: m.iter(),
                value: &self.value,
            }),
            Maps::I64(m) => ReflectMapIter::new(DynamicMapIterImpl {
                iter: m.iter(),
                value: &self.value,
            }),
            Maps::Bool(m) => ReflectMapIter::new(DynamicMapIterImpl {
                iter: m.iter(),
                value: &self.value,
            }),
            Maps::String(m) => ReflectMapIter::new(DynamicMapIterImpl {
                iter: m.iter(),
                value: &self.value,
            }),
        }
    }

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

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

    fn get<'a>(&'a self, key: ReflectValueRef) -> Option<ReflectValueRef<'a>> {
        match (&self.maps, key) {
            (Maps::U32(m), ReflectValueRef::U32(v)) => m.get(&v),
            (Maps::U64(m), ReflectValueRef::U64(v)) => m.get(&v),
            (Maps::I32(m), ReflectValueRef::I32(v)) => m.get(&v),
            (Maps::I64(m), ReflectValueRef::I64(v)) => m.get(&v),
            (Maps::Bool(m), ReflectValueRef::Bool(v)) => m.get(&v),
            (Maps::String(m), ReflectValueRef::String(v)) => m.get(&*v),
            _ => None,
        }
        .map(ReflectValueBox::as_value_ref)
    }

    fn insert(&mut self, key: ReflectValueBox, value: ReflectValueBox) {
        assert!(value.get_type() == self.value);
        match (&mut self.maps, &key) {
            (Maps::U32(m), ReflectValueBox::U32(k)) => m.insert(*k, value),
            (Maps::U64(m), ReflectValueBox::U64(k)) => m.insert(*k, value),
            (Maps::I32(m), ReflectValueBox::I32(k)) => m.insert(*k, value),
            (Maps::I64(m), ReflectValueBox::I64(k)) => m.insert(*k, value),
            (Maps::Bool(m), ReflectValueBox::Bool(k)) => m.insert(*k, value),
            (Maps::String(m), _) => match key {
                ReflectValueBox::String(k) => m.insert(k, value),
                _ => panic!("wrong key type"),
            },
            _ => panic!("wrong key type"),
        };
    }

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

    fn key_type(&self) -> RuntimeTypeBox {
        self.maps.key_type()
    }

    fn value_type(&self) -> RuntimeTypeBox {
        self.value.clone()
    }
}
<|endoftext|>


<commit_before>use either::Either;
use hir::{db::AstDatabase, InFile};
use ide_db::{assists::Assist, source_change::SourceChange};
use rustc_hash::FxHashMap;
use stdx::format_to;
use syntax::{algo, ast::make, AstNode, SyntaxNodePtr};
use text_edit::TextEdit;

use crate::{fix, Diagnostic, DiagnosticsContext};

// Diagnostic: missing-fields
//
// This diagnostic is triggered if record lacks some fields that exist in the corresponding structure.
//
// Example:
//
// ```rust
// struct A { a: u8, b: u8 }
//
// let a = A { a: 10 };
// ```
pub(crate) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Diagnostic {
    let mut message = String::from("missing structure fields:\n");
    for field in &d.missed_fields {
        format_to!(message, "- {}\n", field);
    }

    let ptr = InFile::new(
        d.file,
        d.field_list_parent_path
            .clone()
            .map(SyntaxNodePtr::from)
            .unwrap_or_else(|| d.field_list_parent.clone().either(|it| it.into(), |it| it.into())),
    );

    Diagnostic::new("missing-fields", message, ctx.sema.diagnostics_display_range(ptr).range)
        .with_fixes(fixes(ctx, d))
}

fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Assist>> {
    // Note that although we could add a diagnostics to
    // fill the missing tuple field, e.g :
    // `struct A(usize);`
    // `let a = A { 0: () }`
    // but it is uncommon usage and it should not be encouraged.
    if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) {
        return None;
    }

    let root = ctx.sema.db.parse_or_expand(d.file)?;
    let field_list_parent = match &d.field_list_parent {
        Either::Left(record_expr) => record_expr.to_node(&root),
        // FIXE: patterns should be fixable as well.
        Either::Right(_) => return None,
    };
    let old_field_list = field_list_parent.record_expr_field_list()?;
    let new_field_list = old_field_list.clone_for_update();
    let mut locals = FxHashMap::default();
    ctx.sema.scope(field_list_parent.syntax()).process_all_names(&mut |name, def| {
        if let hir::ScopeDef::Local(local) = def {
            locals.insert(name.clone(), local);
        }
    });
    let missing_fields = ctx.sema.record_literal_missing_fields(&field_list_parent);
    for (f, ty) in missing_fields.iter() {
        let field_expr = if let Some(local_candidate) = locals.get(&f.name(ctx.sema.db)) {
            let candidate_ty = local_candidate.ty(ctx.sema.db);
            if ty.could_unify_with(ctx.sema.db, &candidate_ty) {
                None
            } else {
                Some(make::expr_unit())
            }
        } else {
            Some(make::expr_unit())
        };
        let field =
            make::record_expr_field(make::name_ref(&f.name(ctx.sema.db).to_string()), field_expr)
                .clone_for_update();
        new_field_list.add_field(field);
    }

    let edit = {
        let mut builder = TextEdit::builder();
        algo::diff(old_field_list.syntax(), new_field_list.syntax()).into_text_edit(&mut builder);
        builder.finish()
    };
    Some(vec![fix(
        "fill_missing_fields",
        "Fill struct fields",
        SourceChange::from_text_edit(d.file.original_file(ctx.sema.db), edit),
        ctx.sema.original_range(field_list_parent.syntax()).range,
    )])
}

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

    #[test]
    fn missing_record_pat_field_diagnostic() {
        check_diagnostics(
            r#"
struct S { foo: i32, bar: () }
fn baz(s: S) {
    let S { foo: _ } = s;
      //^ error: missing structure fields:
      //| - bar
}
"#,
        );
    }

    #[test]
    fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() {
        check_diagnostics(
            r"
struct S { foo: i32, bar: () }
fn baz(s: S) -> i32 {
    match s {
        S { foo, .. } => foo,
    }
}
",
        )
    }

    #[test]
    fn missing_record_pat_field_box() {
        check_diagnostics(
            r"
struct S { s: Box<u32> }
fn x(a: S) {
    let S { box s } = a;
}
",
        )
    }

    #[test]
    fn missing_record_pat_field_ref() {
        check_diagnostics(
            r"
struct S { s: u32 }
fn x(a: S) {
    let S { ref s } = a;
}
",
        )
    }

    #[test]
    fn range_mapping_out_of_macros() {
        // FIXME: this is very wrong, but somewhat tricky to fix.
        check_fix(
            r#"
fn some() {}
fn items() {}
fn here() {}

macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }

fn main() {
    let _x = id![Foo { a: $042 }];
}

pub struct Foo { pub a: i32, pub b: i32 }
"#,
            r#"
fn some(, b: () ) {}
fn items() {}
fn here() {}

macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }

fn main() {
    let _x = id![Foo { a: 42 }];
}

pub struct Foo { pub a: i32, pub b: i32 }
"#,
        );
    }

    #[test]
    fn test_fill_struct_fields_empty() {
        check_fix(
            r#"
struct TestStruct { one: i32, two: i64 }

fn test_fn() {
    let s = TestStruct {$0};
}
"#,
            r#"
struct TestStruct { one: i32, two: i64 }

fn test_fn() {
    let s = TestStruct { one: (), two: () };
}
"#,
        );
    }

    #[test]
    fn test_fill_struct_fields_self() {
        check_fix(
            r#"
struct TestStruct { one: i32 }

impl TestStruct {
    fn test_fn() { let s = Self {$0}; }
}
"#,
            r#"
struct TestStruct { one: i32 }

impl TestStruct {
    fn test_fn() { let s = Self { one: () }; }
}
"#,
        );
    }

    #[test]
    fn test_fill_struct_fields_enum() {
        check_fix(
            r#"
enum Expr {
    Bin { lhs: Box<Expr>, rhs: Box<Expr> }
}

impl Expr {
    fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
        Expr::Bin {$0 }
    }
}
"#,
            r#"
enum Expr {
    Bin { lhs: Box<Expr>, rhs: Box<Expr> }
}

impl Expr {
    fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
        Expr::Bin { lhs: (), rhs: () }
    }
}
"#,
        );
    }

    #[test]
    fn test_fill_struct_fields_partial() {
        check_fix(
            r#"
struct TestStruct { one: i32, two: i64 }

fn test_fn() {
    let s = TestStruct{ two: 2$0 };
}
"#,
            r"
struct TestStruct { one: i32, two: i64 }

fn test_fn() {
    let s = TestStruct{ two: 2, one: () };
}
",
        );
    }

    #[test]
    fn test_fill_struct_fields_raw_ident() {
        check_fix(
            r#"
struct TestStruct { r#type: u8 }

fn test_fn() {
    TestStruct { $0 };
}
"#,
            r"
struct TestStruct { r#type: u8 }

fn test_fn() {
    TestStruct { r#type: ()  };
}
",
        );
    }

    #[test]
    fn test_fill_struct_fields_no_diagnostic() {
        check_diagnostics(
            r#"
struct TestStruct { one: i32, two: i64 }

fn test_fn() {
    let one = 1;
    let s = TestStruct{ one, two: 2 };
}
        "#,
        );
    }

    #[test]
    fn test_fill_struct_fields_no_diagnostic_on_spread() {
        check_diagnostics(
            r#"
struct TestStruct { one: i32, two: i64 }

fn test_fn() {
    let one = 1;
    let s = TestStruct{ ..a };
}
"#,
        );
    }

    #[test]
    fn test_fill_struct_fields_blank_line() {
        check_fix(
            r#"
struct S { a: (), b: () }

fn f() {
    S {
        $0
    };
}
"#,
            r#"
struct S { a: (), b: () }

fn f() {
    S {
        a: (),
        b: (),
    };
}
"#,
        );
    }

    #[test]
    fn import_extern_crate_clash_with_inner_item() {
        // This is more of a resolver test, but doesn't really work with the hir_def testsuite.

        check_diagnostics(
            r#"
//- /lib.rs crate:lib deps:jwt
mod permissions;

use permissions::jwt;

fn f() {
    fn inner() {}
    jwt::Claims {}; // should resolve to the local one with 0 fields, and not get a diagnostic
}

//- /permissions.rs
pub mod jwt  {
    pub struct Claims {}
}

//- /jwt/lib.rs crate:jwt
pub struct Claims {
    field: u8,
}
        "#,
        );
    }
}
<commit_msg>Add tests for fill struct fields shorthand<commit_after>use either::Either;
use hir::{db::AstDatabase, InFile};
use ide_db::{assists::Assist, source_change::SourceChange};
use rustc_hash::FxHashMap;
use stdx::format_to;
use syntax::{algo, ast::make, AstNode, SyntaxNodePtr};
use text_edit::TextEdit;

use crate::{fix, Diagnostic, DiagnosticsContext};

// Diagnostic: missing-fields
//
// This diagnostic is triggered if record lacks some fields that exist in the corresponding structure.
//
// Example:
//
// ```rust
// struct A { a: u8, b: u8 }
//
// let a = A { a: 10 };
// ```
pub(crate) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Diagnostic {
    let mut message = String::from("missing structure fields:\n");
    for field in &d.missed_fields {
        format_to!(message, "- {}\n", field);
    }

    let ptr = InFile::new(
        d.file,
        d.field_list_parent_path
            .clone()
            .map(SyntaxNodePtr::from)
            .unwrap_or_else(|| d.field_list_parent.clone().either(|it| it.into(), |it| it.into())),
    );

    Diagnostic::new("missing-fields", message, ctx.sema.diagnostics_display_range(ptr).range)
        .with_fixes(fixes(ctx, d))
}

fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Assist>> {
    // Note that although we could add a diagnostics to
    // fill the missing tuple field, e.g :
    // `struct A(usize);`
    // `let a = A { 0: () }`
    // but it is uncommon usage and it should not be encouraged.
    if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) {
        return None;
    }

    let root = ctx.sema.db.parse_or_expand(d.file)?;
    let field_list_parent = match &d.field_list_parent {
        Either::Left(record_expr) => record_expr.to_node(&root),
        // FIXE: patterns should be fixable as well.
        Either::Right(_) => return None,
    };
    let old_field_list = field_list_parent.record_expr_field_list()?;
    let new_field_list = old_field_list.clone_for_update();
    let mut locals = FxHashMap::default();
    ctx.sema.scope(field_list_parent.syntax()).process_all_names(&mut |name, def| {
        if let hir::ScopeDef::Local(local) = def {
            locals.insert(name.clone(), local);
        }
    });
    let missing_fields = ctx.sema.record_literal_missing_fields(&field_list_parent);
    for (f, ty) in missing_fields.iter() {
        let field_expr = if let Some(local_candidate) = locals.get(&f.name(ctx.sema.db)) {
            let candidate_ty = local_candidate.ty(ctx.sema.db);
            if ty.could_unify_with(ctx.sema.db, &candidate_ty) {
                None
            } else {
                Some(make::expr_unit())
            }
        } else {
            Some(make::expr_unit())
        };
        let field =
            make::record_expr_field(make::name_ref(&f.name(ctx.sema.db).to_string()), field_expr)
                .clone_for_update();
        new_field_list.add_field(field);
    }

    let edit = {
        let mut builder = TextEdit::builder();
        algo::diff(old_field_list.syntax(), new_field_list.syntax()).into_text_edit(&mut builder);
        builder.finish()
    };
    Some(vec![fix(
        "fill_missing_fields",
        "Fill struct fields",
        SourceChange::from_text_edit(d.file.original_file(ctx.sema.db), edit),
        ctx.sema.original_range(field_list_parent.syntax()).range,
    )])
}

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

    #[test]
    fn missing_record_pat_field_diagnostic() {
        check_diagnostics(
            r#"
struct S { foo: i32, bar: () }
fn baz(s: S) {
    let S { foo: _ } = s;
      //^ error: missing structure fields:
      //| - bar
}
"#,
        );
    }

    #[test]
    fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() {
        check_diagnostics(
            r"
struct S { foo: i32, bar: () }
fn baz(s: S) -> i32 {
    match s {
        S { foo, .. } => foo,
    }
}
",
        )
    }

    #[test]
    fn missing_record_pat_field_box() {
        check_diagnostics(
            r"
struct S { s: Box<u32> }
fn x(a: S) {
    let S { box s } = a;
}
",
        )
    }

    #[test]
    fn missing_record_pat_field_ref() {
        check_diagnostics(
            r"
struct S { s: u32 }
fn x(a: S) {
    let S { ref s } = a;
}
",
        )
    }

    #[test]
    fn range_mapping_out_of_macros() {
        // FIXME: this is very wrong, but somewhat tricky to fix.
        check_fix(
            r#"
fn some() {}
fn items() {}
fn here() {}

macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }

fn main() {
    let _x = id![Foo { a: $042 }];
}

pub struct Foo { pub a: i32, pub b: i32 }
"#,
            r#"
fn some(, b: () ) {}
fn items() {}
fn here() {}

macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }

fn main() {
    let _x = id![Foo { a: 42 }];
}

pub struct Foo { pub a: i32, pub b: i32 }
"#,
        );
    }

    #[test]
    fn test_fill_struct_fields_empty() {
        check_fix(
            r#"
struct TestStruct { one: i32, two: i64 }

fn test_fn() {
    let s = TestStruct {$0};
}
"#,
            r#"
struct TestStruct { one: i32, two: i64 }

fn test_fn() {
    let s = TestStruct { one: (), two: () };
}
"#,
        );
    }

    #[test]
    fn test_fill_struct_fields_self() {
        check_fix(
            r#"
struct TestStruct { one: i32 }

impl TestStruct {
    fn test_fn() { let s = Self {$0}; }
}
"#,
            r#"
struct TestStruct { one: i32 }

impl TestStruct {
    fn test_fn() { let s = Self { one: () }; }
}
"#,
        );
    }

    #[test]
    fn test_fill_struct_fields_enum() {
        check_fix(
            r#"
enum Expr {
    Bin { lhs: Box<Expr>, rhs: Box<Expr> }
}

impl Expr {
    fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
        Expr::Bin {$0 }
    }
}
"#,
            r#"
enum Expr {
    Bin { lhs: Box<Expr>, rhs: Box<Expr> }
}

impl Expr {
    fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
        Expr::Bin { lhs: (), rhs: () }
    }
}
"#,
        );
    }

    #[test]
    fn test_fill_struct_fields_partial() {
        check_fix(
            r#"
struct TestStruct { one: i32, two: i64 }

fn test_fn() {
    let s = TestStruct{ two: 2$0 };
}
"#,
            r"
struct TestStruct { one: i32, two: i64 }

fn test_fn() {
    let s = TestStruct{ two: 2, one: () };
}
",
        );
    }

    #[test]
    fn test_fill_struct_fields_raw_ident() {
        check_fix(
            r#"
struct TestStruct { r#type: u8 }

fn test_fn() {
    TestStruct { $0 };
}
"#,
            r"
struct TestStruct { r#type: u8 }

fn test_fn() {
    TestStruct { r#type: ()  };
}
",
        );
    }

    #[test]
    fn test_fill_struct_fields_no_diagnostic() {
        check_diagnostics(
            r#"
struct TestStruct { one: i32, two: i64 }

fn test_fn() {
    let one = 1;
    let s = TestStruct{ one, two: 2 };
}
        "#,
        );
    }

    #[test]
    fn test_fill_struct_fields_no_diagnostic_on_spread() {
        check_diagnostics(
            r#"
struct TestStruct { one: i32, two: i64 }

fn test_fn() {
    let one = 1;
    let s = TestStruct{ ..a };
}
"#,
        );
    }

    #[test]
    fn test_fill_struct_fields_blank_line() {
        check_fix(
            r#"
struct S { a: (), b: () }

fn f() {
    S {
        $0
    };
}
"#,
            r#"
struct S { a: (), b: () }

fn f() {
    S {
        a: (),
        b: (),
    };
}
"#,
        );
    }

    #[test]
    fn test_fill_struct_fields_shorthand() {
        check_fix(
            r#"
struct S { a: &'static str, b: i32 }

fn f() {
    let a = "hello";
    let b = 1i32;
    S {
        $0
    };
}
"#,
            r#"
struct S { a: &'static str, b: i32 }

fn f() {
    let a = "hello";
    let b = 1i32;
    S {
        a,
        b,
    };
}
"#,
        );
    }

    #[test]
    fn test_fill_struct_fields_shorthand_ty_mismatch() {
        check_fix(
            r#"
struct S { a: &'static str, b: i32 }

fn f() {
    let a = "hello";
    let b = 1usize;
    S {
        $0
    };
}
"#,
            r#"
struct S { a: &'static str, b: i32 }

fn f() {
    let a = "hello";
    let b = 1usize;
    S {
        a,
        b: (),
    };
}
"#,
        );
    }

    #[test]
    fn test_fill_struct_fields_shorthand_unifies() {
        check_fix(
            r#"
struct S<T> { a: &'static str, b: T }

fn f() {
    let a = "hello";
    let b = 1i32;
    S {
        $0
    };
}
"#,
            r#"
struct S<T> { a: &'static str, b: T }

fn f() {
    let a = "hello";
    let b = 1i32;
    S {
        a,
        b,
    };
}
"#,
        );
    }

    #[test]
    fn import_extern_crate_clash_with_inner_item() {
        // This is more of a resolver test, but doesn't really work with the hir_def testsuite.

        check_diagnostics(
            r#"
//- /lib.rs crate:lib deps:jwt
mod permissions;

use permissions::jwt;

fn f() {
    fn inner() {}
    jwt::Claims {}; // should resolve to the local one with 0 fields, and not get a diagnostic
}

//- /permissions.rs
pub mod jwt  {
    pub struct Claims {}
}

//- /jwt/lib.rs crate:jwt
pub struct Claims {
    field: u8,
}
        "#,
        );
    }
}
<|endoftext|>


<commit_before>use std::cmp::Ordering;

use libimagstore::storeid::StoreId;
use libimagstore::store::Entry;
use libimagstore::store::EntryHeader;
use libimagstore::store::Result as StoreResult;
use libimagerror::into::IntoError;

use error::LinkErrorKind as LEK;
use result::Result;

use toml::Value;
use itertools::Itertools;

pub type Link = StoreId;

pub trait InternalLinker {

    /// Get the internal links from the implementor object
    fn get_internal_links(&self) -> Result<Vec<Link>>;

    /// Set the internal links for the implementor object
    fn set_internal_links(&mut self, links: Vec<&mut Entry>) -> Result<Vec<Link>>;

    /// Add an internal link to the implementor object
    fn add_internal_link(&mut self, link: &mut Entry) -> Result<()>;

    /// Remove an internal link from the implementor object
    fn remove_internal_link(&mut self, link: &mut Entry) -> Result<()>;

}

impl InternalLinker for Entry {

    fn get_internal_links(&self) -> Result<Vec<Link>> {
        process_rw_result(self.get_header().read("imag.links"))
    }

    /// Set the links in a header and return the old links, if any.
    fn set_internal_links(&mut self, links: Vec<&mut Entry>) -> Result<Vec<Link>> {
        let self_location = self.get_location().clone();
        let mut new_links = vec![];

        for link in links {
            if let Err(e) = add_foreign_link(link, self_location.clone()) {
                return Err(e);
            }
            let link = link.get_location().clone();
            new_links.push(link);
        }

        let new_links = try!(links_into_values(new_links)
                             .into_iter()
                             .fold(Ok(vec![]), |acc, elem| {
                                acc.and_then(move |mut v| {
                                    match elem {
                                        None => Err(LEK::InternalConversionError.into()),
                                        Some(e) => {
                                            v.push(e);
                                            Ok(v)
                                        },
                                    }
                                })
                            }));
        process_rw_result(self.get_header_mut().set("imag.links", Value::Array(new_links)))
    }

    fn add_internal_link(&mut self, link: &mut Entry) -> Result<()> {
        let new_link = link.get_location().clone();

        add_foreign_link(link, self.get_location().clone())
            .and_then(|_| {
                self.get_internal_links()
                    .and_then(|mut links| {
                        links.push(new_link);
                        rewrite_links(self.get_header_mut(), links)
                    })
            })
    }

    fn remove_internal_link(&mut self, link: &mut Entry) -> Result<()> {
        let own_loc   = link.get_location().clone();
        let other_loc = link.get_location().clone();

        link.get_internal_links()
            .and_then(|links| {
                let links = links.into_iter().filter(|l| l.clone() != own_loc).collect();
                rewrite_links(self.get_header_mut(), links)
            })
            .and_then(|_| {
                self.get_internal_links()
                    .and_then(|links| {
                        let links = links.into_iter().filter(|l| l.clone() != other_loc).collect();
                        rewrite_links(link.get_header_mut(), links)
                    })
            })
    }

}

fn links_into_values(links: Vec<StoreId>) -> Vec<Option<Value>> {
    links
        .into_iter()
        .map(|s| s.to_str().map(String::from))
        .unique()
        .map(|elem| elem.map(Value::String))
        .sorted_by(|a, b| {
            match (a, b) {
                (&Some(Value::String(ref a)), &Some(Value::String(ref b))) => Ord::cmp(a, b),
                (&None, _) | (_, &None) => Ordering::Equal,
                _                                              => unreachable!()
            }
        })
}

fn rewrite_links(header: &mut EntryHeader, links: Vec<StoreId>) -> Result<()> {
    let links = try!(links_into_values(links)
                     .into_iter()
                     .fold(Ok(vec![]), |acc, elem| {
                        acc.and_then(move |mut v| {
                            match elem {
                                None => Err(LEK::InternalConversionError.into()),
                                Some(e) => {
                                    v.push(e);
                                    Ok(v)
                                },
                            }
                        })
                     }));

    let process = header.set("imag.links", Value::Array(links));
    process_rw_result(process).map(|_| ())
}

/// When Linking A -> B, the specification wants us to link back B -> A.
/// This is a helper function which does this.
fn add_foreign_link(target: &mut Entry, from: StoreId) -> Result<()> {
    target.get_internal_links()
        .and_then(|mut links| {
            links.push(from);
            let links = links_into_values(links);
            if links.iter().any(|o| o.is_none()) {
                Err(LEK::InternalConversionError.into())
            } else {
                let links = links.into_iter().map(|opt| opt.unwrap()).collect();
                process_rw_result(target.get_header_mut().set("imag.links", Value::Array(links)))
                    .map(|_| ())
            }
        })
}

fn process_rw_result(links: StoreResult<Option<Value>>) -> Result<Vec<Link>> {
    let links = match links {
        Err(e) => {
            debug!("RW action on store failed. Generating LinkError");
            return Err(LEK::EntryHeaderReadError.into_error_with_cause(Box::new(e)))
        },
        Ok(None) => {
            debug!("We got no value from the header!");
            return Ok(vec![])
        },
        Ok(Some(Value::Array(l))) => l,
        Ok(Some(_)) => {
            debug!("We expected an Array for the links, but there was a non-Array!");
            return Err(LEK::ExistingLinkTypeWrong.into());
        }
    };

    if !links.iter().all(|l| is_match!(*l, Value::String(_))) {
        debug!("At least one of the Values which were expected in the Array of links is a non-String!");
        debug!("Generating LinkError");
        return Err(LEK::ExistingLinkTypeWrong.into());
    }

    let links : Vec<Link> = links.into_iter()
        .map(|link| {
            match link {
                Value::String(s) => StoreId::from(s),
                _ => unreachable!(),
            }
        })
        .collect();

    debug!("Ok, the RW action was successful, returning link vector now!");
    Ok(links)
}

<commit_msg>Replace map(unwrap()) with fold()<commit_after>use std::cmp::Ordering;

use libimagstore::storeid::StoreId;
use libimagstore::store::Entry;
use libimagstore::store::EntryHeader;
use libimagstore::store::Result as StoreResult;
use libimagerror::into::IntoError;

use error::LinkErrorKind as LEK;
use result::Result;

use toml::Value;
use itertools::Itertools;

pub type Link = StoreId;

pub trait InternalLinker {

    /// Get the internal links from the implementor object
    fn get_internal_links(&self) -> Result<Vec<Link>>;

    /// Set the internal links for the implementor object
    fn set_internal_links(&mut self, links: Vec<&mut Entry>) -> Result<Vec<Link>>;

    /// Add an internal link to the implementor object
    fn add_internal_link(&mut self, link: &mut Entry) -> Result<()>;

    /// Remove an internal link from the implementor object
    fn remove_internal_link(&mut self, link: &mut Entry) -> Result<()>;

}

impl InternalLinker for Entry {

    fn get_internal_links(&self) -> Result<Vec<Link>> {
        process_rw_result(self.get_header().read("imag.links"))
    }

    /// Set the links in a header and return the old links, if any.
    fn set_internal_links(&mut self, links: Vec<&mut Entry>) -> Result<Vec<Link>> {
        let self_location = self.get_location().clone();
        let mut new_links = vec![];

        for link in links {
            if let Err(e) = add_foreign_link(link, self_location.clone()) {
                return Err(e);
            }
            let link = link.get_location().clone();
            new_links.push(link);
        }

        let new_links = try!(links_into_values(new_links)
                             .into_iter()
                             .fold(Ok(vec![]), |acc, elem| {
                                acc.and_then(move |mut v| {
                                    match elem {
                                        None => Err(LEK::InternalConversionError.into()),
                                        Some(e) => {
                                            v.push(e);
                                            Ok(v)
                                        },
                                    }
                                })
                            }));
        process_rw_result(self.get_header_mut().set("imag.links", Value::Array(new_links)))
    }

    fn add_internal_link(&mut self, link: &mut Entry) -> Result<()> {
        let new_link = link.get_location().clone();

        add_foreign_link(link, self.get_location().clone())
            .and_then(|_| {
                self.get_internal_links()
                    .and_then(|mut links| {
                        links.push(new_link);
                        rewrite_links(self.get_header_mut(), links)
                    })
            })
    }

    fn remove_internal_link(&mut self, link: &mut Entry) -> Result<()> {
        let own_loc   = link.get_location().clone();
        let other_loc = link.get_location().clone();

        link.get_internal_links()
            .and_then(|links| {
                let links = links.into_iter().filter(|l| l.clone() != own_loc).collect();
                rewrite_links(self.get_header_mut(), links)
            })
            .and_then(|_| {
                self.get_internal_links()
                    .and_then(|links| {
                        let links = links.into_iter().filter(|l| l.clone() != other_loc).collect();
                        rewrite_links(link.get_header_mut(), links)
                    })
            })
    }

}

fn links_into_values(links: Vec<StoreId>) -> Vec<Option<Value>> {
    links
        .into_iter()
        .map(|s| s.to_str().map(String::from))
        .unique()
        .map(|elem| elem.map(Value::String))
        .sorted_by(|a, b| {
            match (a, b) {
                (&Some(Value::String(ref a)), &Some(Value::String(ref b))) => Ord::cmp(a, b),
                (&None, _) | (_, &None) => Ordering::Equal,
                _                                              => unreachable!()
            }
        })
}

fn rewrite_links(header: &mut EntryHeader, links: Vec<StoreId>) -> Result<()> {
    let links = try!(links_into_values(links)
                     .into_iter()
                     .fold(Ok(vec![]), |acc, elem| {
                        acc.and_then(move |mut v| {
                            match elem {
                                None => Err(LEK::InternalConversionError.into()),
                                Some(e) => {
                                    v.push(e);
                                    Ok(v)
                                },
                            }
                        })
                     }));

    let process = header.set("imag.links", Value::Array(links));
    process_rw_result(process).map(|_| ())
}

/// When Linking A -> B, the specification wants us to link back B -> A.
/// This is a helper function which does this.
fn add_foreign_link(target: &mut Entry, from: StoreId) -> Result<()> {
    target.get_internal_links()
        .and_then(|mut links| {
            links.push(from);
            let links = try!(links_into_values(links)
                             .into_iter()
                             .fold(Ok(vec![]), |acc, elem| {
                                acc.and_then(move |mut v| {
                                    match elem {
                                        None => Err(LEK::InternalConversionError.into()),
                                        Some(e) => {
                                            v.push(e);
                                            Ok(v)
                                        },
                                    }
                                })
                             }));
            process_rw_result(target.get_header_mut().set("imag.links", Value::Array(links)))
                .map(|_| ())
        })
}

fn process_rw_result(links: StoreResult<Option<Value>>) -> Result<Vec<Link>> {
    let links = match links {
        Err(e) => {
            debug!("RW action on store failed. Generating LinkError");
            return Err(LEK::EntryHeaderReadError.into_error_with_cause(Box::new(e)))
        },
        Ok(None) => {
            debug!("We got no value from the header!");
            return Ok(vec![])
        },
        Ok(Some(Value::Array(l))) => l,
        Ok(Some(_)) => {
            debug!("We expected an Array for the links, but there was a non-Array!");
            return Err(LEK::ExistingLinkTypeWrong.into());
        }
    };

    if !links.iter().all(|l| is_match!(*l, Value::String(_))) {
        debug!("At least one of the Values which were expected in the Array of links is a non-String!");
        debug!("Generating LinkError");
        return Err(LEK::ExistingLinkTypeWrong.into());
    }

    let links : Vec<Link> = links.into_iter()
        .map(|link| {
            match link {
                Value::String(s) => StoreId::from(s),
                _ => unreachable!(),
            }
        })
        .collect();

    debug!("Ok, the RW action was successful, returning link vector now!");
    Ok(links)
}

<|endoftext|>