<commit_before>use ide_db::defs::{Definition, NameRefClass};
use syntax::{ast, AstNode, SyntaxKind, T};

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

// Assist: add_turbo_fish
//
// Adds `::<_>` to a call of a generic method or function.
//
// ```
// fn make<T>() -> T { todo!() }
// fn main() {
//     let x = make$0();
// }
// ```
// ->
// ```
// fn make<T>() -> T { todo!() }
// fn main() {
//     let x = make::<${0:_}>();
// }
// ```
pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let ident = ctx.find_token_syntax_at_offset(SyntaxKind::IDENT).or_else(|| {
        let arg_list = ctx.find_node_at_offset::<ast::ArgList>()?;
        if arg_list.args().next().is_some() {
            return None;
        }
        cov_mark::hit!(add_turbo_fish_after_call);
        cov_mark::hit!(add_type_ascription_after_call);
        arg_list.l_paren_token()?.prev_token().filter(|it| it.kind() == SyntaxKind::IDENT)
    })?;
    let next_token = ident.next_token()?;
    if next_token.kind() == T![::] {
        cov_mark::hit!(add_turbo_fish_one_fish_is_enough);
        return None;
    }
    let name_ref = ast::NameRef::cast(ident.parent()?)?;
    let def = match NameRefClass::classify(&ctx.sema, &name_ref)? {
        NameRefClass::Definition(def) => def,
        NameRefClass::FieldShorthand { .. } => return None,
    };
    let fun = match def {
        Definition::Function(it) => it,
        _ => return None,
    };
    let generics = hir::GenericDef::Function(fun).params(ctx.sema.db);
    if generics.is_empty() {
        cov_mark::hit!(add_turbo_fish_non_generic);
        return None;
    }

    if let Some(let_stmt) = ctx.find_node_at_offset::<ast::LetStmt>() {
        if let_stmt.colon_token().is_none() {
            let type_pos = let_stmt.pat()?.syntax().last_token()?.text_range().end();
            let semi_pos = let_stmt.syntax().last_token()?.text_range().end();

            acc.add(
                AssistId("add_type_ascription", AssistKind::RefactorRewrite),
                "Add `: _` before assignment operator",
                ident.text_range(),
                |builder| {
                    if let_stmt.semicolon_token().is_none() {
                        builder.insert(semi_pos, ";");
                    }
                    match ctx.config.snippet_cap {
                        Some(cap) => builder.insert_snippet(cap, type_pos, ": ${0:_}"),
                        None => builder.insert(type_pos, ": _"),
                    }
                },
            )?
        } else {
            cov_mark::hit!(add_type_ascription_already_typed);
        }
    }

    acc.add(
        AssistId("add_turbo_fish", AssistKind::RefactorRewrite),
        "Add `::<>`",
        ident.text_range(),
        |builder| match ctx.config.snippet_cap {
            Some(cap) => builder.insert_snippet(cap, ident.text_range().end(), "::<${0:_}>"),
            None => builder.insert(ident.text_range().end(), "::<_>"),
        },
    )
}

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

    use super::*;

    #[test]
    fn add_turbo_fish_function() {
        check_assist(
            add_turbo_fish,
            r#"
fn make<T>() -> T {}
fn main() {
    make$0();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    make::<${0:_}>();
}
"#,
        );
    }

    #[test]
    fn add_turbo_fish_after_call() {
        cov_mark::check!(add_turbo_fish_after_call);
        check_assist(
            add_turbo_fish,
            r#"
fn make<T>() -> T {}
fn main() {
    make()$0;
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    make::<${0:_}>();
}
"#,
        );
    }

    #[test]
    fn add_turbo_fish_method() {
        check_assist(
            add_turbo_fish,
            r#"
struct S;
impl S {
    fn make<T>(&self) -> T {}
}
fn main() {
    S.make$0();
}
"#,
            r#"
struct S;
impl S {
    fn make<T>(&self) -> T {}
}
fn main() {
    S.make::<${0:_}>();
}
"#,
        );
    }

    #[test]
    fn add_turbo_fish_one_fish_is_enough() {
        cov_mark::check!(add_turbo_fish_one_fish_is_enough);
        check_assist_not_applicable(
            add_turbo_fish,
            r#"
fn make<T>() -> T {}
fn main() {
    make$0::<()>();
}
"#,
        );
    }

    #[test]
    fn add_turbo_fish_non_generic() {
        cov_mark::check!(add_turbo_fish_non_generic);
        check_assist_not_applicable(
            add_turbo_fish,
            r#"
fn make() -> () {}
fn main() {
    make$0();
}
"#,
        );
    }

    #[test]
    fn add_type_ascription_function() {
        check_assist_by_label(
            add_turbo_fish,
            r#"
fn make<T>() -> T {}
fn main() {
    let x = make$0();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let x: ${0:_} = make();
}
"#,
            "Add `: _` before assignment operator",
        );
    }

    #[test]
    fn add_type_ascription_after_call() {
        cov_mark::check!(add_type_ascription_after_call);
        check_assist_by_label(
            add_turbo_fish,
            r#"
fn make<T>() -> T {}
fn main() {
    let x = make()$0;
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let x: ${0:_} = make();
}
"#,
            "Add `: _` before assignment operator",
        );
    }

    #[test]
    fn add_type_ascription_method() {
        check_assist_by_label(
            add_turbo_fish,
            r#"
struct S;
impl S {
    fn make<T>(&self) -> T {}
}
fn main() {
    let x = S.make$0();
}
"#,
            r#"
struct S;
impl S {
    fn make<T>(&self) -> T {}
}
fn main() {
    let x: ${0:_} = S.make();
}
"#,
            "Add `: _` before assignment operator",
        );
    }

    #[test]
    fn add_type_ascription_already_typed() {
        cov_mark::check!(add_type_ascription_already_typed);
        check_assist(
            add_turbo_fish,
            r#"
fn make<T>() -> T {}
fn main() {
    let x: () = make$0();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let x: () = make::<${0:_}>();
}
"#,
        );
    }

    #[test]
    fn add_type_ascription_append_semicolon() {
        check_assist_by_label(
            add_turbo_fish,
            r#"
fn make<T>() -> T {}
fn main() {
    let x = make$0()
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let x: ${0:_} = make();
}
"#,
            "Add `: _` before assignment operator",
        );
    }
}
<commit_msg>turbo fish supports multiple type arguments<commit_after>use ide_db::defs::{Definition, NameRefClass};
use itertools::Itertools;
use syntax::{ast, AstNode, SyntaxKind, T};

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

// Assist: add_turbo_fish
//
// Adds `::<_>` to a call of a generic method or function.
//
// ```
// fn make<T>() -> T { todo!() }
// fn main() {
//     let x = make$0();
// }
// ```
// ->
// ```
// fn make<T>() -> T { todo!() }
// fn main() {
//     let x = make::<${0:_}>();
// }
// ```
pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let ident = ctx.find_token_syntax_at_offset(SyntaxKind::IDENT).or_else(|| {
        let arg_list = ctx.find_node_at_offset::<ast::ArgList>()?;
        if arg_list.args().next().is_some() {
            return None;
        }
        cov_mark::hit!(add_turbo_fish_after_call);
        cov_mark::hit!(add_type_ascription_after_call);
        arg_list.l_paren_token()?.prev_token().filter(|it| it.kind() == SyntaxKind::IDENT)
    })?;
    let next_token = ident.next_token()?;
    if next_token.kind() == T![::] {
        cov_mark::hit!(add_turbo_fish_one_fish_is_enough);
        return None;
    }
    let name_ref = ast::NameRef::cast(ident.parent()?)?;
    let def = match NameRefClass::classify(&ctx.sema, &name_ref)? {
        NameRefClass::Definition(def) => def,
        NameRefClass::FieldShorthand { .. } => return None,
    };
    let fun = match def {
        Definition::Function(it) => it,
        _ => return None,
    };
    let generics = hir::GenericDef::Function(fun).params(ctx.sema.db);
    if generics.is_empty() {
        cov_mark::hit!(add_turbo_fish_non_generic);
        return None;
    }

    if let Some(let_stmt) = ctx.find_node_at_offset::<ast::LetStmt>() {
        if let_stmt.colon_token().is_none() {
            let type_pos = let_stmt.pat()?.syntax().last_token()?.text_range().end();
            let semi_pos = let_stmt.syntax().last_token()?.text_range().end();

            acc.add(
                AssistId("add_type_ascription", AssistKind::RefactorRewrite),
                "Add `: _` before assignment operator",
                ident.text_range(),
                |builder| {
                    if let_stmt.semicolon_token().is_none() {
                        builder.insert(semi_pos, ";");
                    }
                    match ctx.config.snippet_cap {
                        Some(cap) => builder.insert_snippet(cap, type_pos, ": ${0:_}"),
                        None => builder.insert(type_pos, ": _"),
                    }
                },
            )?
        } else {
            cov_mark::hit!(add_type_ascription_already_typed);
        }
    }

    let number_of_arguments = generics.len();
    let fish_head = std::iter::repeat("_").take(number_of_arguments).collect_vec().join(",");

    acc.add(
        AssistId("add_turbo_fish", AssistKind::RefactorRewrite),
        "Add `::<>`",
        ident.text_range(),
        |builder| match ctx.config.snippet_cap {
            Some(cap) => {
                let snip = format!("::<${{0:{}}}>", fish_head);
                builder.insert_snippet(cap, ident.text_range().end(), snip)
            },
            None => {
                let snip = format!("::<{}>", fish_head);
                builder.insert(ident.text_range().end(), snip);
            },
        },
    )
}

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

    use super::*;

    #[test]
    fn add_turbo_fish_function() {
        check_assist(
            add_turbo_fish,
            r#"
fn make<T>() -> T {}
fn main() {
    make$0();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    make::<${0:_}>();
}
"#,
        );
    }

    #[test]
    fn add_turbo_fish_function_multiple_generic_types() {
        check_assist(
            add_turbo_fish,
            r#"
fn make<T, A>() -> T {}
fn main() {
    make$0();
}
"#,
            r#"
fn make<T, A>() -> T {}
fn main() {
    make::<${0:_,_}>();
}
"#,
        );
    }

    #[test]
    fn add_turbo_fish_function_many_generic_types() {
        check_assist(
            add_turbo_fish,
            r#"
fn make<T, A, B, C, D, E, F>() -> T {}
fn main() {
    make$0();
}
"#,
            r#"
fn make<T, A, B, C, D, E, F>() -> T {}
fn main() {
    make::<${0:_,_,_,_,_,_,_}>();
}
"#,
        );
    }

    #[test]
    fn add_turbo_fish_after_call() {
        cov_mark::check!(add_turbo_fish_after_call);
        check_assist(
            add_turbo_fish,
            r#"
fn make<T>() -> T {}
fn main() {
    make()$0;
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    make::<${0:_}>();
}
"#,
        );
    }

    #[test]
    fn add_turbo_fish_method() {
        check_assist(
            add_turbo_fish,
            r#"
struct S;
impl S {
    fn make<T>(&self) -> T {}
}
fn main() {
    S.make$0();
}
"#,
            r#"
struct S;
impl S {
    fn make<T>(&self) -> T {}
}
fn main() {
    S.make::<${0:_}>();
}
"#,
        );
    }

    #[test]
    fn add_turbo_fish_one_fish_is_enough() {
        cov_mark::check!(add_turbo_fish_one_fish_is_enough);
        check_assist_not_applicable(
            add_turbo_fish,
            r#"
fn make<T>() -> T {}
fn main() {
    make$0::<()>();
}
"#,
        );
    }

    #[test]
    fn add_turbo_fish_non_generic() {
        cov_mark::check!(add_turbo_fish_non_generic);
        check_assist_not_applicable(
            add_turbo_fish,
            r#"
fn make() -> () {}
fn main() {
    make$0();
}
"#,
        );
    }

    #[test]
    fn add_type_ascription_function() {
        check_assist_by_label(
            add_turbo_fish,
            r#"
fn make<T>() -> T {}
fn main() {
    let x = make$0();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let x: ${0:_} = make();
}
"#,
            "Add `: _` before assignment operator",
        );
    }

    #[test]
    fn add_type_ascription_after_call() {
        cov_mark::check!(add_type_ascription_after_call);
        check_assist_by_label(
            add_turbo_fish,
            r#"
fn make<T>() -> T {}
fn main() {
    let x = make()$0;
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let x: ${0:_} = make();
}
"#,
            "Add `: _` before assignment operator",
        );
    }

    #[test]
    fn add_type_ascription_method() {
        check_assist_by_label(
            add_turbo_fish,
            r#"
struct S;
impl S {
    fn make<T>(&self) -> T {}
}
fn main() {
    let x = S.make$0();
}
"#,
            r#"
struct S;
impl S {
    fn make<T>(&self) -> T {}
}
fn main() {
    let x: ${0:_} = S.make();
}
"#,
            "Add `: _` before assignment operator",
        );
    }

    #[test]
    fn add_type_ascription_already_typed() {
        cov_mark::check!(add_type_ascription_already_typed);
        check_assist(
            add_turbo_fish,
            r#"
fn make<T>() -> T {}
fn main() {
    let x: () = make$0();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let x: () = make::<${0:_}>();
}
"#,
        );
    }

    #[test]
    fn add_type_ascription_append_semicolon() {
        check_assist_by_label(
            add_turbo_fish,
            r#"
fn make<T>() -> T {}
fn main() {
    let x = make$0()
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let x: ${0:_} = make();
}
"#,
            "Add `: _` before assignment operator",
        );
    }
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here

use ra_syntax::{
    ast::{self, ModuleItemOwner},
    match_ast, AstNode,
};
use rustc_hash::FxHashMap;

use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions};

/// Complete repeated parameters, both name and type. For example, if all
/// functions in a file have a `spam: &mut Spam` parameter, a completion with
/// `spam: &mut Spam` insert text/label and `spam` lookup string will be
/// suggested.
pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext) {
    if !ctx.is_param {
        return;
    }

    let mut params = FxHashMap::default();
    for node in ctx.token.parent().ancestors() {
        let items = match_ast! {
            match node {
                ast::SourceFile(it) => it.items(),
                ast::ItemList(it) => it.items(),
                _ => continue,
            }
        };
        for item in items {
            if let ast::ModuleItem::FnDef(func) = item {
                func.param_list().into_iter().flat_map(|it| it.params()).for_each(|param| {
                    let text = param.syntax().text().to_string();
                    params.entry(text).or_insert((0, param)).0 += 1;
                })
            }
        }
    }
    params
        .into_iter()
        .filter_map(|(label, (count, param))| {
            let lookup = param.pat()?.syntax().text().to_string();
            if count < 2 {
                None
            } else {
                Some((label, lookup))
            }
        })
        .for_each(|(label, lookup)| {
            CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label)
                .kind(crate::CompletionItemKind::Binding)
                .lookup_by(lookup)
                .add_to(acc)
        });
}

#[cfg(test)]
mod tests {
    use expect::{expect, Expect};

    use crate::completion::{test_utils::completion_list, CompletionKind};

    fn check(ra_fixture: &str, expect: Expect) {
        let actual = completion_list(ra_fixture, CompletionKind::Magic);
        expect.assert_eq(&actual);
    }

    #[test]
    fn test_param_completion_last_param() {
        check(
            r#"
fn foo(file_id: FileId) {}
fn bar(file_id: FileId) {}
fn baz(file<|>) {}
"#,
            expect![[r#"
                bn file_id: FileId
            "#]],
        );
    }

    #[test]
    fn test_param_completion_nth_param() {
        check(
            r#"
fn foo(file_id: FileId) {}
fn bar(file_id: FileId) {}
fn baz(file<|>, x: i32) {}
"#,
            expect![[r#"
                bn file_id: FileId
            "#]],
        );
    }

    #[test]
    fn test_param_completion_trait_param() {
        check(
            r#"
pub(crate) trait SourceRoot {
    pub fn contains(&self, file_id: FileId) -> bool;
    pub fn module_map(&self) -> &ModuleMap;
    pub fn lines(&self, file_id: FileId) -> &LineIndex;
    pub fn syntax(&self, file<|>)
}
"#,
            expect![[r#"
                bn file_id: FileId
            "#]],
        );
    }
}
<commit_msg>Complete parameters more aggressively<commit_after>//! See `complete_fn_param`.

use ra_syntax::{
    ast::{self, ModuleItemOwner},
    match_ast, AstNode,
};
use rustc_hash::FxHashMap;

use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions};

/// Complete repeated parameters, both name and type. For example, if all
/// functions in a file have a `spam: &mut Spam` parameter, a completion with
/// `spam: &mut Spam` insert text/label and `spam` lookup string will be
/// suggested.
pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext) {
    if !ctx.is_param {
        return;
    }

    let mut params = FxHashMap::default();
    let mut me = None;
    for node in ctx.token.parent().ancestors() {
        let items = match_ast! {
            match node {
                ast::SourceFile(it) => it.items(),
                ast::ItemList(it) => it.items(),
                ast::FnDef(it) => {
                    me = Some(it);
                    continue;
                },
                _ => continue,
            }
        };
        for item in items {
            if let ast::ModuleItem::FnDef(func) = item {
                if Some(&func) == me.as_ref() {
                    continue;
                }
                func.param_list().into_iter().flat_map(|it| it.params()).for_each(|param| {
                    let text = param.syntax().text().to_string();
                    params.entry(text).or_insert(param);
                })
            }
        }
    }
    params
        .into_iter()
        .filter_map(|(label, param)| {
            let lookup = param.pat()?.syntax().text().to_string();
            Some((label, lookup))
        })
        .for_each(|(label, lookup)| {
            CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label)
                .kind(crate::CompletionItemKind::Binding)
                .lookup_by(lookup)
                .add_to(acc)
        });
}

#[cfg(test)]
mod tests {
    use expect::{expect, Expect};

    use crate::completion::{test_utils::completion_list, CompletionKind};

    fn check(ra_fixture: &str, expect: Expect) {
        let actual = completion_list(ra_fixture, CompletionKind::Magic);
        expect.assert_eq(&actual);
    }

    #[test]
    fn test_param_completion_last_param() {
        check(
            r#"
fn foo(file_id: FileId) {}
fn bar(file_id: FileId) {}
fn baz(file<|>) {}
"#,
            expect![[r#"
                bn file_id: FileId
            "#]],
        );
    }

    #[test]
    fn test_param_completion_nth_param() {
        check(
            r#"
fn foo(file_id: FileId) {}
fn baz(file<|>, x: i32) {}
"#,
            expect![[r#"
                bn file_id: FileId
            "#]],
        );
    }

    #[test]
    fn test_param_completion_trait_param() {
        check(
            r#"
pub(crate) trait SourceRoot {
    pub fn contains(&self, file_id: FileId) -> bool;
    pub fn module_map(&self) -> &ModuleMap;
    pub fn lines(&self, file_id: FileId) -> &LineIndex;
    pub fn syntax(&self, file<|>)
}
"#,
            expect![[r#"
                bn file_id: FileId
            "#]],
        );
    }
}
<|endoftext|>


<commit_before>use std::cmp;

use bytes::Bytes;

use error;

use solicit::session::StreamState;
use solicit::WindowSize;
use solicit::header::Headers;
use solicit::connection::EndStream;

use futures_misc::ResultOrEof;

use stream_part::*;

use error::ErrorCode;

use super::types::Types;

use super::stream_queue::StreamQueue;
use super::stream_queue_sync::StreamQueueSyncSender;
use super::window_size;

pub enum HttpStreamCommand {
    Headers(Headers, EndStream),
    Data(Bytes, EndStream),
    Rst(ErrorCode),
}

impl HttpStreamCommand {
    pub fn from(part: HttpStreamPart) -> HttpStreamCommand {
        let end_stream = match part.last {
            true => EndStream::Yes,
            false => EndStream::No,
        };
        match part.content {
            HttpStreamPartContent::Data(data) => {
                HttpStreamCommand::Data(data, end_stream)
            },
            HttpStreamPartContent::Headers(headers) => {
                HttpStreamCommand::Headers(headers, end_stream)
            },
        }
    }
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct HttpStreamStateSnapshot {
    pub state: StreamState,
    pub out_window_size: i32,
    pub in_window_size: i32,
    pub out_data_size: usize,
}

pub struct HttpStreamCommon<T : Types> {
    pub specific: T::HttpStreamSpecific,
    pub state: StreamState,
    pub out_window_size: WindowSize,
    pub in_window_size: WindowSize,
    pub outgoing: StreamQueue,
    pub peer_tx: Option<StreamQueueSyncSender>,
    // task waiting for window increase
    pub pump_out_window: window_size::StreamOutWindowSender,
}

impl<T : Types> HttpStreamCommon<T> {
    pub fn new(
        in_window_size: u32,
        out_window_size: u32,
        incoming: StreamQueueSyncSender,
        pump_out_window: window_size::StreamOutWindowSender,
        specific: T::HttpStreamSpecific)
            -> HttpStreamCommon<T>
    {
        HttpStreamCommon {
            specific: specific,
            state: StreamState::Open,
            in_window_size: WindowSize::new(in_window_size as i32),
            out_window_size: WindowSize::new(out_window_size as i32),
            outgoing: StreamQueue::new(),
            peer_tx: Some(incoming),
            pump_out_window: pump_out_window,
        }
    }

    pub fn snapshot(&self) -> HttpStreamStateSnapshot {
        HttpStreamStateSnapshot {
            state: self.state,
            out_window_size: self.out_window_size.0,
            in_window_size: self.in_window_size.0,
            out_data_size: self.outgoing.data_size(),
        }
    }

    pub fn close_local(&mut self) {
        trace!("close local");
        self.state = match self.state {
            StreamState::Closed | StreamState::HalfClosedRemote => StreamState::Closed,
            _ => StreamState::HalfClosedLocal,
        };
    }

    pub fn close_remote(&mut self) {
        trace!("close remote");
        self.state = match self.state {
            StreamState::Closed | StreamState::HalfClosedLocal => StreamState::Closed,
            _ => StreamState::HalfClosedRemote,
        };

        if let Some(response_handler) = self.peer_tx.take() {
            // TODO: reset on error
            drop(response_handler.send(ResultOrEof::Eof));
        }
    }

    pub fn pop_outg(&mut self, conn_out_window_size: &mut WindowSize) -> Option<HttpStreamCommand> {
        if self.outgoing.is_empty() {
            return
                if let Some(error_code) = self.outgoing.end() {
                    if self.state.is_closed_local() {
                        None
                    } else {
                        self.close_local();
                        Some(match error_code {
                            ErrorCode::NoError => HttpStreamCommand::Data(Bytes::new(), EndStream::Yes),
                            error_code => HttpStreamCommand::Rst(error_code),
                        })
                    }
                } else {
                    None
                };
        }

        let pop_headers =
            if let &HttpStreamPartContent::Headers(..) = self.outgoing.front().unwrap() {
                true
            } else {
                false
            };
        if pop_headers {
            let r = self.outgoing.pop_front().unwrap();
            let last = self.outgoing.end() == Some(ErrorCode::NoError);
            if last {
                self.close_local();
            }
            return Some(HttpStreamCommand::from(HttpStreamPart {
                content: r,
                last: last,
            }))
        }

        if self.out_window_size.size() <= 0 || conn_out_window_size.size() <= 0 {
            return None
        }

        let mut data =
            if let Some(HttpStreamPartContent::Data(data)) = self.outgoing.pop_front() {
                data
            } else {
                unreachable!()
            };

        // Min of connection and stream window size
        let max_window = cmp::min(self.out_window_size.size(), conn_out_window_size.size());

        if data.len() as usize > max_window as usize {
            trace!("truncating data of len {} to {}", data.len(), max_window);
            let size = max_window as usize;
            let rem = data.split_off(size);
            self.outgoing.push_front(HttpStreamPartContent::Data(rem));
        };

        self.out_window_size.try_decrease(data.len() as i32).unwrap();
        conn_out_window_size.try_decrease(data.len() as i32).unwrap();

        let last = self.outgoing.end() == Some(ErrorCode::NoError);
        if last {
            self.close_local();
        }

        Some(HttpStreamCommand::from(HttpStreamPart {
            content: HttpStreamPartContent::Data(data),
            last: last,
        }))
    }

    pub fn _pop_outg_all(&mut self, conn_out_window_size: &mut WindowSize) -> Vec<HttpStreamCommand> {
        let mut r = Vec::new();
        while let Some(p) = self.pop_outg(conn_out_window_size) {
            r.push(p);
        }
        r
    }

    pub fn new_data_chunk(&mut self, data: &[u8], last: bool) {
        if let Some(ref mut response_handler) = self.peer_tx {
            // TODO: reset stream if rx is dead
            drop(response_handler.send(ResultOrEof::Item(HttpStreamPart {
                content: HttpStreamPartContent::Data(Bytes::from(data)),
                last: last,
            })));
        }
    }

    pub fn rst(&mut self, error_code: ErrorCode) {
        if let Some(ref mut response_handler) = self.peer_tx.take() {
            drop(response_handler.send(ResultOrEof::Error(error::Error::CodeError(error_code))));
        }
    }

    pub fn goaway_recvd(&mut self, _raw_error_code: u32) {
        if let Some(response_handler) = self.peer_tx.take() {
            // it is OK to ignore error: handler may be already dead
            drop(response_handler.send(ResultOrEof::Error(error::Error::Other("peer sent GOAWAY"))));
        }
    }
}

pub trait HttpStreamDataSpecific {
}

pub trait HttpStream {
    type Types : Types;
}

<commit_msg>Self-test than window size doesn't underflow<commit_after>use std::cmp;

use bytes::Bytes;

use error;

use solicit::session::StreamState;
use solicit::WindowSize;
use solicit::header::Headers;
use solicit::connection::EndStream;

use futures_misc::ResultOrEof;

use stream_part::*;

use error::ErrorCode;

use super::types::Types;

use super::stream_queue::StreamQueue;
use super::stream_queue_sync::StreamQueueSyncSender;
use super::window_size;

pub enum HttpStreamCommand {
    Headers(Headers, EndStream),
    Data(Bytes, EndStream),
    Rst(ErrorCode),
}

impl HttpStreamCommand {
    pub fn from(part: HttpStreamPart) -> HttpStreamCommand {
        let end_stream = match part.last {
            true => EndStream::Yes,
            false => EndStream::No,
        };
        match part.content {
            HttpStreamPartContent::Data(data) => {
                HttpStreamCommand::Data(data, end_stream)
            },
            HttpStreamPartContent::Headers(headers) => {
                HttpStreamCommand::Headers(headers, end_stream)
            },
        }
    }
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct HttpStreamStateSnapshot {
    pub state: StreamState,
    pub out_window_size: i32,
    pub in_window_size: i32,
    pub out_data_size: usize,
}

pub struct HttpStreamCommon<T : Types> {
    pub specific: T::HttpStreamSpecific,
    pub state: StreamState,
    pub out_window_size: WindowSize,
    pub in_window_size: WindowSize,
    pub outgoing: StreamQueue,
    pub peer_tx: Option<StreamQueueSyncSender>,
    // task waiting for window increase
    pub pump_out_window: window_size::StreamOutWindowSender,
}

impl<T : Types> HttpStreamCommon<T> {
    pub fn new(
        in_window_size: u32,
        out_window_size: u32,
        incoming: StreamQueueSyncSender,
        pump_out_window: window_size::StreamOutWindowSender,
        specific: T::HttpStreamSpecific)
            -> HttpStreamCommon<T>
    {
        HttpStreamCommon {
            specific: specific,
            state: StreamState::Open,
            in_window_size: WindowSize::new(in_window_size as i32),
            out_window_size: WindowSize::new(out_window_size as i32),
            outgoing: StreamQueue::new(),
            peer_tx: Some(incoming),
            pump_out_window: pump_out_window,
        }
    }

    pub fn snapshot(&self) -> HttpStreamStateSnapshot {
        HttpStreamStateSnapshot {
            state: self.state,
            out_window_size: self.out_window_size.0,
            in_window_size: self.in_window_size.0,
            out_data_size: self.outgoing.data_size(),
        }
    }

    pub fn close_local(&mut self) {
        trace!("close local");
        self.state = match self.state {
            StreamState::Closed | StreamState::HalfClosedRemote => StreamState::Closed,
            _ => StreamState::HalfClosedLocal,
        };
    }

    pub fn close_remote(&mut self) {
        trace!("close remote");
        self.state = match self.state {
            StreamState::Closed | StreamState::HalfClosedLocal => StreamState::Closed,
            _ => StreamState::HalfClosedRemote,
        };

        if let Some(response_handler) = self.peer_tx.take() {
            // TODO: reset on error
            drop(response_handler.send(ResultOrEof::Eof));
        }
    }

    pub fn pop_outg(&mut self, conn_out_window_size: &mut WindowSize) -> Option<HttpStreamCommand> {
        if self.outgoing.is_empty() {
            return
                if let Some(error_code) = self.outgoing.end() {
                    if self.state.is_closed_local() {
                        None
                    } else {
                        self.close_local();
                        Some(match error_code {
                            ErrorCode::NoError => HttpStreamCommand::Data(Bytes::new(), EndStream::Yes),
                            error_code => HttpStreamCommand::Rst(error_code),
                        })
                    }
                } else {
                    None
                };
        }

        let pop_headers =
            if let &HttpStreamPartContent::Headers(..) = self.outgoing.front().unwrap() {
                true
            } else {
                false
            };
        if pop_headers {
            let r = self.outgoing.pop_front().unwrap();
            let last = self.outgoing.end() == Some(ErrorCode::NoError);
            if last {
                self.close_local();
            }
            return Some(HttpStreamCommand::from(HttpStreamPart {
                content: r,
                last: last,
            }))
        }

        if self.out_window_size.size() <= 0 || conn_out_window_size.size() <= 0 {
            return None
        }

        let mut data =
            if let Some(HttpStreamPartContent::Data(data)) = self.outgoing.pop_front() {
                data
            } else {
                unreachable!()
            };

        // Min of connection and stream window size
        let max_window = cmp::min(self.out_window_size.size(), conn_out_window_size.size());

        if data.len() as usize > max_window as usize {
            trace!("truncating data of len {} to {}", data.len(), max_window);
            let size = max_window as usize;
            let rem = data.split_off(size);
            self.outgoing.push_front(HttpStreamPartContent::Data(rem));
        };

        self.out_window_size.try_decrease_to_positive(data.len() as i32).unwrap();
        conn_out_window_size.try_decrease_to_positive(data.len() as i32).unwrap();

        let last = self.outgoing.end() == Some(ErrorCode::NoError);
        if last {
            self.close_local();
        }

        Some(HttpStreamCommand::from(HttpStreamPart {
            content: HttpStreamPartContent::Data(data),
            last: last,
        }))
    }

    pub fn _pop_outg_all(&mut self, conn_out_window_size: &mut WindowSize) -> Vec<HttpStreamCommand> {
        let mut r = Vec::new();
        while let Some(p) = self.pop_outg(conn_out_window_size) {
            r.push(p);
        }
        r
    }

    pub fn new_data_chunk(&mut self, data: &[u8], last: bool) {
        if let Some(ref mut response_handler) = self.peer_tx {
            // TODO: reset stream if rx is dead
            drop(response_handler.send(ResultOrEof::Item(HttpStreamPart {
                content: HttpStreamPartContent::Data(Bytes::from(data)),
                last: last,
            })));
        }
    }

    pub fn rst(&mut self, error_code: ErrorCode) {
        if let Some(ref mut response_handler) = self.peer_tx.take() {
            drop(response_handler.send(ResultOrEof::Error(error::Error::CodeError(error_code))));
        }
    }

    pub fn goaway_recvd(&mut self, _raw_error_code: u32) {
        if let Some(response_handler) = self.peer_tx.take() {
            // it is OK to ignore error: handler may be already dead
            drop(response_handler.send(ResultOrEof::Error(error::Error::Other("peer sent GOAWAY"))));
        }
    }
}

pub trait HttpStreamDataSpecific {
}

pub trait HttpStream {
    type Types : Types;
}

<|endoftext|>


<commit_before>//! Constant-time comparison of fixed-size vecs
use ffi;

/// `verify_16()` returns `true` if `x[0]`, `x[1]`, ..., `x[15]` are the
/// same as `y[0]`, `y[1]`, ..., `y[15]`. Otherwise it returns `false`.
///
/// This function is safe to use for secrets `x[0]`, `x[1]`, ..., `x[15]`,
/// `y[0]`, `y[1]`, ..., `y[15]`. The time taken by `verify_16` is independent
/// of the contents of `x[0]`, `x[1]`, ..., `x[15]`, `y[0]`, `y[1]`, ..., `y[15]`.
/// In contrast, the standard C comparison function `memcmp(x,y,16)` takes time
/// that depends on the longest matching prefix of `x` and `y`, often allowing easy
/// timing attacks.
pub fn verify_16(x: &[u8; 16], y: &[u8; 16]) -> bool {
    unsafe {
        ffi::crypto_verify_16(x, y) == 0
    }
}

/// `verify_32()` returns true if `x[0]`, `x[1]`, ..., `x[31]` are the
/// same as `y[0]`, `y[1]`, ..., `y[31]`. Otherwise it returns `false`.
///
/// This function is safe to use for secrets `x[0]`, `x[1]`, ..., `x[31]`,
/// `y[0]`, `y[1]`, ..., `y[31]`. The time taken by `verify_32` is independent
/// of the contents of `x[0]`, `x[1]`, ..., `x[31]`, `y[0]`, `y[1]`, ..., `y[31]`.
/// In contrast, the standard C comparison function `memcmp(x,y,32)` takes time
/// that depends on the longest matching prefix of `x` and `y`, often allowing easy
/// timing attacks.
pub fn verify_32(x: &[u8; 32], y: &[u8; 32]) -> bool {
    unsafe {
        ffi::crypto_verify_32(x, y) == 0
    }
}

/// `verify_64()` returns true if `x[0]`, `x[1]`, ..., `x[63]` are the
/// same as `y[0]`, `y[1]`, ..., `y[63]`. Otherwise it returns `false`.
///
/// This function is safe to use for secrets `x[0]`, `x[1]`, ..., `x[63]`,
/// `y[0]`, `y[1]`, ..., `y[63]`. The time taken by `verify_64` is independent
/// of the contents of `x[0]`, `x[1]`, ..., `x[63]`, `y[0]`, `y[1]`, ..., `y[63]`.
/// In contrast, the standard C comparison function `memcmp(x,y,64)` takes time
/// that depends on the longest matching prefix of `x` and `y`, often allowing easy
/// timing attacks.
pub fn verify_64(x: &[u8; 64], y: &[u8; 64]) -> bool {
    unsafe {
        ffi::crypto_verify_64(x, y) == 0
    }
}

/// `safe_memcmp()` returns true if `x[0]`, `x[1]`, ..., `x[len-1]` are the
/// same as `y[0]`, `y[1]`, ..., `y[len-1]`. Otherwise it returns `false`.
///
/// This function is safe to use for secrets `x[0]`, `x[1]`, ..., `x[len-1]`,
/// `y[0]`, `y[1]`, ..., `y[len-1]`. The time taken by `safe_memcmp` is independent
/// of the contents of `x[0]`, `x[1]`, ..., `x[len-1]`, `y[0]`, `y[1]`, ..., `y[len-1]`.
/// In contrast, the standard C comparison function `memcmp(x,y,len)` takes time
/// that depends on the longest matching prefix of `x` and `y`, often allowing easy
/// timing attacks.
pub fn safe_memcmp(x: &[u8], y: &[u8]) -> bool {
    if x.len() != y.len() {
        return false
    }
    unsafe {
        ffi::sodium_memcmp(x.as_ptr(), y.as_ptr(), x.len() as u64) == 0
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_verify_16() {
        use randombytes::randombytes_into;

        for _ in (0usize..256) {
            let mut x = [0; 16];
            let mut y = [0; 16];
            assert!(verify_16(&x, &y));
            randombytes_into(&mut x);
            randombytes_into(&mut y);
            if x == y {
                assert!(verify_16(&x, &y))
            } else {
                assert!(!verify_16(&x, &y))
            }
        }
    }

    #[test]
    fn test_verify_32() {
        use randombytes::randombytes_into;

        for _ in (0usize..256) {
            let mut x = [0; 32];
            let mut y = [0; 32];
            assert!(verify_32(&x, &y));
            randombytes_into(&mut x);
            randombytes_into(&mut y);
            if x == y {
                assert!(verify_32(&x, &y))
            } else {
                assert!(!verify_32(&x, &y))
            }
        }
    }

    #[test]
    fn test_verify_64() {
        use randombytes::randombytes_into;

        for _ in (0usize..256) {
            let mut x = [0; 64];
            let mut y = [0; 64];
            assert!(verify_64(&x, &y));
            randombytes_into(&mut x);
            randombytes_into(&mut y);
            if x[..] == y[..] {
                assert!(verify_64(&x, &y))
            } else {
                assert!(!verify_64(&x, &y))
            }
        }
    }

    #[test]
    fn test_safe_memcmp() {
        use randombytes::randombytes;

        for i in (0usize..256) {
            let x = randombytes(i);
            assert!(safe_memcmp(&x, &x));
            let mut y = x.clone();
            assert!(safe_memcmp(&x, &y));
            y.push(0);
            assert!(!safe_memcmp(&x, &y));
            assert!(!safe_memcmp(&y, &x));

            y = randombytes(i);
            if x == y {
                assert!(safe_memcmp(&x, &y))
            } else {
                assert!(!safe_memcmp(&x, &y))
            }
        }
    }
}
<commit_msg>sodium_memcmp() expects a size_t<commit_after>//! Constant-time comparison of fixed-size vecs
use ffi;
use libc::size_t;

/// `verify_16()` returns `true` if `x[0]`, `x[1]`, ..., `x[15]` are the
/// same as `y[0]`, `y[1]`, ..., `y[15]`. Otherwise it returns `false`.
///
/// This function is safe to use for secrets `x[0]`, `x[1]`, ..., `x[15]`,
/// `y[0]`, `y[1]`, ..., `y[15]`. The time taken by `verify_16` is independent
/// of the contents of `x[0]`, `x[1]`, ..., `x[15]`, `y[0]`, `y[1]`, ..., `y[15]`.
/// In contrast, the standard C comparison function `memcmp(x,y,16)` takes time
/// that depends on the longest matching prefix of `x` and `y`, often allowing easy
/// timing attacks.
pub fn verify_16(x: &[u8; 16], y: &[u8; 16]) -> bool {
    unsafe {
        ffi::crypto_verify_16(x, y) == 0
    }
}

/// `verify_32()` returns true if `x[0]`, `x[1]`, ..., `x[31]` are the
/// same as `y[0]`, `y[1]`, ..., `y[31]`. Otherwise it returns `false`.
///
/// This function is safe to use for secrets `x[0]`, `x[1]`, ..., `x[31]`,
/// `y[0]`, `y[1]`, ..., `y[31]`. The time taken by `verify_32` is independent
/// of the contents of `x[0]`, `x[1]`, ..., `x[31]`, `y[0]`, `y[1]`, ..., `y[31]`.
/// In contrast, the standard C comparison function `memcmp(x,y,32)` takes time
/// that depends on the longest matching prefix of `x` and `y`, often allowing easy
/// timing attacks.
pub fn verify_32(x: &[u8; 32], y: &[u8; 32]) -> bool {
    unsafe {
        ffi::crypto_verify_32(x, y) == 0
    }
}

/// `verify_64()` returns true if `x[0]`, `x[1]`, ..., `x[63]` are the
/// same as `y[0]`, `y[1]`, ..., `y[63]`. Otherwise it returns `false`.
///
/// This function is safe to use for secrets `x[0]`, `x[1]`, ..., `x[63]`,
/// `y[0]`, `y[1]`, ..., `y[63]`. The time taken by `verify_64` is independent
/// of the contents of `x[0]`, `x[1]`, ..., `x[63]`, `y[0]`, `y[1]`, ..., `y[63]`.
/// In contrast, the standard C comparison function `memcmp(x,y,64)` takes time
/// that depends on the longest matching prefix of `x` and `y`, often allowing easy
/// timing attacks.
pub fn verify_64(x: &[u8; 64], y: &[u8; 64]) -> bool {
    unsafe {
        ffi::crypto_verify_64(x, y) == 0
    }
}

/// `safe_memcmp()` returns true if `x[0]`, `x[1]`, ..., `x[len-1]` are the
/// same as `y[0]`, `y[1]`, ..., `y[len-1]`. Otherwise it returns `false`.
///
/// This function is safe to use for secrets `x[0]`, `x[1]`, ..., `x[len-1]`,
/// `y[0]`, `y[1]`, ..., `y[len-1]`. The time taken by `safe_memcmp` is independent
/// of the contents of `x[0]`, `x[1]`, ..., `x[len-1]`, `y[0]`, `y[1]`, ..., `y[len-1]`.
/// In contrast, the standard C comparison function `memcmp(x,y,len)` takes time
/// that depends on the longest matching prefix of `x` and `y`, often allowing easy
/// timing attacks.
pub fn safe_memcmp(x: &[u8], y: &[u8]) -> bool {
    if x.len() != y.len() {
        return false
    }
    unsafe {
        ffi::sodium_memcmp(x.as_ptr(), y.as_ptr(), x.len() as size_t) == 0
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_verify_16() {
        use randombytes::randombytes_into;

        for _ in (0usize..256) {
            let mut x = [0; 16];
            let mut y = [0; 16];
            assert!(verify_16(&x, &y));
            randombytes_into(&mut x);
            randombytes_into(&mut y);
            if x == y {
                assert!(verify_16(&x, &y))
            } else {
                assert!(!verify_16(&x, &y))
            }
        }
    }

    #[test]
    fn test_verify_32() {
        use randombytes::randombytes_into;

        for _ in (0usize..256) {
            let mut x = [0; 32];
            let mut y = [0; 32];
            assert!(verify_32(&x, &y));
            randombytes_into(&mut x);
            randombytes_into(&mut y);
            if x == y {
                assert!(verify_32(&x, &y))
            } else {
                assert!(!verify_32(&x, &y))
            }
        }
    }

    #[test]
    fn test_verify_64() {
        use randombytes::randombytes_into;

        for _ in (0usize..256) {
            let mut x = [0; 64];
            let mut y = [0; 64];
            assert!(verify_64(&x, &y));
            randombytes_into(&mut x);
            randombytes_into(&mut y);
            if x[..] == y[..] {
                assert!(verify_64(&x, &y))
            } else {
                assert!(!verify_64(&x, &y))
            }
        }
    }

    #[test]
    fn test_safe_memcmp() {
        use randombytes::randombytes;

        for i in (0usize..256) {
            let x = randombytes(i);
            assert!(safe_memcmp(&x, &x));
            let mut y = x.clone();
            assert!(safe_memcmp(&x, &y));
            y.push(0);
            assert!(!safe_memcmp(&x, &y));
            assert!(!safe_memcmp(&y, &x));

            y = randombytes(i);
            if x == y {
                assert!(safe_memcmp(&x, &y))
            } else {
                assert!(!safe_memcmp(&x, &y))
            }
        }
    }
}
<|endoftext|>


<commit_before>use crate::gui::person_list_box::{PersonListBox, PersonListBoxMsg};
use crate::model::Person;

use gtk::{
    BoxExt, ButtonExt, EditableSignals, EntryExt, Inhibit, OrientableExt, Orientation, WidgetExt,};
use relm::{Relm, StreamHandle, Widget};
use relm_derive::{widget, Msg};

#[derive(Msg)]
pub enum WinMsg {
    /// Create a new person. This message is sent by the `Create` button.
    CreatePerson,
    /// Update the selected person. This message is sent by the `Update` button.
    UpdatePerson,
    /// Delete the selected person. This message is sent by the `Delete` button.
    DeletePerson,
    /// The selection in the `PersonListBox` has changed. This message is sent by the `person_list_box` component.
    UpdateSelected(Option<Person>),
    /// The filter has changed. This message is sent by the filter entry.
    FilterChanged,
    /// The window was closed.
    Quit,
}

pub struct WinModel {
    msg_stream: StreamHandle<WinMsg>,
}

#[widget]
impl Widget for Win {
    fn model(relm: &Relm<Self>, _: ()) -> WinModel {
        WinModel {
            msg_stream: relm.stream().clone(),
        }
    }

    fn update(&mut self, event: WinMsg) {
        match event {
            WinMsg::CreatePerson => {
                let person = self.get_person();
                // `self.components` has all components of the widget referenced by name given in the `view!` makro.
                // You can send messages to the component using the `emit` function.
                // `self.compnents` and `self.widgets` are not the same. `self.widgets``refers to the `gtk::Widget` (or subclass).
                self.components
                    .person_list_box
                    .emit(PersonListBoxMsg::AddPerson(person))
            }
            WinMsg::UpdatePerson => {
                let person = self.get_person();
                self.components
                    .person_list_box
                    .emit(PersonListBoxMsg::UpdateSelected(person))
            }
            WinMsg::DeletePerson => self
                .components
                .person_list_box
                .emit(PersonListBoxMsg::DeleteSelected),
            WinMsg::UpdateSelected(person_opt) => {
                if let Some(person) = person_opt {
                    self.widgets.entry_name.set_text(&person.get_name());
                    self.widgets.entry_surname.set_text(&person.get_surname());
                } else {
                    self.widgets.entry_name.set_text("");
                    self.widgets.entry_surname.set_text("");
                }
            }
            WinMsg::FilterChanged => {
                let filter = self.widgets.entry_filter.get_text();
                self.components
                    .person_list_box
                    .emit(PersonListBoxMsg::Filter(filter.to_string()));
            }
            // Quit the application
            WinMsg::Quit => gtk::main_quit(),
        }
    }

    view! {
        gtk::Window {
            gtk::Box {
                #[name="entry_filter"]
                gtk::Entry {
                    placeholder_text: Some("Filter"),
                    changed => WinMsg::FilterChanged,
                },
                orientation: Orientation::Vertical,
                spacing: 16,
                gtk::Box {
                    spacing: 16,
                    #[name="person_list_box"]
                    // Create a new `PersonListBox` with the stream of this widget as the argument.
                    PersonListBox(self.model.msg_stream.clone()) {
                        hexpand: true,
                    },
                    gtk::Box {
                        spacing: 16,
                        orientation: Orientation::Vertical,
                        #[name="entry_name"]
                        gtk::Entry {
                            placeholder_text: Some("Name"),
                        },
                        #[name="entry_surname"]
                        gtk::Entry {
                            placeholder_text: Some("Surname"),
                        }
                    }
                },
                gtk::Box {
                    spacing: 16,
                    gtk::Button {
                        label: "Create",
                        clicked => WinMsg::CreatePerson
                    },
                    gtk::Button {
                        label: "Update",
                        clicked => WinMsg::UpdatePerson
                    },
                    gtk::Button {
                        label: "Delete",
                        clicked => WinMsg::DeletePerson
                    },
                }
            },
            delete_event(_, _) => (WinMsg::Quit, Inhibit(false)),
        }
    }
}

impl Win {
    /// Get the person from the entries `entry_name` and `entry_surname`.
    fn get_person(&self) -> Person {
        let name = self.widgets.entry_name.get_text();
        let surname = self.widgets.entry_surname.get_text();

        Person::new(&name, &surname)
    }
}
<commit_msg>CRUD: Update and delete buttons will now be available iff a person is selected<commit_after>use crate::gui::person_list_box::{PersonListBox, PersonListBoxMsg};
use crate::model::Person;

use gtk::{
    BoxExt, ButtonExt, EditableSignals, EntryExt, Inhibit, OrientableExt, Orientation, WidgetExt,
};
use relm::{Relm, StreamHandle, Widget};
use relm_derive::{widget, Msg};

#[derive(Msg)]
pub enum WinMsg {
    /// Create a new person. This message is sent by the `Create` button.
    CreatePerson,
    /// Update the selected person. This message is sent by the `Update` button.
    UpdatePerson,
    /// Delete the selected person. This message is sent by the `Delete` button.
    DeletePerson,
    /// The selection in the `PersonListBox` has changed. This message is sent by the `person_list_box` component.
    UpdateSelected(Option<Person>),
    /// The filter has changed. This message is sent by the filter entry.
    FilterChanged,
    /// The window was closed.
    Quit,
}

pub struct WinModel {
    msg_stream: StreamHandle<WinMsg>,

    selected_person: Option<Person>,
}

#[widget]
impl Widget for Win {
    fn model(relm: &Relm<Self>, _: ()) -> WinModel {
        WinModel {
            msg_stream: relm.stream().clone(),
            selected_person: None,
        }
    }

    fn update(&mut self, event: WinMsg) {
        match event {
            WinMsg::CreatePerson => {
                let person = self.get_person();
                // `self.components` has all components of the widget referenced by name given in the `view!` macro.
                // You can send messages to the component using the `emit` function.
                // `self.compnents` and `self.widgets` are not the same. `self.widgets``refers to the `gtk::Widget` (or subclass).
                self.components
                    .person_list_box
                    .emit(PersonListBoxMsg::AddPerson(person))
            }
            WinMsg::UpdatePerson => {
                let person = self.get_person();
                self.components
                    .person_list_box
                    .emit(PersonListBoxMsg::UpdateSelected(person))
            }
            WinMsg::DeletePerson => self
                .components
                .person_list_box
                .emit(PersonListBoxMsg::DeleteSelected),
            WinMsg::UpdateSelected(person_opt) => {
                // Set the entry fields.
                if let Some(person) = &person_opt {
                    self.widgets.entry_name.set_text(&person.get_name());
                    self.widgets.entry_surname.set_text(&person.get_surname());
                } else {
                    self.widgets.entry_name.set_text("");
                    self.widgets.entry_surname.set_text("");
                }

                // Set the person in the model.
                self.model.selected_person = person_opt;
            }
            WinMsg::FilterChanged => {
                let filter = self.widgets.entry_filter.get_text();
                self.components
                    .person_list_box
                    .emit(PersonListBoxMsg::Filter(filter.to_string()));
            }
            // Quit the application
            WinMsg::Quit => gtk::main_quit(),
        }
    }

    view! {
        gtk::Window {
            gtk::Box {
                #[name="entry_filter"]
                gtk::Entry {
                    placeholder_text: Some("Filter"),
                    changed => WinMsg::FilterChanged,
                },
                orientation: Orientation::Vertical,
                spacing: 16,
                gtk::Box {
                    spacing: 16,
                    #[name="person_list_box"]
                    // Create a new `PersonListBox` with the stream of this widget as the argument.
                    PersonListBox(self.model.msg_stream.clone()) {
                        hexpand: true,
                    },
                    gtk::Box {
                        spacing: 16,
                        orientation: Orientation::Vertical,
                        #[name="entry_name"]
                        gtk::Entry {
                            placeholder_text: Some("Name"),
                        },
                        #[name="entry_surname"]
                        gtk::Entry {
                            placeholder_text: Some("Surname"),
                        }
                    }
                },
                gtk::Box {
                    spacing: 16,
                    gtk::Button {
                        label: "Create",
                        clicked => WinMsg::CreatePerson
                    },
                    gtk::Button {
                        label: "Update",
                        // This button will only be sensitive if a person is selected.
                        sensitive: self.model.selected_person.is_some(),
                        clicked => WinMsg::UpdatePerson
                    },
                    gtk::Button {
                        label: "Delete",
                        // This button will only be sensitive if a person is selected.
                        sensitive: self.model.selected_person.is_some(),
                        clicked => WinMsg::DeletePerson
                    },
                }
            },
            delete_event(_, _) => (WinMsg::Quit, Inhibit(false)),
        }
    }
}

impl Win {
    /// Get the person from the entries `entry_name` and `entry_surname`.
    fn get_person(&self) -> Person {
        let name = self.widgets.entry_name.get_text();
        let surname = self.widgets.entry_surname.get_text();

        Person::new(&name, &surname)
    }
}
<|endoftext|>


<commit_before>extern crate libsqlite3_sys as ffi;

use std::ffi::{CStr, CString};
use std::io::{stderr, Write};
use std::os::raw as libc;
use std::ptr::NonNull;
use std::{mem, ptr, slice, str};

use super::serialized_value::SerializedValue;
use result::Error::DatabaseError;
use result::*;

#[allow(missing_debug_implementations, missing_copy_implementations)]
pub struct RawConnection {
    pub(crate) internal_connection: NonNull<ffi::sqlite3>,
}

impl RawConnection {
    pub fn establish(database_url: &str) -> ConnectionResult<Self> {
        let mut conn_pointer = ptr::null_mut();
        let database_url = CString::new(database_url)?;
        let connection_status =
            unsafe { ffi::sqlite3_open(database_url.as_ptr(), &mut conn_pointer) };

        match connection_status {
            ffi::SQLITE_OK => {
                let conn_pointer = unsafe { NonNull::new_unchecked(conn_pointer) };
                Ok(RawConnection {
                    internal_connection: conn_pointer,
                })
            }
            err_code => {
                let message = super::error_message(err_code);
                Err(ConnectionError::BadConnection(message.into()))
            }
        }
    }

    pub fn exec(&self, query: &str) -> QueryResult<()> {
        let mut err_msg = ptr::null_mut();
        let query = CString::new(query)?;
        let callback_fn = None;
        let callback_arg = ptr::null_mut();
        unsafe {
            ffi::sqlite3_exec(
                self.internal_connection.as_ptr(),
                query.as_ptr(),
                callback_fn,
                callback_arg,
                &mut err_msg,
            );
        }

        if err_msg.is_null() {
            Ok(())
        } else {
            let msg = convert_to_string_and_free(err_msg);
            let error_kind = DatabaseErrorKind::__Unknown;
            Err(DatabaseError(error_kind, Box::new(msg)))
        }
    }

    pub fn rows_affected_by_last_query(&self) -> usize {
        unsafe { ffi::sqlite3_changes(self.internal_connection.as_ptr()) as usize }
    }

    pub fn register_sql_function<F>(
        &self,
        fn_name: &str,
        num_args: usize,
        deterministic: bool,
        f: F,
    ) -> QueryResult<()>
    where
        F: FnMut(&Self, &[*mut ffi::sqlite3_value]) -> QueryResult<SerializedValue>
            + Send
            + 'static,
    {
        let fn_name = CString::new(fn_name)?;
        let mut flags = ffi::SQLITE_UTF8;
        if deterministic {
            flags |= ffi::SQLITE_DETERMINISTIC;
        }
        let callback_fn = Box::into_raw(Box::new(f));

        let result = unsafe {
            ffi::sqlite3_create_function_v2(
                self.internal_connection.as_ptr(),
                fn_name.as_ptr(),
                num_args as _,
                flags,
                callback_fn as *mut _,
                Some(run_custom_function::<F>),
                None,
                None,
                Some(destroy_boxed_fn::<F>),
            )
        };

        if result == ffi::SQLITE_OK {
            Ok(())
        } else {
            let error_message = super::error_message(result);
            Err(DatabaseError(
                DatabaseErrorKind::__Unknown,
                Box::new(error_message.to_string()),
            ))
        }
    }
}

impl Drop for RawConnection {
    fn drop(&mut self) {
        use std::thread::panicking;

        let close_result = unsafe { ffi::sqlite3_close(self.internal_connection.as_ptr()) };
        if close_result != ffi::SQLITE_OK {
            let error_message = super::error_message(close_result);
            if panicking() {
                write!(
                    stderr(),
                    "Error closing SQLite connection: {}",
                    error_message
                )
                .expect("Error writing to `stderr`");
            } else {
                panic!("Error closing SQLite connection: {}", error_message);
            }
        }
    }
}

fn convert_to_string_and_free(err_msg: *const libc::c_char) -> String {
    let msg = unsafe {
        let bytes = CStr::from_ptr(err_msg).to_bytes();
        str::from_utf8_unchecked(bytes).into()
    };
    unsafe { ffi::sqlite3_free(err_msg as *mut libc::c_void) };
    msg
}

#[allow(warnings)]
extern "C" fn run_custom_function<F>(
    ctx: *mut ffi::sqlite3_context,
    num_args: libc::c_int,
    value_ptr: *mut *mut ffi::sqlite3_value,
) where
    F: FnMut(&RawConnection, &[*mut ffi::sqlite3_value]) -> QueryResult<SerializedValue>
        + Send
        + 'static,
{
    static NULL_DATA_ERR: &str = "An unknown error occurred. sqlite3_user_data returned a null pointer. This should never happen.";
    static NULL_CONN_ERR: &str = "An unknown error occurred. sqlite3_context_db_handle returned a null pointer. This should never happen.";

    unsafe {
        let data_ptr = ffi::sqlite3_user_data(ctx);
        let data_ptr = data_ptr as *mut F;
        let f = match data_ptr.as_mut() {
            Some(f) => f,
            None => {
                ffi::sqlite3_result_error(
                    ctx,
                    NULL_DATA_ERR.as_ptr() as *const _ as *const _,
                    NULL_DATA_ERR.len() as _,
                );
                return;
            }
        };

        let args = slice::from_raw_parts(value_ptr, num_args as _);
        let conn = match NonNull::new(ffi::sqlite3_context_db_handle(ctx)) {
            Some(conn) => RawConnection {
                internal_connection: conn,
            },
            None => {
                ffi::sqlite3_result_error(
                    ctx,
                    NULL_DATA_ERR.as_ptr() as *const _ as *const _,
                    NULL_DATA_ERR.len() as _,
                );
                return;
            }
        };
        match f(&conn, args) {
            Ok(value) => value.result_of(ctx),
            Err(e) => {
                let msg = e.to_string();
                ffi::sqlite3_result_error(ctx, msg.as_ptr() as *const _, msg.len() as _);
            }
        }

        mem::forget(conn);
    }
}

extern "C" fn destroy_boxed_fn<F>(data: *mut libc::c_void)
where
    F: FnMut(&RawConnection, &[*mut ffi::sqlite3_value]) -> QueryResult<SerializedValue>
        + Send
        + 'static,
{
    let ptr = data as *mut F;
    unsafe { Box::from_raw(ptr) };
}
<commit_msg>Strip scheme from sqlite database URL<commit_after>extern crate libsqlite3_sys as ffi;

use std::ffi::{CStr, CString};
use std::io::{stderr, Write};
use std::os::raw as libc;
use std::ptr::NonNull;
use std::{mem, ptr, slice, str};

use super::serialized_value::SerializedValue;
use result::Error::DatabaseError;
use result::*;

#[allow(missing_debug_implementations, missing_copy_implementations)]
pub struct RawConnection {
    pub(crate) internal_connection: NonNull<ffi::sqlite3>,
}

impl RawConnection {
    pub fn establish(database_url: &str) -> ConnectionResult<Self> {
        let mut conn_pointer = ptr::null_mut();
        let database_url = CString::new(database_url.trim_start_matches("sqlite://"))?;
        let connection_status =
            unsafe { ffi::sqlite3_open(database_url.as_ptr(), &mut conn_pointer) };

        match connection_status {
            ffi::SQLITE_OK => {
                let conn_pointer = unsafe { NonNull::new_unchecked(conn_pointer) };
                Ok(RawConnection {
                    internal_connection: conn_pointer,
                })
            }
            err_code => {
                let message = super::error_message(err_code);
                Err(ConnectionError::BadConnection(message.into()))
            }
        }
    }

    pub fn exec(&self, query: &str) -> QueryResult<()> {
        let mut err_msg = ptr::null_mut();
        let query = CString::new(query)?;
        let callback_fn = None;
        let callback_arg = ptr::null_mut();
        unsafe {
            ffi::sqlite3_exec(
                self.internal_connection.as_ptr(),
                query.as_ptr(),
                callback_fn,
                callback_arg,
                &mut err_msg,
            );
        }

        if err_msg.is_null() {
            Ok(())
        } else {
            let msg = convert_to_string_and_free(err_msg);
            let error_kind = DatabaseErrorKind::__Unknown;
            Err(DatabaseError(error_kind, Box::new(msg)))
        }
    }

    pub fn rows_affected_by_last_query(&self) -> usize {
        unsafe { ffi::sqlite3_changes(self.internal_connection.as_ptr()) as usize }
    }

    pub fn register_sql_function<F>(
        &self,
        fn_name: &str,
        num_args: usize,
        deterministic: bool,
        f: F,
    ) -> QueryResult<()>
    where
        F: FnMut(&Self, &[*mut ffi::sqlite3_value]) -> QueryResult<SerializedValue>
            + Send
            + 'static,
    {
        let fn_name = CString::new(fn_name)?;
        let mut flags = ffi::SQLITE_UTF8;
        if deterministic {
            flags |= ffi::SQLITE_DETERMINISTIC;
        }
        let callback_fn = Box::into_raw(Box::new(f));

        let result = unsafe {
            ffi::sqlite3_create_function_v2(
                self.internal_connection.as_ptr(),
                fn_name.as_ptr(),
                num_args as _,
                flags,
                callback_fn as *mut _,
                Some(run_custom_function::<F>),
                None,
                None,
                Some(destroy_boxed_fn::<F>),
            )
        };

        if result == ffi::SQLITE_OK {
            Ok(())
        } else {
            let error_message = super::error_message(result);
            Err(DatabaseError(
                DatabaseErrorKind::__Unknown,
                Box::new(error_message.to_string()),
            ))
        }
    }
}

impl Drop for RawConnection {
    fn drop(&mut self) {
        use std::thread::panicking;

        let close_result = unsafe { ffi::sqlite3_close(self.internal_connection.as_ptr()) };
        if close_result != ffi::SQLITE_OK {
            let error_message = super::error_message(close_result);
            if panicking() {
                write!(
                    stderr(),
                    "Error closing SQLite connection: {}",
                    error_message
                )
                .expect("Error writing to `stderr`");
            } else {
                panic!("Error closing SQLite connection: {}", error_message);
            }
        }
    }
}

fn convert_to_string_and_free(err_msg: *const libc::c_char) -> String {
    let msg = unsafe {
        let bytes = CStr::from_ptr(err_msg).to_bytes();
        str::from_utf8_unchecked(bytes).into()
    };
    unsafe { ffi::sqlite3_free(err_msg as *mut libc::c_void) };
    msg
}

#[allow(warnings)]
extern "C" fn run_custom_function<F>(
    ctx: *mut ffi::sqlite3_context,
    num_args: libc::c_int,
    value_ptr: *mut *mut ffi::sqlite3_value,
) where
    F: FnMut(&RawConnection, &[*mut ffi::sqlite3_value]) -> QueryResult<SerializedValue>
        + Send
        + 'static,
{
    static NULL_DATA_ERR: &str = "An unknown error occurred. sqlite3_user_data returned a null pointer. This should never happen.";
    static NULL_CONN_ERR: &str = "An unknown error occurred. sqlite3_context_db_handle returned a null pointer. This should never happen.";

    unsafe {
        let data_ptr = ffi::sqlite3_user_data(ctx);
        let data_ptr = data_ptr as *mut F;
        let f = match data_ptr.as_mut() {
            Some(f) => f,
            None => {
                ffi::sqlite3_result_error(
                    ctx,
                    NULL_DATA_ERR.as_ptr() as *const _ as *const _,
                    NULL_DATA_ERR.len() as _,
                );
                return;
            }
        };

        let args = slice::from_raw_parts(value_ptr, num_args as _);
        let conn = match NonNull::new(ffi::sqlite3_context_db_handle(ctx)) {
            Some(conn) => RawConnection {
                internal_connection: conn,
            },
            None => {
                ffi::sqlite3_result_error(
                    ctx,
                    NULL_DATA_ERR.as_ptr() as *const _ as *const _,
                    NULL_DATA_ERR.len() as _,
                );
                return;
            }
        };
        match f(&conn, args) {
            Ok(value) => value.result_of(ctx),
            Err(e) => {
                let msg = e.to_string();
                ffi::sqlite3_result_error(ctx, msg.as_ptr() as *const _, msg.len() as _);
            }
        }

        mem::forget(conn);
    }
}

extern "C" fn destroy_boxed_fn<F>(data: *mut libc::c_void)
where
    F: FnMut(&RawConnection, &[*mut ffi::sqlite3_value]) -> QueryResult<SerializedValue>
        + Send
        + 'static,
{
    let ptr = data as *mut F;
    unsafe { Box::from_raw(ptr) };
}
<|endoftext|>


<commit_before>use crate::utils::TLDS;
use err_derive::Error;
use matches::matches;
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::str::FromStr;

fn is_invalid_domain_char(c: char) -> bool {
    matches!(
        c,
        '\0' | '\t' | '\n' | '\r' | ' ' | '#' | '%' | '/' | ':' | '?' | '@' | '[' | '\\' | ']'
    )
}

#[derive(Debug, Error)]
pub enum ParseEmailError {
    #[error(display = "missing '@' separator in email address")]
    NoSeparator,
    #[error(display = "local part of an email address cannot be empty")]
    EmptyLocal,
    #[error(display = "invalid international domain name in email address")]
    InvalidIdna(#[error(from)] idna::Errors),
    #[error(display = "domain part of an email address cannot be empty")]
    EmptyDomain,
    #[error(display = "email address contains invalid characters in the domain part")]
    InvalidDomainChars,
    #[error(display = "email address domain part is not in a public top-level domain")]
    InvalidTld,
}

#[derive(Clone, PartialEq, Eq)]
pub struct EmailAddress {
    serialization: String,
    local_end: usize,
}

impl FromStr for EmailAddress {
    type Err = ParseEmailError;

    /// Parse and normalize an email address.
    /// <https://github.com/portier/portier.github.io/blob/master/specs/Email-Normalization.md>
    fn from_str(input: &str) -> Result<Self, Self::Err> {
        let local_end = input.rfind('@').ok_or(ParseEmailError::NoSeparator)?;
        // Transform the local part to lowercase
        let local = input[..local_end].to_lowercase();
        if local == "" {
            return Err(ParseEmailError::EmptyLocal);
        }
        // Verify and normalize the domain
        let domain = idna::domain_to_ascii(&input[local_end + 1..])?;
        if domain == "" {
            return Err(ParseEmailError::EmptyDomain);
        }
        if domain.find(is_invalid_domain_char).is_some() {
            return Err(ParseEmailError::InvalidDomainChars);
        }
        // Verify the domain has a valid TLD.
        let tld_start = domain.rfind('.').map_or(0, |v| v + 1);
        if !TLDS.contains(&domain[tld_start..]) {
            return Err(ParseEmailError::InvalidTld);
        }
        Ok(EmailAddress::from_parts(&local, &domain))
    }
}

impl AsRef<str> for EmailAddress {
    /// Return the serialization of this email address.
    fn as_ref(&self) -> &str {
        &self.serialization
    }
}

impl EmailAddress {
    /// Create an email address from trusted local and domain parts.
    fn from_parts(local: &str, domain: &str) -> EmailAddress {
        EmailAddress {
            serialization: format!("{}@{}", local, domain),
            local_end: local.len(),
        }
    }

    /// Return the serialization.
    pub fn as_str(&self) -> &str {
        &self.serialization
    }

    /// Consume and return the serialization.
    pub fn into_string(self) -> String {
        self.serialization
    }

    /// Return the normalized local part.
    pub fn local(&self) -> &str {
        &self.serialization[..self.local_end]
    }

    /// Return the ASCII normalized domain.
    pub fn domain(&self) -> &str {
        &self.serialization[self.local_end + 1..]
    }

    /// Return the normalized local and domain parts as a tuple.
    pub fn parts(&self) -> (&str, &str) {
        (self.local(), self.domain())
    }

    /// Normalize a Google email address.
    ///
    /// This method can also be used to normalize G Suite addresses.
    pub fn normalize_google(&self) -> EmailAddress {
        let (local, domain) = self.parts();

        // Normalize googlemail.com to gmail.com
        let domain = match domain {
            "googlemail.com" => "gmail.com",
            domain => domain,
        };

        // Trim plus addresses
        let local = match self.local().find('+') {
            Some(pos) => &local[..pos],
            None => local,
        };

        // Ignore dots
        let local = local.replace(".", "");

        EmailAddress::from_parts(&local, domain)
    }
}

/// Display the serialization of this email address.
impl Display for EmailAddress {
    fn fmt(&self, formatter: &mut Formatter) -> FmtResult {
        Display::fmt(&self.serialization, formatter)
    }
}

/// Debug the serialization of this email address.
impl Debug for EmailAddress {
    fn fmt(&self, formatter: &mut Formatter) -> FmtResult {
        Debug::fmt(&self.serialization, formatter)
    }
}

serde_string!(EmailAddress);

#[cfg(test)]
mod tests {
    use super::EmailAddress;

    #[test]
    fn test_valid() {
        fn parse(input: &str, output: &str) {
            assert_eq!(
                input.parse::<EmailAddress>().unwrap(),
                output.parse::<EmailAddress>().unwrap()
            );
        }
        parse("example.foo+bar@example.com", "example.foo+bar@example.com");
        parse("EXAMPLE.FOO+BAR@EXAMPLE.COM", "example.foo+bar@example.com");
        parse("BJÖRN@göteborg.se", "björn@xn--gteborg-90a.se");
        parse("İⅢ@İⅢ.ninja", "i̇ⅲ@xn--iiii-qwc.ninja");
        parse("\"ex@mple\"@example.com", "\"ex@mple\"@example.com");
        parse("test@example.موقع", "test@example.xn--4gbrim");
        parse("test@uk", "test@uk");
    }

    #[test]
    fn test_invalid() {
        fn parse(input: &str) {
            assert!(input.parse::<EmailAddress>().is_err());
        }
        parse("foo");
        parse("foo@");
        parse("@foo.com");
        parse("foo@127.0.0.1");
        parse("foo@[::1]");
        parse("foo@bla.test");
        parse("foo@bla.example");
        parse("foo@bla.invalid");
    }

    #[test]
    fn test_google() {
        fn parse(input: &str, output: &str) {
            assert_eq!(
                input.parse::<EmailAddress>().unwrap().normalize_google(),
                output.parse::<EmailAddress>().unwrap()
            );
        }
        parse("example@gmail.com", "example@gmail.com");
        parse("example@googlemail.com", "example@gmail.com");
        parse("example.foo@gmail.com", "examplefoo@gmail.com");
        parse("example+bar@gmail.com", "example@gmail.com");
        parse("example.foo+bar@googlemail.com", "examplefoo@gmail.com");
        parse("EXAMPLE@GOOGLEMAIL.COM", "example@gmail.com");
        parse("EXAMPLE.FOO+BAR@GOOGLEMAIL.COM", "examplefoo@gmail.com");
    }
}
<commit_msg>Manual trait implementations for EmailAddress<commit_after>use crate::utils::TLDS;
use err_derive::Error;
use matches::matches;
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::hash::{Hash, Hasher};
use std::str::FromStr;

fn is_invalid_domain_char(c: char) -> bool {
    matches!(
        c,
        '\0' | '\t' | '\n' | '\r' | ' ' | '#' | '%' | '/' | ':' | '?' | '@' | '[' | '\\' | ']'
    )
}

#[derive(Debug, Error)]
pub enum ParseEmailError {
    #[error(display = "missing '@' separator in email address")]
    NoSeparator,
    #[error(display = "local part of an email address cannot be empty")]
    EmptyLocal,
    #[error(display = "invalid international domain name in email address")]
    InvalidIdna(#[error(from)] idna::Errors),
    #[error(display = "domain part of an email address cannot be empty")]
    EmptyDomain,
    #[error(display = "email address contains invalid characters in the domain part")]
    InvalidDomainChars,
    #[error(display = "email address domain part is not in a public top-level domain")]
    InvalidTld,
}

#[derive(Clone)]
pub struct EmailAddress {
    serialization: String,
    local_end: usize,
}

impl PartialEq for EmailAddress {
    fn eq(&self, other: &Self) -> bool {
        self.serialization == other.serialization
    }
}

impl Eq for EmailAddress {}

impl Hash for EmailAddress {
    fn hash<H>(&self, state: &mut impl Hasher) {
        self.serialization.hash(state)
    }
}

impl FromStr for EmailAddress {
    type Err = ParseEmailError;

    /// Parse and normalize an email address.
    /// <https://github.com/portier/portier.github.io/blob/master/specs/Email-Normalization.md>
    fn from_str(input: &str) -> Result<Self, Self::Err> {
        let local_end = input.rfind('@').ok_or(ParseEmailError::NoSeparator)?;
        // Transform the local part to lowercase
        let local = input[..local_end].to_lowercase();
        if local == "" {
            return Err(ParseEmailError::EmptyLocal);
        }
        // Verify and normalize the domain
        let domain = idna::domain_to_ascii(&input[local_end + 1..])?;
        if domain == "" {
            return Err(ParseEmailError::EmptyDomain);
        }
        if domain.find(is_invalid_domain_char).is_some() {
            return Err(ParseEmailError::InvalidDomainChars);
        }
        // Verify the domain has a valid TLD.
        let tld_start = domain.rfind('.').map_or(0, |v| v + 1);
        if !TLDS.contains(&domain[tld_start..]) {
            return Err(ParseEmailError::InvalidTld);
        }
        Ok(EmailAddress::from_parts(&local, &domain))
    }
}

impl AsRef<str> for EmailAddress {
    /// Return the serialization of this email address.
    fn as_ref(&self) -> &str {
        &self.serialization
    }
}

impl EmailAddress {
    /// Create an email address from trusted local and domain parts.
    fn from_parts(local: &str, domain: &str) -> EmailAddress {
        EmailAddress {
            serialization: format!("{}@{}", local, domain),
            local_end: local.len(),
        }
    }

    /// Return the serialization.
    pub fn as_str(&self) -> &str {
        &self.serialization
    }

    /// Consume and return the serialization.
    pub fn into_string(self) -> String {
        self.serialization
    }

    /// Return the normalized local part.
    pub fn local(&self) -> &str {
        &self.serialization[..self.local_end]
    }

    /// Return the ASCII normalized domain.
    pub fn domain(&self) -> &str {
        &self.serialization[self.local_end + 1..]
    }

    /// Return the normalized local and domain parts as a tuple.
    pub fn parts(&self) -> (&str, &str) {
        (self.local(), self.domain())
    }

    /// Normalize a Google email address.
    ///
    /// This method can also be used to normalize G Suite addresses.
    pub fn normalize_google(&self) -> EmailAddress {
        let (local, domain) = self.parts();

        // Normalize googlemail.com to gmail.com
        let domain = match domain {
            "googlemail.com" => "gmail.com",
            domain => domain,
        };

        // Trim plus addresses
        let local = match self.local().find('+') {
            Some(pos) => &local[..pos],
            None => local,
        };

        // Ignore dots
        let local = local.replace(".", "");

        EmailAddress::from_parts(&local, domain)
    }
}

/// Display the serialization of this email address.
impl Display for EmailAddress {
    fn fmt(&self, formatter: &mut Formatter) -> FmtResult {
        Display::fmt(&self.serialization, formatter)
    }
}

/// Debug the serialization of this email address.
impl Debug for EmailAddress {
    fn fmt(&self, formatter: &mut Formatter) -> FmtResult {
        Debug::fmt(&self.serialization, formatter)
    }
}

serde_string!(EmailAddress);

#[cfg(test)]
mod tests {
    use super::EmailAddress;

    #[test]
    fn test_valid() {
        fn parse(input: &str, output: &str) {
            assert_eq!(
                input.parse::<EmailAddress>().unwrap(),
                output.parse::<EmailAddress>().unwrap()
            );
        }
        parse("example.foo+bar@example.com", "example.foo+bar@example.com");
        parse("EXAMPLE.FOO+BAR@EXAMPLE.COM", "example.foo+bar@example.com");
        parse("BJÖRN@göteborg.se", "björn@xn--gteborg-90a.se");
        parse("İⅢ@İⅢ.ninja", "i̇ⅲ@xn--iiii-qwc.ninja");
        parse("\"ex@mple\"@example.com", "\"ex@mple\"@example.com");
        parse("test@example.موقع", "test@example.xn--4gbrim");
        parse("test@uk", "test@uk");
    }

    #[test]
    fn test_invalid() {
        fn parse(input: &str) {
            assert!(input.parse::<EmailAddress>().is_err());
        }
        parse("foo");
        parse("foo@");
        parse("@foo.com");
        parse("foo@127.0.0.1");
        parse("foo@[::1]");
        parse("foo@bla.test");
        parse("foo@bla.example");
        parse("foo@bla.invalid");
    }

    #[test]
    fn test_google() {
        fn parse(input: &str, output: &str) {
            assert_eq!(
                input.parse::<EmailAddress>().unwrap().normalize_google(),
                output.parse::<EmailAddress>().unwrap()
            );
        }
        parse("example@gmail.com", "example@gmail.com");
        parse("example@googlemail.com", "example@gmail.com");
        parse("example.foo@gmail.com", "examplefoo@gmail.com");
        parse("example+bar@gmail.com", "example@gmail.com");
        parse("example.foo+bar@googlemail.com", "examplefoo@gmail.com");
        parse("EXAMPLE@GOOGLEMAIL.COM", "example@gmail.com");
        parse("EXAMPLE.FOO+BAR@GOOGLEMAIL.COM", "examplefoo@gmail.com");
    }
}
<|endoftext|>