<commit_before>use hir::{db::AstDatabase, HirDisplay, Type};
use ide_db::{famous_defs::FamousDefs, source_change::SourceChange};
use syntax::{
    ast::{BlockExpr, ExprStmt},
    AstNode,
};
use text_edit::TextEdit;

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

// Diagnostic: type-mismatch
//
// This diagnostic is triggered when the type of an expression does not match
// the expected type.
pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Diagnostic {
    let mut diag = Diagnostic::new(
        "type-mismatch",
        format!(
            "expected {}, found {}",
            d.expected.display(ctx.sema.db),
            d.actual.display(ctx.sema.db)
        ),
        ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
    )
    .with_fixes(fixes(ctx, d));
    if diag.fixes.is_none() {
        diag.experimental = true;
    }
    diag
}

fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Option<Vec<Assist>> {
    let mut fixes = Vec::new();

    add_reference(ctx, d, &mut fixes);
    add_missing_ok_or_some(ctx, d, &mut fixes);
    remove_semicolon(ctx, d, &mut fixes);

    if fixes.is_empty() {
        None
    } else {
        Some(fixes)
    }
}

fn add_reference(
    ctx: &DiagnosticsContext<'_>,
    d: &hir::TypeMismatch,
    acc: &mut Vec<Assist>,
) -> Option<()> {
    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
    let expr_node = d.expr.value.to_node(&root);

    let range = ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range;

    let (_, mutability) = d.expected.as_reference()?;
    let actual_with_ref = Type::reference(&d.actual, mutability);
    if !actual_with_ref.could_coerce_to(ctx.sema.db, &d.expected) {
        return None;
    }

    let ampersands = format!("&{}", mutability.as_keyword_for_ref());

    let edit = TextEdit::insert(expr_node.syntax().text_range().start(), ampersands);
    let source_change =
        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
    acc.push(fix("add_reference_here", "Add reference here", source_change, range));
    Some(())
}

fn add_missing_ok_or_some(
    ctx: &DiagnosticsContext<'_>,
    d: &hir::TypeMismatch,
    acc: &mut Vec<Assist>,
) -> Option<()> {
    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
    let expr = d.expr.value.to_node(&root);
    let expr_range = expr.syntax().text_range();
    let scope = ctx.sema.scope(expr.syntax())?;

    let expected_adt = d.expected.as_adt()?;
    let expected_enum = expected_adt.as_enum()?;

    let famous_defs = FamousDefs(&ctx.sema, scope.krate());
    let core_result = famous_defs.core_result_Result();
    let core_option = famous_defs.core_option_Option();

    if Some(expected_enum) != core_result && Some(expected_enum) != core_option {
        return None;
    }

    let variant_name = if Some(expected_enum) == core_result { "Ok" } else { "Some" };

    let wrapped_actual_ty = expected_adt.ty_with_args(ctx.sema.db, &[d.actual.clone()]);

    if !d.expected.could_unify_with(ctx.sema.db, &wrapped_actual_ty) {
        return None;
    }

    let mut builder = TextEdit::builder();
    builder.insert(expr.syntax().text_range().start(), format!("{}(", variant_name));
    builder.insert(expr.syntax().text_range().end(), ")".to_string());
    let source_change =
        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), builder.finish());
    let name = format!("Wrap in {}", variant_name);
    acc.push(fix("wrap_in_constructor", &name, source_change, expr_range));
    Some(())
}

fn remove_semicolon(
    ctx: &DiagnosticsContext<'_>,
    d: &hir::TypeMismatch,
    acc: &mut Vec<Assist>,
) -> Option<()> {
    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
    let expr = d.expr.value.to_node(&root);
    if !d.actual.is_unit() {
        return None;
    }
    let block = BlockExpr::cast(expr.syntax().clone())?;
    let expr_before_semi =
        block.statements().last().and_then(|s| ExprStmt::cast(s.syntax().clone()))?;
    let type_before_semi = ctx.sema.type_of_expr(&expr_before_semi.expr()?)?.original();
    if !type_before_semi.could_coerce_to(ctx.sema.db, &d.expected) {
        return None;
    }
    let semicolon_range = expr_before_semi.semicolon_token()?.text_range();

    let edit = TextEdit::delete(semicolon_range);
    let source_change =
        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);

    acc.push(fix("remove_semicolon", "Remove this semicolon", source_change, semicolon_range));
    Some(())
}

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

    #[test]
    fn missing_reference() {
        check_diagnostics(
            r#"
fn main() {
    test(123);
       //^^^ 💡 error: expected &i32, found i32
}
fn test(arg: &i32) {}
"#,
        );
    }

    #[test]
    fn test_add_reference_to_int() {
        check_fix(
            r#"
fn main() {
    test(123$0);
}
fn test(arg: &i32) {}
            "#,
            r#"
fn main() {
    test(&123);
}
fn test(arg: &i32) {}
            "#,
        );
    }

    #[test]
    fn test_add_mutable_reference_to_int() {
        check_fix(
            r#"
fn main() {
    test($0123);
}
fn test(arg: &mut i32) {}
            "#,
            r#"
fn main() {
    test(&mut 123);
}
fn test(arg: &mut i32) {}
            "#,
        );
    }

    #[test]
    fn test_add_reference_to_array() {
        check_fix(
            r#"
//- minicore: coerce_unsized
fn main() {
    test($0[1, 2, 3]);
}
fn test(arg: &[i32]) {}
            "#,
            r#"
fn main() {
    test(&[1, 2, 3]);
}
fn test(arg: &[i32]) {}
            "#,
        );
    }

    #[test]
    fn test_add_reference_with_autoderef() {
        check_fix(
            r#"
//- minicore: coerce_unsized, deref
struct Foo;
struct Bar;
impl core::ops::Deref for Foo {
    type Target = Bar;
}

fn main() {
    test($0Foo);
}
fn test(arg: &Bar) {}
            "#,
            r#"
struct Foo;
struct Bar;
impl core::ops::Deref for Foo {
    type Target = Bar;
}

fn main() {
    test(&Foo);
}
fn test(arg: &Bar) {}
            "#,
        );
    }

    #[test]
    fn test_add_reference_to_method_call() {
        check_fix(
            r#"
fn main() {
    Test.call_by_ref($0123);
}
struct Test;
impl Test {
    fn call_by_ref(&self, arg: &i32) {}
}
            "#,
            r#"
fn main() {
    Test.call_by_ref(&123);
}
struct Test;
impl Test {
    fn call_by_ref(&self, arg: &i32) {}
}
            "#,
        );
    }

    #[test]
    fn test_add_reference_to_let_stmt() {
        check_fix(
            r#"
fn main() {
    let test: &i32 = $0123;
}
            "#,
            r#"
fn main() {
    let test: &i32 = &123;
}
            "#,
        );
    }

    #[test]
    fn test_add_mutable_reference_to_let_stmt() {
        check_fix(
            r#"
fn main() {
    let test: &mut i32 = $0123;
}
            "#,
            r#"
fn main() {
    let test: &mut i32 = &mut 123;
}
            "#,
        );
    }

    #[test]
    fn test_wrap_return_type_option() {
        check_fix(
            r#"
//- minicore: option, result
fn div(x: i32, y: i32) -> Option<i32> {
    if y == 0 {
        return None;
    }
    x / y$0
}
"#,
            r#"
fn div(x: i32, y: i32) -> Option<i32> {
    if y == 0 {
        return None;
    }
    Some(x / y)
}
"#,
        );
    }

    #[test]
    fn const_generic_type_mismatch() {
        check_diagnostics(
            r#"
            pub struct Rate<const N: u32>;
            fn f<const N: u64>() -> Rate<N> { // FIXME: add some error
                loop {}
            }
            fn run(t: Rate<5>) {
            }
            fn main() {
                run(f()) // FIXME: remove this error
                  //^^^ error: expected Rate<5>, found Rate<_>
            }
"#,
        );
    }

    #[test]
    fn const_generic_unknown() {
        check_diagnostics(
            r#"
            pub struct Rate<T, const NOM: u32, const DENOM: u32>(T);
            fn run(t: Rate<u32, 1, 1>) {
            }
            fn main() {
                run(Rate::<_, _, _>(5));
            }
"#,
        );
    }

    #[test]
    fn test_wrap_return_type_option_tails() {
        check_fix(
            r#"
//- minicore: option, result
fn div(x: i32, y: i32) -> Option<i32> {
    if y == 0 {
        Some(0)
    } else if true {
        100$0
    } else {
        None
    }
}
"#,
            r#"
fn div(x: i32, y: i32) -> Option<i32> {
    if y == 0 {
        Some(0)
    } else if true {
        Some(100)
    } else {
        None
    }
}
"#,
        );
    }

    #[test]
    fn test_wrap_return_type() {
        check_fix(
            r#"
//- minicore: option, result
fn div(x: i32, y: i32) -> Result<i32, ()> {
    if y == 0 {
        return Err(());
    }
    x / y$0
}
"#,
            r#"
fn div(x: i32, y: i32) -> Result<i32, ()> {
    if y == 0 {
        return Err(());
    }
    Ok(x / y)
}
"#,
        );
    }

    #[test]
    fn test_wrap_return_type_handles_generic_functions() {
        check_fix(
            r#"
//- minicore: option, result
fn div<T>(x: T) -> Result<T, i32> {
    if x == 0 {
        return Err(7);
    }
    $0x
}
"#,
            r#"
fn div<T>(x: T) -> Result<T, i32> {
    if x == 0 {
        return Err(7);
    }
    Ok(x)
}
"#,
        );
    }

    #[test]
    fn test_wrap_return_type_handles_type_aliases() {
        check_fix(
            r#"
//- minicore: option, result
type MyResult<T> = Result<T, ()>;

fn div(x: i32, y: i32) -> MyResult<i32> {
    if y == 0 {
        return Err(());
    }
    x $0/ y
}
"#,
            r#"
type MyResult<T> = Result<T, ()>;

fn div(x: i32, y: i32) -> MyResult<i32> {
    if y == 0 {
        return Err(());
    }
    Ok(x / y)
}
"#,
        );
    }

    #[test]
    fn test_in_const_and_static() {
        check_fix(
            r#"
//- minicore: option, result
static A: Option<()> = {($0)};
            "#,
            r#"
static A: Option<()> = {Some(())};
            "#,
        );
        check_fix(
            r#"
//- minicore: option, result
const _: Option<()> = {($0)};
            "#,
            r#"
const _: Option<()> = {Some(())};
            "#,
        );
    }

    #[test]
    fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
        check_no_fix(
            r#"
//- minicore: option, result
fn foo() -> Result<(), i32> { 0$0 }
"#,
        );
    }

    #[test]
    fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() {
        check_no_fix(
            r#"
//- minicore: option, result
enum SomeOtherEnum { Ok(i32), Err(String) }

fn foo() -> SomeOtherEnum { 0$0 }
"#,
        );
    }

    #[test]
    fn remove_semicolon() {
        check_fix(r#"fn f() -> i32 { 92$0; }"#, r#"fn f() -> i32 { 92 }"#);
    }
}
<commit_msg>Auto merge of #12696 - hi-rustin:rustin-patch-fix, r=Veykril<commit_after>use hir::{db::AstDatabase, HirDisplay, Type};
use ide_db::{famous_defs::FamousDefs, source_change::SourceChange};
use syntax::{
    ast::{BlockExpr, ExprStmt},
    AstNode,
};
use text_edit::TextEdit;

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

// Diagnostic: type-mismatch
//
// This diagnostic is triggered when the type of an expression does not match
// the expected type.
pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Diagnostic {
    let mut diag = Diagnostic::new(
        "type-mismatch",
        format!(
            "expected {}, found {}",
            d.expected.display(ctx.sema.db),
            d.actual.display(ctx.sema.db)
        ),
        ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
    )
    .with_fixes(fixes(ctx, d));
    if diag.fixes.is_none() {
        diag.experimental = true;
    }
    diag
}

fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Option<Vec<Assist>> {
    let mut fixes = Vec::new();

    add_reference(ctx, d, &mut fixes);
    add_missing_ok_or_some(ctx, d, &mut fixes);
    remove_semicolon(ctx, d, &mut fixes);
    str_ref_to_string(ctx, d, &mut fixes);

    if fixes.is_empty() {
        None
    } else {
        Some(fixes)
    }
}

fn add_reference(
    ctx: &DiagnosticsContext<'_>,
    d: &hir::TypeMismatch,
    acc: &mut Vec<Assist>,
) -> Option<()> {
    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
    let expr_node = d.expr.value.to_node(&root);

    let range = ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range;

    let (_, mutability) = d.expected.as_reference()?;
    let actual_with_ref = Type::reference(&d.actual, mutability);
    if !actual_with_ref.could_coerce_to(ctx.sema.db, &d.expected) {
        return None;
    }

    let ampersands = format!("&{}", mutability.as_keyword_for_ref());

    let edit = TextEdit::insert(expr_node.syntax().text_range().start(), ampersands);
    let source_change =
        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
    acc.push(fix("add_reference_here", "Add reference here", source_change, range));
    Some(())
}

fn add_missing_ok_or_some(
    ctx: &DiagnosticsContext<'_>,
    d: &hir::TypeMismatch,
    acc: &mut Vec<Assist>,
) -> Option<()> {
    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
    let expr = d.expr.value.to_node(&root);
    let expr_range = expr.syntax().text_range();
    let scope = ctx.sema.scope(expr.syntax())?;

    let expected_adt = d.expected.as_adt()?;
    let expected_enum = expected_adt.as_enum()?;

    let famous_defs = FamousDefs(&ctx.sema, scope.krate());
    let core_result = famous_defs.core_result_Result();
    let core_option = famous_defs.core_option_Option();

    if Some(expected_enum) != core_result && Some(expected_enum) != core_option {
        return None;
    }

    let variant_name = if Some(expected_enum) == core_result { "Ok" } else { "Some" };

    let wrapped_actual_ty = expected_adt.ty_with_args(ctx.sema.db, &[d.actual.clone()]);

    if !d.expected.could_unify_with(ctx.sema.db, &wrapped_actual_ty) {
        return None;
    }

    let mut builder = TextEdit::builder();
    builder.insert(expr.syntax().text_range().start(), format!("{}(", variant_name));
    builder.insert(expr.syntax().text_range().end(), ")".to_string());
    let source_change =
        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), builder.finish());
    let name = format!("Wrap in {}", variant_name);
    acc.push(fix("wrap_in_constructor", &name, source_change, expr_range));
    Some(())
}

fn remove_semicolon(
    ctx: &DiagnosticsContext<'_>,
    d: &hir::TypeMismatch,
    acc: &mut Vec<Assist>,
) -> Option<()> {
    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
    let expr = d.expr.value.to_node(&root);
    if !d.actual.is_unit() {
        return None;
    }
    let block = BlockExpr::cast(expr.syntax().clone())?;
    let expr_before_semi =
        block.statements().last().and_then(|s| ExprStmt::cast(s.syntax().clone()))?;
    let type_before_semi = ctx.sema.type_of_expr(&expr_before_semi.expr()?)?.original();
    if !type_before_semi.could_coerce_to(ctx.sema.db, &d.expected) {
        return None;
    }
    let semicolon_range = expr_before_semi.semicolon_token()?.text_range();

    let edit = TextEdit::delete(semicolon_range);
    let source_change =
        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);

    acc.push(fix("remove_semicolon", "Remove this semicolon", source_change, semicolon_range));
    Some(())
}

fn str_ref_to_string(
    ctx: &DiagnosticsContext<'_>,
    d: &hir::TypeMismatch,
    acc: &mut Vec<Assist>,
) -> Option<()> {
    let expected = d.expected.display(ctx.sema.db);
    let actual = d.actual.display(ctx.sema.db);

    if expected.to_string() != "String" || actual.to_string() != "&str" {
        return None;
    }

    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
    let expr = d.expr.value.to_node(&root);
    let expr_range = expr.syntax().text_range();

    let to_string = format!(".to_string()");

    let edit = TextEdit::insert(expr.syntax().text_range().end(), to_string);
    let source_change =
        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
    acc.push(fix("str_ref_to_string", "Add .to_string() here", source_change, expr_range));

    Some(())
}

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

    #[test]
    fn missing_reference() {
        check_diagnostics(
            r#"
fn main() {
    test(123);
       //^^^ 💡 error: expected &i32, found i32
}
fn test(arg: &i32) {}
"#,
        );
    }

    #[test]
    fn test_add_reference_to_int() {
        check_fix(
            r#"
fn main() {
    test(123$0);
}
fn test(arg: &i32) {}
            "#,
            r#"
fn main() {
    test(&123);
}
fn test(arg: &i32) {}
            "#,
        );
    }

    #[test]
    fn test_add_mutable_reference_to_int() {
        check_fix(
            r#"
fn main() {
    test($0123);
}
fn test(arg: &mut i32) {}
            "#,
            r#"
fn main() {
    test(&mut 123);
}
fn test(arg: &mut i32) {}
            "#,
        );
    }

    #[test]
    fn test_add_reference_to_array() {
        check_fix(
            r#"
//- minicore: coerce_unsized
fn main() {
    test($0[1, 2, 3]);
}
fn test(arg: &[i32]) {}
            "#,
            r#"
fn main() {
    test(&[1, 2, 3]);
}
fn test(arg: &[i32]) {}
            "#,
        );
    }

    #[test]
    fn test_add_reference_with_autoderef() {
        check_fix(
            r#"
//- minicore: coerce_unsized, deref
struct Foo;
struct Bar;
impl core::ops::Deref for Foo {
    type Target = Bar;
}

fn main() {
    test($0Foo);
}
fn test(arg: &Bar) {}
            "#,
            r#"
struct Foo;
struct Bar;
impl core::ops::Deref for Foo {
    type Target = Bar;
}

fn main() {
    test(&Foo);
}
fn test(arg: &Bar) {}
            "#,
        );
    }

    #[test]
    fn test_add_reference_to_method_call() {
        check_fix(
            r#"
fn main() {
    Test.call_by_ref($0123);
}
struct Test;
impl Test {
    fn call_by_ref(&self, arg: &i32) {}
}
            "#,
            r#"
fn main() {
    Test.call_by_ref(&123);
}
struct Test;
impl Test {
    fn call_by_ref(&self, arg: &i32) {}
}
            "#,
        );
    }

    #[test]
    fn test_add_reference_to_let_stmt() {
        check_fix(
            r#"
fn main() {
    let test: &i32 = $0123;
}
            "#,
            r#"
fn main() {
    let test: &i32 = &123;
}
            "#,
        );
    }

    #[test]
    fn test_add_mutable_reference_to_let_stmt() {
        check_fix(
            r#"
fn main() {
    let test: &mut i32 = $0123;
}
            "#,
            r#"
fn main() {
    let test: &mut i32 = &mut 123;
}
            "#,
        );
    }

    #[test]
    fn test_wrap_return_type_option() {
        check_fix(
            r#"
//- minicore: option, result
fn div(x: i32, y: i32) -> Option<i32> {
    if y == 0 {
        return None;
    }
    x / y$0
}
"#,
            r#"
fn div(x: i32, y: i32) -> Option<i32> {
    if y == 0 {
        return None;
    }
    Some(x / y)
}
"#,
        );
    }

    #[test]
    fn const_generic_type_mismatch() {
        check_diagnostics(
            r#"
            pub struct Rate<const N: u32>;
            fn f<const N: u64>() -> Rate<N> { // FIXME: add some error
                loop {}
            }
            fn run(t: Rate<5>) {
            }
            fn main() {
                run(f()) // FIXME: remove this error
                  //^^^ error: expected Rate<5>, found Rate<_>
            }
"#,
        );
    }

    #[test]
    fn const_generic_unknown() {
        check_diagnostics(
            r#"
            pub struct Rate<T, const NOM: u32, const DENOM: u32>(T);
            fn run(t: Rate<u32, 1, 1>) {
            }
            fn main() {
                run(Rate::<_, _, _>(5));
            }
"#,
        );
    }

    #[test]
    fn test_wrap_return_type_option_tails() {
        check_fix(
            r#"
//- minicore: option, result
fn div(x: i32, y: i32) -> Option<i32> {
    if y == 0 {
        Some(0)
    } else if true {
        100$0
    } else {
        None
    }
}
"#,
            r#"
fn div(x: i32, y: i32) -> Option<i32> {
    if y == 0 {
        Some(0)
    } else if true {
        Some(100)
    } else {
        None
    }
}
"#,
        );
    }

    #[test]
    fn test_wrap_return_type() {
        check_fix(
            r#"
//- minicore: option, result
fn div(x: i32, y: i32) -> Result<i32, ()> {
    if y == 0 {
        return Err(());
    }
    x / y$0
}
"#,
            r#"
fn div(x: i32, y: i32) -> Result<i32, ()> {
    if y == 0 {
        return Err(());
    }
    Ok(x / y)
}
"#,
        );
    }

    #[test]
    fn test_wrap_return_type_handles_generic_functions() {
        check_fix(
            r#"
//- minicore: option, result
fn div<T>(x: T) -> Result<T, i32> {
    if x == 0 {
        return Err(7);
    }
    $0x
}
"#,
            r#"
fn div<T>(x: T) -> Result<T, i32> {
    if x == 0 {
        return Err(7);
    }
    Ok(x)
}
"#,
        );
    }

    #[test]
    fn test_wrap_return_type_handles_type_aliases() {
        check_fix(
            r#"
//- minicore: option, result
type MyResult<T> = Result<T, ()>;

fn div(x: i32, y: i32) -> MyResult<i32> {
    if y == 0 {
        return Err(());
    }
    x $0/ y
}
"#,
            r#"
type MyResult<T> = Result<T, ()>;

fn div(x: i32, y: i32) -> MyResult<i32> {
    if y == 0 {
        return Err(());
    }
    Ok(x / y)
}
"#,
        );
    }

    #[test]
    fn test_in_const_and_static() {
        check_fix(
            r#"
//- minicore: option, result
static A: Option<()> = {($0)};
            "#,
            r#"
static A: Option<()> = {Some(())};
            "#,
        );
        check_fix(
            r#"
//- minicore: option, result
const _: Option<()> = {($0)};
            "#,
            r#"
const _: Option<()> = {Some(())};
            "#,
        );
    }

    #[test]
    fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
        check_no_fix(
            r#"
//- minicore: option, result
fn foo() -> Result<(), i32> { 0$0 }
"#,
        );
    }

    #[test]
    fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() {
        check_no_fix(
            r#"
//- minicore: option, result
enum SomeOtherEnum { Ok(i32), Err(String) }

fn foo() -> SomeOtherEnum { 0$0 }
"#,
        );
    }

    #[test]
    fn remove_semicolon() {
        check_fix(r#"fn f() -> i32 { 92$0; }"#, r#"fn f() -> i32 { 92 }"#);
    }

    #[test]
    fn str_ref_to_string() {
        check_fix(
            r#"
struct String;

fn test() -> String {
    "a"$0
}
            "#,
            r#"
struct String;

fn test() -> String {
    "a".to_string()
}
            "#,
        );
    }
}
<|endoftext|>


<commit_before>extern crate neat;
extern crate rand;
extern crate graph_neighbor_matching;
extern crate graph_io_gml as gml;
extern crate closed01;

use neat::population::{Population, Unrated, Runner};
use neat::network::{NetworkGenome, NetworkGenomeDistance, NodeType, Environment,
                    ElementStrategy};
use neat::fitness::Fitness;
use neat::traits::Mate;
use neat::crossover::ProbabilisticCrossover;
use neat::prob::is_probable;
use graph_neighbor_matching::{SimilarityMatrix, ScoreNorm, NodeColorMatching};
use graph_neighbor_matching::graph::{OwnedGraph, GraphBuilder};
use rand::{Rng, Closed01};
use std::marker::PhantomData;
use neat::mutate::{MutateMethod, MutateMethodWeighting};

fn load_graph(graph_file: &str) -> OwnedGraph<NodeType> {
    use std::fs::File;
    use std::io::Read;

    let graph_s = {
        let mut graph_file = File::open(graph_file).unwrap();
        let mut graph_s = String::new();
        let _ = graph_file.read_to_string(&mut graph_s).unwrap();
        graph_s
    };

    let graph = gml::parse_gml(&graph_s,
                               &|sexp| -> Option<NodeType> {
                                   sexp.and_then(|se| {
                                       se.get_str().map(|s| {
                                           match s {
                                               "input" => NodeType::Input,
                                               "output" => NodeType::Output,
                                               "hidden" => NodeType::Hidden,
                                               _ => panic!("Invalid node type/weight"),
                                           }
                                       })
                                   })
                               },
                               &|_| -> Option<()> { Some(()) })
                    .unwrap();
    OwnedGraph::from_petgraph(&graph)
}

fn genome_to_graph(genome: &NetworkGenome) -> OwnedGraph<NodeType> {
    let mut builder = GraphBuilder::new();

    for (&innov, node) in genome.node_genes.map.iter() {
        // make sure the node exists, even if there are no connection to it.
        let _ = builder.add_node(innov.get(), node.node_type);
    }

    for link in genome.link_genes.map.values() {
        if link.active {
            builder.add_edge_unweighted(link.source_node_gene.get(), link.target_node_gene.get());
        }
    }

    return builder.graph();
}

#[derive(Debug)]
struct FitnessEvaluator {
    target_graph: OwnedGraph<NodeType>,
}

#[derive(Debug)]
struct NodeColors;

impl NodeColorMatching<NodeType> for NodeColors {
    fn node_color_matching(&self,
                           node_i_value: &NodeType,
                           node_j_value: &NodeType)
                           -> closed01::Closed01<f32> {
        if node_i_value == node_j_value {
            closed01::Closed01::one()
        } else {
            closed01::Closed01::zero()
        }
    }
}

impl FitnessEvaluator {
    // A larger fitness means "better"
    fn fitness(&self, genome: &NetworkGenome) -> f32 {
        let graph = genome_to_graph(genome);
        let mut s = SimilarityMatrix::new(&graph, &self.target_graph, NodeColors);
        s.iterate(50, 0.01);
        s.score_optimal_sum_norm(None, ScoreNorm::MaxDegree).get()
    }
}

struct ES;

impl ElementStrategy for ES {
    fn random_link_weight<R: Rng>(rng: &mut R) -> f64 {
        // XXX Choose a weight between -1 and 1?
        rng.gen()
    }
    fn random_activation_function<R: Rng>(rng: &mut R) -> u32 {
        rng.gen_range(0, 5)
    }
}

const POP_SIZE: usize = 100;
const INPUTS: usize = 2;
const OUTPUTS: usize = 3;

struct Mater<'a, T: ElementStrategy + 'a> {
    // probability for crossover. P_mutate = 1.0 - p_crossover
    p_crossover: Closed01<f32>,
    p_crossover_detail: ProbabilisticCrossover,
    mutate_weights: MutateMethodWeighting,
    env: &'a mut Environment<T>,
}

impl<'a, T: ElementStrategy> Mate<NetworkGenome> for Mater<'a, T> {
    // Add an argument that descibes whether both genomes are of equal fitness.
    // Pass individual, which includes the fitness.
    fn mate<R: Rng>(&mut self,
                    parent_left: &NetworkGenome,
                    parent_right: &NetworkGenome,
                    prefer_mutate: bool,
                    rng: &mut R)
                    -> NetworkGenome {
        if prefer_mutate == false && is_probable(&self.p_crossover, rng) {
            NetworkGenome::crossover(parent_left, parent_right, &self.p_crossover_detail, rng)
        } else {
            // mutate
            let mutate_method = MutateMethod::random_with(&self.mutate_weights, rng);
            self.env.mutate(parent_left, mutate_method, rng).
                or_else(|| self.env.mutate(parent_right, mutate_method, rng)).
                unwrap_or_else(|| parent_left.clone())
        }
    }
}

fn main() {
    let mut rng = rand::thread_rng();

    let fitness_evaluator = FitnessEvaluator { target_graph: load_graph("examples/jeffress.gml") };
    println!("{:?}", fitness_evaluator);

    // start with minimal random topology.
    let mut env: Environment<ES> = Environment::new();

    let template_genome = env.generate_genome(INPUTS, OUTPUTS);

    println!("{:#?}", template_genome);

    let mut initial_pop = Population::<_, Unrated>::new();

    for _ in 0..POP_SIZE {
        // Add a single link gene! This is required, otherwise we can't determine
        // correctly an InnovationRange.
        let genome = env.mutate_add_connection(&template_genome, &mut rng).unwrap();

        initial_pop.add_genome(Box::new(genome));
    }
    assert!(initial_pop.len() == POP_SIZE);

    let mut mater = Mater {
        p_crossover: Closed01(0.5),
        p_crossover_detail: ProbabilisticCrossover {
                prob_match_left: Closed01(0.5), /* NEAT always selects a random parent for matching genes */
                prob_disjoint_left: Closed01(0.9),
                prob_excess_left: Closed01(0.9),
                prob_disjoint_right: Closed01(0.15),
                prob_excess_right: Closed01(0.15),
            },
        mutate_weights: MutateMethodWeighting {
            w_modify_weight: 1,
            w_add_node: 1,
            w_add_connection: 1,
        },

        env: &mut env,
    };

    let compatibility = NetworkGenomeDistance {
        excess: 1.0,
        disjoint: 1.0,
        weight: 0.0,
    };

    let mut runner = Runner {
        pop_size: POP_SIZE,
        elite_percentage: Closed01(0.05),
        selection_percentage: Closed01(0.2),
        tournament_k: 3,
        compatibility_threshold: 1.0,
        compatibility: &compatibility,
        mate: &mut mater,
        fitness: &|genome| Fitness::new(fitness_evaluator.fitness(genome) as f64),
        _marker: PhantomData,
    };

    let (iter, new_pop) = runner.run(initial_pop,
                                     &|iter, pop| {
                                         iter >= 100 || pop.max_fitness().unwrap().get() > 0.99
                                     },
                                     &mut rng);

    let new_pop = new_pop.sort();

    println!("iter: {}", iter);
    println!("{:#?}", new_pop);
}
<commit_msg>Include the weight in the graph<commit_after>extern crate neat;
extern crate rand;
extern crate graph_neighbor_matching;
extern crate graph_io_gml as gml;
extern crate closed01;

use neat::population::{Population, Unrated, Runner};
use neat::network::{NetworkGenome, NetworkGenomeDistance, NodeType, Environment,
                    ElementStrategy};
use neat::fitness::Fitness;
use neat::traits::Mate;
use neat::crossover::ProbabilisticCrossover;
use neat::prob::is_probable;
use graph_neighbor_matching::{SimilarityMatrix, ScoreNorm, NodeColorMatching};
use graph_neighbor_matching::graph::{OwnedGraph, GraphBuilder};
use rand::{Rng, Closed01};
use std::marker::PhantomData;
use neat::mutate::{MutateMethod, MutateMethodWeighting};

fn load_graph(graph_file: &str) -> OwnedGraph<NodeType> {
    use std::fs::File;
    use std::io::Read;

    let graph_s = {
        let mut graph_file = File::open(graph_file).unwrap();
        let mut graph_s = String::new();
        let _ = graph_file.read_to_string(&mut graph_s).unwrap();
        graph_s
    };

    let graph = gml::parse_gml(&graph_s,
                               &|sexp| -> Option<NodeType> {
                                   sexp.and_then(|se| {
                                       se.get_str().map(|s| {
                                           match s {
                                               "input" => NodeType::Input,
                                               "output" => NodeType::Output,
                                               "hidden" => NodeType::Hidden,
                                               _ => panic!("Invalid node type/weight"),
                                           }
                                       })
                                   })
                               },
                               &|_| -> Option<()> { Some(()) })
                    .unwrap();
    OwnedGraph::from_petgraph(&graph)
}

fn genome_to_graph(genome: &NetworkGenome) -> OwnedGraph<NodeType> {
    let mut builder = GraphBuilder::new();

    for (&innov, node) in genome.node_genes.map.iter() {
        // make sure the node exists, even if there are no connection to it.
        let _ = builder.add_node(innov.get(), node.node_type);
    }

    for link in genome.link_genes.map.values() {
        if link.active {
            builder.add_edge(link.source_node_gene.get(), link.target_node_gene.get(), closed01::Closed01::new(link.weight as f32));
        }
    }

    return builder.graph();
}

#[derive(Debug)]
struct FitnessEvaluator {
    target_graph: OwnedGraph<NodeType>,
}

#[derive(Debug)]
struct NodeColors;

impl NodeColorMatching<NodeType> for NodeColors {
    fn node_color_matching(&self,
                           node_i_value: &NodeType,
                           node_j_value: &NodeType)
                           -> closed01::Closed01<f32> {
        if node_i_value == node_j_value {
            closed01::Closed01::one()
        } else {
            closed01::Closed01::zero()
        }
    }
}

impl FitnessEvaluator {
    // A larger fitness means "better"
    fn fitness(&self, genome: &NetworkGenome) -> f32 {
        let graph = genome_to_graph(genome);
        let mut s = SimilarityMatrix::new(&graph, &self.target_graph, NodeColors);
        s.iterate(50, 0.01);
        s.score_optimal_sum_norm(None, ScoreNorm::MaxDegree).get()
    }
}

struct ES;

impl ElementStrategy for ES {
    fn random_link_weight<R: Rng>(rng: &mut R) -> f64 {
        // XXX Choose a weight between -1 and 1?
        rng.gen()
    }
    fn random_activation_function<R: Rng>(rng: &mut R) -> u32 {
        rng.gen_range(0, 5)
    }
}

const POP_SIZE: usize = 100;
const INPUTS: usize = 2;
const OUTPUTS: usize = 3;

struct Mater<'a, T: ElementStrategy + 'a> {
    // probability for crossover. P_mutate = 1.0 - p_crossover
    p_crossover: Closed01<f32>,
    p_crossover_detail: ProbabilisticCrossover,
    mutate_weights: MutateMethodWeighting,
    env: &'a mut Environment<T>,
}

impl<'a, T: ElementStrategy> Mate<NetworkGenome> for Mater<'a, T> {
    // Add an argument that descibes whether both genomes are of equal fitness.
    // Pass individual, which includes the fitness.
    fn mate<R: Rng>(&mut self,
                    parent_left: &NetworkGenome,
                    parent_right: &NetworkGenome,
                    prefer_mutate: bool,
                    rng: &mut R)
                    -> NetworkGenome {
        if prefer_mutate == false && is_probable(&self.p_crossover, rng) {
            NetworkGenome::crossover(parent_left, parent_right, &self.p_crossover_detail, rng)
        } else {
            // mutate
            let mutate_method = MutateMethod::random_with(&self.mutate_weights, rng);
            self.env.mutate(parent_left, mutate_method, rng).
                or_else(|| self.env.mutate(parent_right, mutate_method, rng)).
                unwrap_or_else(|| parent_left.clone())
        }
    }
}

fn main() {
    let mut rng = rand::thread_rng();

    let fitness_evaluator = FitnessEvaluator { target_graph: load_graph("examples/jeffress.gml") };
    println!("{:?}", fitness_evaluator);

    // start with minimal random topology.
    let mut env: Environment<ES> = Environment::new();

    let template_genome = env.generate_genome(INPUTS, OUTPUTS);

    println!("{:#?}", template_genome);

    let mut initial_pop = Population::<_, Unrated>::new();

    for _ in 0..POP_SIZE {
        // Add a single link gene! This is required, otherwise we can't determine
        // correctly an InnovationRange.
        let genome = env.mutate_add_connection(&template_genome, &mut rng).unwrap();

        initial_pop.add_genome(Box::new(genome));
    }
    assert!(initial_pop.len() == POP_SIZE);

    let mut mater = Mater {
        p_crossover: Closed01(0.5),
        p_crossover_detail: ProbabilisticCrossover {
                prob_match_left: Closed01(0.5), /* NEAT always selects a random parent for matching genes */
                prob_disjoint_left: Closed01(0.9),
                prob_excess_left: Closed01(0.9),
                prob_disjoint_right: Closed01(0.15),
                prob_excess_right: Closed01(0.15),
            },
        mutate_weights: MutateMethodWeighting {
            w_modify_weight: 1,
            w_add_node: 1,
            w_add_connection: 1,
        },

        env: &mut env,
    };

    let compatibility = NetworkGenomeDistance {
        excess: 1.0,
        disjoint: 1.0,
        weight: 0.0,
    };

    let mut runner = Runner {
        pop_size: POP_SIZE,
        elite_percentage: Closed01(0.05),
        selection_percentage: Closed01(0.2),
        tournament_k: 3,
        compatibility_threshold: 1.0,
        compatibility: &compatibility,
        mate: &mut mater,
        fitness: &|genome| Fitness::new(fitness_evaluator.fitness(genome) as f64),
        _marker: PhantomData,
    };

    let (iter, new_pop) = runner.run(initial_pop,
                                     &|iter, pop| {
                                         iter >= 100 || pop.max_fitness().unwrap().get() > 0.99
                                     },
                                     &mut rng);

    let new_pop = new_pop.sort();

    println!("iter: {}", iter);
    println!("{:#?}", new_pop);
}
<|endoftext|>


<commit_before>extern crate iron;
extern crate http;
extern crate mount;

use std::io::net::ip::Ipv4Addr;

use http::status;
use iron::{Iron, Request, Response, Alloy, Server, Chain, Status, Continue, Unwind, FromFn};
use mount::Mount;

fn intercept(_req: &mut Request, _res: &mut Response,
             _alloy: &mut Alloy) -> Status {
    Unwind
}

fn send_hello(_req: &mut Request, res: &mut Response,
              _alloy: &mut Alloy) -> Status {
    let _ = res.serve(status::Ok, "Hello!");
    Continue
}

fn main() {
    let mut server: Server = Iron::new();
    server.chain.link(Mount::new("/blocked", FromFn::new(intercept)));
    server.chain.link(FromFn::new(send_hello));
    server.listen(Ipv4Addr(127, 0, 0, 1), 3000);
}

<commit_msg>(fix) Updated examples.<commit_after>extern crate iron;
extern crate http;
extern crate mount;

use std::io::net::ip::Ipv4Addr;

use http::status;
use iron::{Iron, Request, Response, Server, Chain, Status, Continue, Unwind, FromFn};
use mount::Mount;

fn intercept(_: &mut Request, res: &mut Response) -> Status {
    let _ = res.serve(status::Ok, "Blocked!");
    Unwind
}

fn send_hello(_: &mut Request, res: &mut Response) -> Status {
    let _ = res.serve(status::Ok, "Hello!");
    Continue
}

fn main() {
    let mut server: Server = Iron::new();
    server.chain.link(Mount::new(&["blocked"], FromFn::new(intercept)));
    server.chain.link(FromFn::new(send_hello));
    server.listen(Ipv4Addr(127, 0, 0, 1), 3000);
}

<|endoftext|>


<commit_before>extern crate codespawn;

fn main()
{
    // generate from XML definition
    let raw_code = codespawn::from_xml("examples/sample.xml").unwrap();
    // generate from JSON definition
    let raw_code = codespawn::from_json("examples/sample.json").unwrap();

    println!("\n*** Language specific configs (if defined):");
    for c in raw_code.configs.iter() {
        println!("{}", c.1);
    }

    println!("*** Structure of raw (unformatted) code data:\n{}", raw_code);
    println!("*** Structure of C++ code generated from raw code data:\n{}", raw_code.to_cpp());
    println!("*** Structure of Rust code generated from raw code data:\n{}", raw_code.to_rust());
    println!("*** Generated C++ code:\n\n{}", raw_code.to_cpp().to_string());
    println!("*** Generated Rust code:\n\n{}", raw_code.to_rust().to_string());

    // save generated code to file
    let _ = raw_code.to_cpp().to_file("sample.cpp");
    let _ = raw_code.to_rust().to_file("sample.rs");
}
<commit_msg>added comment<commit_after>extern crate codespawn;

fn main()
{
    // generate from XML definition
    let raw_code = codespawn::from_xml("examples/sample.xml").unwrap();
    // generate from JSON definition
    //let raw_code = codespawn::from_json("examples/sample.json").unwrap();

    println!("\n*** Language specific configs (if defined):");
    for c in raw_code.configs.iter() {
        println!("{}", c.1);
    }

    println!("*** Structure of raw (unformatted) code data:\n{}", raw_code);
    println!("*** Structure of C++ code generated from raw code data:\n{}", raw_code.to_cpp());
    println!("*** Structure of Rust code generated from raw code data:\n{}", raw_code.to_rust());
    println!("*** Generated C++ code:\n\n{}", raw_code.to_cpp().to_string());
    println!("*** Generated Rust code:\n\n{}", raw_code.to_rust().to_string());

    // save generated code to file
    let _ = raw_code.to_cpp().to_file("sample.cpp");
    let _ = raw_code.to_rust().to_file("sample.rs");
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

//! A rust helper to ease the use of Gecko's refcounted types.

use crate::gecko_bindings::sugar::ownership::HasArcFFI;
use crate::gecko_bindings::{bindings, structs};
use crate::Atom;
use servo_arc::Arc;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::{fmt, mem, ptr};

/// Trait for all objects that have Addref() and Release
/// methods and can be placed inside RefPtr<T>
pub unsafe trait RefCounted {
    /// Bump the reference count.
    fn addref(&self);
    /// Decrease the reference count.
    unsafe fn release(&self);
}

/// Trait for types which can be shared across threads in RefPtr.
pub unsafe trait ThreadSafeRefCounted: RefCounted {}

/// A custom RefPtr implementation to take into account Drop semantics and
/// a bit less-painful memory management.
pub struct RefPtr<T: RefCounted> {
    ptr: *mut T,
    _marker: PhantomData<T>,
}

impl<T: RefCounted> fmt::Debug for RefPtr<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("RefPtr { ")?;
        self.ptr.fmt(f)?;
        f.write_str("}")
    }
}

/// A RefPtr that we know is uniquely owned.
///
/// This is basically Box<T>, with the additional guarantee that the box can be
/// safely interpreted as a RefPtr<T> (with refcount 1)
///
/// This is useful when you wish to create a refptr and mutate it temporarily,
/// while it is still uniquely owned.
pub struct UniqueRefPtr<T: RefCounted>(RefPtr<T>);

// There is no safe conversion from &T to RefPtr<T> (like Gecko has)
// because this lets you break UniqueRefPtr's guarantee

impl<T: RefCounted> RefPtr<T> {
    /// Create a new RefPtr from an already addrefed pointer obtained from FFI.
    ///
    /// The pointer must be valid, non-null and have been addrefed.
    pub unsafe fn from_addrefed(ptr: *mut T) -> Self {
        debug_assert!(!ptr.is_null());
        RefPtr {
            ptr: ptr,
            _marker: PhantomData,
        }
    }

    /// Returns whether the current pointer is null.
    pub fn is_null(&self) -> bool {
        self.ptr.is_null()
    }

    /// Returns a null pointer.
    pub fn null() -> Self {
        Self {
            ptr: ptr::null_mut(),
            _marker: PhantomData,
        }
    }

    /// Create a new RefPtr from a pointer obtained from FFI.
    ///
    /// This method calls addref() internally
    pub unsafe fn new(ptr: *mut T) -> Self {
        let ret = RefPtr {
            ptr,
            _marker: PhantomData,
        };
        ret.addref();
        ret
    }

    /// Produces an FFI-compatible RefPtr that can be stored in style structs.
    ///
    /// structs::RefPtr does not have a destructor, so this may leak
    pub fn forget(self) -> structs::RefPtr<T> {
        let ret = structs::RefPtr {
            mRawPtr: self.ptr,
            _phantom_0: PhantomData,
        };
        mem::forget(self);
        ret
    }

    /// Returns the raw inner pointer to be fed back into FFI.
    pub fn get(&self) -> *mut T {
        self.ptr
    }

    /// Addref the inner data, obviously leaky on its own.
    pub fn addref(&self) {
        if !self.ptr.is_null() {
            unsafe {
                (*self.ptr).addref();
            }
        }
    }

    /// Release the inner data.
    ///
    /// Call only when the data actually needs releasing.
    pub unsafe fn release(&self) {
        if !self.ptr.is_null() {
            (*self.ptr).release();
        }
    }
}

impl<T: RefCounted> UniqueRefPtr<T> {
    /// Create a unique refptr from an already addrefed pointer obtained from
    /// FFI.
    ///
    /// The refcount must be one.
    ///
    /// The pointer must be valid and non null
    pub unsafe fn from_addrefed(ptr: *mut T) -> Self {
        UniqueRefPtr(RefPtr::from_addrefed(ptr))
    }

    /// Convert to a RefPtr so that it can be used.
    pub fn get(self) -> RefPtr<T> {
        self.0
    }
}

impl<T: RefCounted> Deref for RefPtr<T> {
    type Target = T;
    fn deref(&self) -> &T {
        debug_assert!(!self.ptr.is_null());
        unsafe { &*self.ptr }
    }
}

impl<T: RefCounted> Deref for UniqueRefPtr<T> {
    type Target = T;
    fn deref(&self) -> &T {
        unsafe { &*self.0.ptr }
    }
}

impl<T: RefCounted> DerefMut for UniqueRefPtr<T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *self.0.ptr }
    }
}

impl<T: RefCounted> structs::RefPtr<T> {
    /// Produces a Rust-side RefPtr from an FFI RefPtr, bumping the refcount.
    ///
    /// Must be called on a valid, non-null structs::RefPtr<T>.
    pub unsafe fn to_safe(&self) -> RefPtr<T> {
        let r = RefPtr {
            ptr: self.mRawPtr,
            _marker: PhantomData,
        };
        r.addref();
        r
    }
    /// Produces a Rust-side RefPtr, consuming the existing one (and not bumping
    /// the refcount).
    pub unsafe fn into_safe(self) -> RefPtr<T> {
        debug_assert!(!self.mRawPtr.is_null());
        RefPtr {
            ptr: self.mRawPtr,
            _marker: PhantomData,
        }
    }

    /// Replace a structs::RefPtr<T> with a different one, appropriately
    /// addref/releasing.
    ///
    /// Both `self` and `other` must be valid, but can be null.
    ///
    /// Safe when called on an aliased pointer because the refcount in that case
    /// needs to be at least two.
    pub unsafe fn set(&mut self, other: &Self) {
        self.clear();
        if !other.mRawPtr.is_null() {
            *self = other.to_safe().forget();
        }
    }

    /// Clear an instance of the structs::RefPtr<T>, by releasing
    /// it and setting its contents to null.
    ///
    /// `self` must be valid, but can be null.
    pub unsafe fn clear(&mut self) {
        if !self.mRawPtr.is_null() {
            (*self.mRawPtr).release();
            self.mRawPtr = ptr::null_mut();
        }
    }

    /// Replace a `structs::RefPtr<T>` with a `RefPtr<T>`,
    /// consuming the `RefPtr<T>`, and releasing the old
    /// value in `self` if necessary.
    ///
    /// `self` must be valid, possibly null.
    pub fn set_move(&mut self, other: RefPtr<T>) {
        if !self.mRawPtr.is_null() {
            unsafe {
                (*self.mRawPtr).release();
            }
        }
        *self = other.forget();
    }
}

impl<T> structs::RefPtr<T> {
    /// Sets the contents to an `Arc<T>`, releasing the old value in `self` if
    /// necessary.
    pub fn set_arc<U>(&mut self, other: Arc<U>)
    where
        U: HasArcFFI<FFIType = T>,
    {
        unsafe {
            U::release_opt(self.mRawPtr.as_ref());
        }
        self.set_arc_leaky(other);
    }

    /// Sets the contents to an Arc<T>
    /// will leak existing contents
    pub fn set_arc_leaky<U>(&mut self, other: Arc<U>)
    where
        U: HasArcFFI<FFIType = T>,
    {
        *self = unsafe { mem::transmute(Arc::into_raw_offset(other)) };
    }
}

impl<T: RefCounted> Drop for RefPtr<T> {
    fn drop(&mut self) {
        unsafe { self.release() }
    }
}

impl<T: RefCounted> Clone for RefPtr<T> {
    fn clone(&self) -> Self {
        self.addref();
        RefPtr {
            ptr: self.ptr,
            _marker: PhantomData,
        }
    }
}

impl<T: RefCounted> PartialEq for RefPtr<T> {
    fn eq(&self, other: &Self) -> bool {
        self.ptr == other.ptr
    }
}

unsafe impl<T: ThreadSafeRefCounted> Send for RefPtr<T> {}
unsafe impl<T: ThreadSafeRefCounted> Sync for RefPtr<T> {}

macro_rules! impl_refcount {
    ($t:ty, $addref:path, $release:path) => {
        unsafe impl RefCounted for $t {
            #[inline]
            fn addref(&self) {
                unsafe { $addref(self as *const _ as *mut _) }
            }

            #[inline]
            unsafe fn release(&self) {
                $release(self as *const _ as *mut _)
            }
        }
    };
}

// Companion of NS_DECL_THREADSAFE_FFI_REFCOUNTING.
//
// Gets you a free RefCounted impl implemented via FFI.
macro_rules! impl_threadsafe_refcount {
    ($t:ty, $addref:path, $release:path) => {
        impl_refcount!($t, $addref, $release);
        unsafe impl ThreadSafeRefCounted for $t {}
    };
}

impl_threadsafe_refcount!(
    structs::mozilla::URLExtraData,
    bindings::Gecko_AddRefURLExtraDataArbitraryThread,
    bindings::Gecko_ReleaseURLExtraDataArbitraryThread
);
impl_threadsafe_refcount!(
    structs::nsIReferrerInfo,
    bindings::Gecko_AddRefnsIReferrerInfoArbitraryThread,
    bindings::Gecko_ReleasensIReferrerInfoArbitraryThread
);
impl_threadsafe_refcount!(
    structs::nsIURI,
    bindings::Gecko_AddRefnsIURIArbitraryThread,
    bindings::Gecko_ReleasensIURIArbitraryThread
);
impl_threadsafe_refcount!(
    structs::SharedFontList,
    bindings::Gecko_AddRefSharedFontListArbitraryThread,
    bindings::Gecko_ReleaseSharedFontListArbitraryThread
);
impl_threadsafe_refcount!(
    structs::SheetLoadDataHolder,
    bindings::Gecko_AddRefSheetLoadDataHolderArbitraryThread,
    bindings::Gecko_ReleaseSheetLoadDataHolderArbitraryThread
);

#[inline]
unsafe fn addref_atom(atom: *mut structs::nsAtom) {
    mem::forget(Atom::from_raw(atom));
}

#[inline]
unsafe fn release_atom(atom: *mut structs::nsAtom) {
    let _ = Atom::from_addrefed(atom);
}
impl_threadsafe_refcount!(structs::nsAtom, addref_atom, release_atom);
<commit_msg>style: Remove unused FFI function to refcount nsIReferrerInfo.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

//! A rust helper to ease the use of Gecko's refcounted types.

use crate::gecko_bindings::sugar::ownership::HasArcFFI;
use crate::gecko_bindings::{bindings, structs};
use crate::Atom;
use servo_arc::Arc;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::{fmt, mem, ptr};

/// Trait for all objects that have Addref() and Release
/// methods and can be placed inside RefPtr<T>
pub unsafe trait RefCounted {
    /// Bump the reference count.
    fn addref(&self);
    /// Decrease the reference count.
    unsafe fn release(&self);
}

/// Trait for types which can be shared across threads in RefPtr.
pub unsafe trait ThreadSafeRefCounted: RefCounted {}

/// A custom RefPtr implementation to take into account Drop semantics and
/// a bit less-painful memory management.
pub struct RefPtr<T: RefCounted> {
    ptr: *mut T,
    _marker: PhantomData<T>,
}

impl<T: RefCounted> fmt::Debug for RefPtr<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("RefPtr { ")?;
        self.ptr.fmt(f)?;
        f.write_str("}")
    }
}

/// A RefPtr that we know is uniquely owned.
///
/// This is basically Box<T>, with the additional guarantee that the box can be
/// safely interpreted as a RefPtr<T> (with refcount 1)
///
/// This is useful when you wish to create a refptr and mutate it temporarily,
/// while it is still uniquely owned.
pub struct UniqueRefPtr<T: RefCounted>(RefPtr<T>);

// There is no safe conversion from &T to RefPtr<T> (like Gecko has)
// because this lets you break UniqueRefPtr's guarantee

impl<T: RefCounted> RefPtr<T> {
    /// Create a new RefPtr from an already addrefed pointer obtained from FFI.
    ///
    /// The pointer must be valid, non-null and have been addrefed.
    pub unsafe fn from_addrefed(ptr: *mut T) -> Self {
        debug_assert!(!ptr.is_null());
        RefPtr {
            ptr: ptr,
            _marker: PhantomData,
        }
    }

    /// Returns whether the current pointer is null.
    pub fn is_null(&self) -> bool {
        self.ptr.is_null()
    }

    /// Returns a null pointer.
    pub fn null() -> Self {
        Self {
            ptr: ptr::null_mut(),
            _marker: PhantomData,
        }
    }

    /// Create a new RefPtr from a pointer obtained from FFI.
    ///
    /// This method calls addref() internally
    pub unsafe fn new(ptr: *mut T) -> Self {
        let ret = RefPtr {
            ptr,
            _marker: PhantomData,
        };
        ret.addref();
        ret
    }

    /// Produces an FFI-compatible RefPtr that can be stored in style structs.
    ///
    /// structs::RefPtr does not have a destructor, so this may leak
    pub fn forget(self) -> structs::RefPtr<T> {
        let ret = structs::RefPtr {
            mRawPtr: self.ptr,
            _phantom_0: PhantomData,
        };
        mem::forget(self);
        ret
    }

    /// Returns the raw inner pointer to be fed back into FFI.
    pub fn get(&self) -> *mut T {
        self.ptr
    }

    /// Addref the inner data, obviously leaky on its own.
    pub fn addref(&self) {
        if !self.ptr.is_null() {
            unsafe {
                (*self.ptr).addref();
            }
        }
    }

    /// Release the inner data.
    ///
    /// Call only when the data actually needs releasing.
    pub unsafe fn release(&self) {
        if !self.ptr.is_null() {
            (*self.ptr).release();
        }
    }
}

impl<T: RefCounted> UniqueRefPtr<T> {
    /// Create a unique refptr from an already addrefed pointer obtained from
    /// FFI.
    ///
    /// The refcount must be one.
    ///
    /// The pointer must be valid and non null
    pub unsafe fn from_addrefed(ptr: *mut T) -> Self {
        UniqueRefPtr(RefPtr::from_addrefed(ptr))
    }

    /// Convert to a RefPtr so that it can be used.
    pub fn get(self) -> RefPtr<T> {
        self.0
    }
}

impl<T: RefCounted> Deref for RefPtr<T> {
    type Target = T;
    fn deref(&self) -> &T {
        debug_assert!(!self.ptr.is_null());
        unsafe { &*self.ptr }
    }
}

impl<T: RefCounted> Deref for UniqueRefPtr<T> {
    type Target = T;
    fn deref(&self) -> &T {
        unsafe { &*self.0.ptr }
    }
}

impl<T: RefCounted> DerefMut for UniqueRefPtr<T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *self.0.ptr }
    }
}

impl<T: RefCounted> structs::RefPtr<T> {
    /// Produces a Rust-side RefPtr from an FFI RefPtr, bumping the refcount.
    ///
    /// Must be called on a valid, non-null structs::RefPtr<T>.
    pub unsafe fn to_safe(&self) -> RefPtr<T> {
        let r = RefPtr {
            ptr: self.mRawPtr,
            _marker: PhantomData,
        };
        r.addref();
        r
    }
    /// Produces a Rust-side RefPtr, consuming the existing one (and not bumping
    /// the refcount).
    pub unsafe fn into_safe(self) -> RefPtr<T> {
        debug_assert!(!self.mRawPtr.is_null());
        RefPtr {
            ptr: self.mRawPtr,
            _marker: PhantomData,
        }
    }

    /// Replace a structs::RefPtr<T> with a different one, appropriately
    /// addref/releasing.
    ///
    /// Both `self` and `other` must be valid, but can be null.
    ///
    /// Safe when called on an aliased pointer because the refcount in that case
    /// needs to be at least two.
    pub unsafe fn set(&mut self, other: &Self) {
        self.clear();
        if !other.mRawPtr.is_null() {
            *self = other.to_safe().forget();
        }
    }

    /// Clear an instance of the structs::RefPtr<T>, by releasing
    /// it and setting its contents to null.
    ///
    /// `self` must be valid, but can be null.
    pub unsafe fn clear(&mut self) {
        if !self.mRawPtr.is_null() {
            (*self.mRawPtr).release();
            self.mRawPtr = ptr::null_mut();
        }
    }

    /// Replace a `structs::RefPtr<T>` with a `RefPtr<T>`,
    /// consuming the `RefPtr<T>`, and releasing the old
    /// value in `self` if necessary.
    ///
    /// `self` must be valid, possibly null.
    pub fn set_move(&mut self, other: RefPtr<T>) {
        if !self.mRawPtr.is_null() {
            unsafe {
                (*self.mRawPtr).release();
            }
        }
        *self = other.forget();
    }
}

impl<T> structs::RefPtr<T> {
    /// Sets the contents to an `Arc<T>`, releasing the old value in `self` if
    /// necessary.
    pub fn set_arc<U>(&mut self, other: Arc<U>)
    where
        U: HasArcFFI<FFIType = T>,
    {
        unsafe {
            U::release_opt(self.mRawPtr.as_ref());
        }
        self.set_arc_leaky(other);
    }

    /// Sets the contents to an Arc<T>
    /// will leak existing contents
    pub fn set_arc_leaky<U>(&mut self, other: Arc<U>)
    where
        U: HasArcFFI<FFIType = T>,
    {
        *self = unsafe { mem::transmute(Arc::into_raw_offset(other)) };
    }
}

impl<T: RefCounted> Drop for RefPtr<T> {
    fn drop(&mut self) {
        unsafe { self.release() }
    }
}

impl<T: RefCounted> Clone for RefPtr<T> {
    fn clone(&self) -> Self {
        self.addref();
        RefPtr {
            ptr: self.ptr,
            _marker: PhantomData,
        }
    }
}

impl<T: RefCounted> PartialEq for RefPtr<T> {
    fn eq(&self, other: &Self) -> bool {
        self.ptr == other.ptr
    }
}

unsafe impl<T: ThreadSafeRefCounted> Send for RefPtr<T> {}
unsafe impl<T: ThreadSafeRefCounted> Sync for RefPtr<T> {}

macro_rules! impl_refcount {
    ($t:ty, $addref:path, $release:path) => {
        unsafe impl RefCounted for $t {
            #[inline]
            fn addref(&self) {
                unsafe { $addref(self as *const _ as *mut _) }
            }

            #[inline]
            unsafe fn release(&self) {
                $release(self as *const _ as *mut _)
            }
        }
    };
}

// Companion of NS_DECL_THREADSAFE_FFI_REFCOUNTING.
//
// Gets you a free RefCounted impl implemented via FFI.
macro_rules! impl_threadsafe_refcount {
    ($t:ty, $addref:path, $release:path) => {
        impl_refcount!($t, $addref, $release);
        unsafe impl ThreadSafeRefCounted for $t {}
    };
}

impl_threadsafe_refcount!(
    structs::mozilla::URLExtraData,
    bindings::Gecko_AddRefURLExtraDataArbitraryThread,
    bindings::Gecko_ReleaseURLExtraDataArbitraryThread
);
impl_threadsafe_refcount!(
    structs::nsIURI,
    bindings::Gecko_AddRefnsIURIArbitraryThread,
    bindings::Gecko_ReleasensIURIArbitraryThread
);
impl_threadsafe_refcount!(
    structs::SharedFontList,
    bindings::Gecko_AddRefSharedFontListArbitraryThread,
    bindings::Gecko_ReleaseSharedFontListArbitraryThread
);
impl_threadsafe_refcount!(
    structs::SheetLoadDataHolder,
    bindings::Gecko_AddRefSheetLoadDataHolderArbitraryThread,
    bindings::Gecko_ReleaseSheetLoadDataHolderArbitraryThread
);

#[inline]
unsafe fn addref_atom(atom: *mut structs::nsAtom) {
    mem::forget(Atom::from_raw(atom));
}

#[inline]
unsafe fn release_atom(atom: *mut structs::nsAtom) {
    let _ = Atom::from_addrefed(atom);
}
impl_threadsafe_refcount!(structs::nsAtom, addref_atom, release_atom);
<|endoftext|>


<commit_before>use public::*;
use checker::Checker;
use checker::checker_type::Type::*;
use checker::operators;
use checker::operators::*;
use std::str::FromStr;

pub struct SourceChecker<'sch, 'ch: 'sch, 'c: 'ch> {
    checker: &'sch Checker<'ch, 'c>,
    source: SourceIndex,
    source_data: &'sch SourceData<'c>,
}

pub fn check(checker: &Checker, source: SourceIndex) -> Type {
    checker.compiler.with_source(source, |source_data| {
        let checker = SourceChecker::new(checker, source, source_data);
        checker.check()
    })
}

impl<'sch, 'ch: 'sch, 'c: 'ch> SourceChecker<'sch, 'ch, 'c> {
    fn new(checker: &'sch Checker<'ch, 'c>, source: SourceIndex, source_data: &'sch SourceData<'c>) -> Self {
        SourceChecker { checker, source, source_data }
    }

    fn check(&self) -> Type {
        let (mut index, mut value, mut last_precedence) = self.evaluate_one(0, Precedence::Other);
        while index < self.source_data.num_tokens() {
            let token = self.source_data.token(index);
            match token.token_type {
                Postfix => {
                    let operator = operators::postfix(&token.string);
                    value = operator.evaluate(self, index, last_precedence, value);
                    last_precedence = operator.precedence();
                    index += 1;
                },
                Infix => {
                    let operator = operators::infix(&token.string);
                    let (next_index, right_operand, next_precedence) = self.evaluate_one(index+1, operator.precedence());
                    value = operator.evaluate(self, index, last_precedence, value, right_operand);
                    last_precedence = next_precedence;
                    index = next_index;
                },
                _ => unreachable!(),
            }
        }
        value
    }

    fn evaluate_one(&self, index: TokenIndex, last_precedence: Precedence) -> (TokenIndex, Type, Precedence) {
        if index >= self.source_data.num_tokens() {
            return (index, Type::Nothing, last_precedence);
        }

        let token = self.source_data.token(index);
        match token.token_type {
            Term(ref term_type) => {
                let value = self.evaluate_term(term_type, &token.string);
                (index+1, value, last_precedence)
            },
            Prefix => {
                let operator = operators::prefix(&token.string);
                let (next_index, right_operand, last_precedence) = self.evaluate_one(index+1, operator.precedence());
                let value = operator.evaluate(self, index, right_operand);
                (next_index, value, last_precedence)
            },
            _ => unreachable!(),
        }
    }

    fn evaluate_term(&self, term_type: &TermType, string: &str) -> Type {
        match *term_type {
            IntegerLiteral => Type::Rational(BigRational::from_str(string).unwrap()),
        }
    }

    pub fn report_at_token(&self, error_type: CompileErrorType, token: TokenIndex) -> Type {
        let start = self.source_data.token_start(token);
        let string = &self.source_data.token(token).string;
        self.checker.compiler.report_at(error_type, self.source, start, string);
        Error
    }
}

<commit_msg>Remove unused file<commit_after><|endoftext|>


<commit_before>extern crate env_logger;
extern crate hyper;
extern crate tools;

use std::collections::HashMap;
use std::env;
use std::fs::{File, OpenOptions};
use std::io::BufReader;
use std::io::{Read, Write};
use std::path::Path;
use std::process::Command;
use std::sync::Mutex;

use hyper::header::{ContentType, Location};
use hyper::method::Method;
use hyper::mime::Mime;
use hyper::server::{Request, Response};
use hyper::status::StatusCode;
use hyper::uri;

use tools::blame;
use tools::config;
use tools::file_format::identifiers::IdentMap;
use tools::format;
use tools::git_ops;

struct WebRequest {
    path: String,
}

struct WebResponse {
    status: StatusCode,
    content_type: String,
    redirect_location: Option<String>,
    output: String,
}

impl Default for WebResponse {
    fn default() -> WebResponse {
        WebResponse {
            status: StatusCode::Ok,
            content_type: "text/plain".to_owned(),
            redirect_location: None,
            output: String::new(),
        }
    }
}

impl WebResponse {
    fn html(body: String) -> WebResponse {
        WebResponse {
            content_type: "text/html".to_owned(),
            output: body,
            .. WebResponse::default()
        }
    }

    fn json(body: String) -> WebResponse {
        WebResponse {
            content_type: "application/json".to_owned(),
            output: body,
            .. WebResponse::default()
        }
    }

    fn internal_error(body: String) -> WebResponse {
        WebResponse {
            status: StatusCode::InternalServerError,
            output: body,
            .. WebResponse::default()
        }
    }

    fn not_found() -> WebResponse {
        WebResponse {
            status: StatusCode::NotFound,
            output: "Not found".to_owned(),
            .. WebResponse::default()
        }
    }

    fn redirect(url: String) -> WebResponse {
        WebResponse {
            status: StatusCode::MovedPermanently,
            redirect_location: Some(url),
            .. WebResponse::default()
        }
    }
}

fn handle_static(path: String, content_type: Option<&str>) -> WebResponse {
    let source_file = match File::open(&path) {
        Ok(f) => f,
        Err(_) => {
            return WebResponse::not_found();
        }
    };
    let mut reader = BufReader::new(&source_file);
    let mut input = String::new();
    match reader.read_to_string(&mut input) {
        Ok(_) => {}
        Err(_) => {
            return WebResponse::not_found();
        }
    }

    let inferred_content_type = match Path::new(&path).extension() {
        Some(ext) => match ext.to_str().unwrap() {
            "css" => "text/css",
            "js" => "text/javascript",
            _ => "text/html",
        },
        None => "text/html",
    };
    let content_type = match content_type {
        Some(ct) => ct,
        None => inferred_content_type,
    };

    WebResponse {
        content_type: content_type.to_owned(),
        output: input,
        .. WebResponse::default()
    }
}

fn handle(
    cfg: &config::Config,
    ident_map: &HashMap<String, IdentMap>,
    req: WebRequest,
) -> WebResponse {
    let path = req.path.clone();
    let path = path[1..].split('/').collect::<Vec<_>>();

    if path.len() > 0 && path[0] == "static" {
        let path = cfg.mozsearch_path.clone() + &req.path;
        return handle_static(path, None);
    }

    if path.len() < 2 {
        return WebResponse::not_found();
    }

    let tree_name = &path[0];
    let kind = &path[1];

    println!("DBG {:?} {} {}", path, tree_name, kind);

    match &kind[..] {
        "rev" => {
            if path.len() < 3 {
                return WebResponse::not_found();
            }

            let rev = &path[2];
            let path = path.clone().split_off(3);
            let path = path.join("/");

            let mut writer = Vec::new();
            match format::format_path(cfg, &tree_name, &rev, &path, &mut writer) {
                Ok(()) => WebResponse::html(String::from_utf8(writer).unwrap()),
                Err(err) => WebResponse::internal_error(err.to_owned()),
            }
        }

        "hgrev" => {
            let tree_config = &cfg.trees[*tree_name];
            let git_path = match config::get_git_path(tree_config) {
                Ok(git_path) => git_path,
                Err(_) => return WebResponse::not_found(),
            };

            let hg_rev = path[2];
            let output_result = Command::new("git")
                .arg("cinnabar")
                .arg("hg2git")
                .arg(hg_rev)
                .current_dir(&git_path)
                .output();
            match output_result {
                Ok(output) if output.status.success() => {
                    WebResponse::redirect(format!("/{}/rev/{}/{}",
                        tree_name,
                        git_ops::decode_bytes(output.stdout).trim(),
                        path[3..].join("/"))
                    )
                }
                Ok(_) => WebResponse::not_found(),
                Err(err) => WebResponse::internal_error(format!("{:?}", err)),
            }
        }

        "source" => {
            let path = path.clone().split_off(2);
            let path = path.join("/");

            let tree_config = &cfg.trees[*tree_name];

            let path = format!("{}/file/{}", tree_config.paths.index_path, path);
            return handle_static(path, Some("text/html"));
        }

        "diff" => {
            if path.len() < 3 {
                return WebResponse::not_found();
            }

            let rev = &path[2];
            let path = path.clone().split_off(3);
            let path = path.join("/");

            let mut writer = Vec::new();
            match format::format_diff(cfg, &tree_name, &rev, &path, &mut writer) {
                Ok(()) => WebResponse::html(String::from_utf8(writer).unwrap()),
                Err(err) => WebResponse::internal_error(err.to_owned()),
            }
        }

        "commit" => {
            if path.len() < 3 {
                return WebResponse::not_found();
            }

            let rev = &path[2];

            let mut writer = Vec::new();
            match format::format_commit(cfg, &tree_name, &rev, &mut writer) {
                Ok(()) => WebResponse::html(String::from_utf8(writer).unwrap()),
                Err(err) => WebResponse::internal_error(err.to_owned()),
            }
        }

        "commit-info" => {
            if path.len() < 3 {
                return WebResponse::not_found();
            }

            let rev = &path[2];
            match blame::get_commit_info(&cfg, tree_name, rev) {
                Ok(json) => WebResponse::json(json),
                Err(err) => WebResponse::internal_error(err.to_owned()),
            }
        }

        "complete" => {
            let ids = ident_map.get(&tree_name.to_string()).unwrap();
            let json = ids.lookup_json(&path[2], false, false, 6);
            WebResponse::json(json)
        }

        _ => WebResponse::not_found(),
    }
}

fn main() {
    env_logger::init();

    let cfg = config::load(&env::args().nth(1).unwrap(), true);

    // Dump config memory usage
    println!("{}", cfg.describe_mem_usage());

    let ident_map = IdentMap::load(&cfg);

    let internal_data = Mutex::new((cfg, ident_map));

    let handler = move |req: Request, mut res: Response| {
        if req.method != Method::Get {
            *res.status_mut() = StatusCode::MethodNotAllowed;
            let resp = format!("Invalid method").into_bytes();
            res.send(&resp).unwrap();
            return;
        }

        let path = match req.uri {
            uri::RequestUri::AbsolutePath(path) => path,
            uri::RequestUri::AbsoluteUri(url) => url.path().to_owned(),
            _ => panic!("Unexpected URI"),
        };

        let guard = match internal_data.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };
        let (ref cfg, ref ident_map) = *guard;

        let response = handle(&cfg, &ident_map, WebRequest { path: path });

        *res.status_mut() = response.status;
        let output = response.output.into_bytes();
        let mime: Mime = response.content_type.parse().unwrap();
        res.headers_mut().set(ContentType(mime));
        if let Some(loc) = response.redirect_location {
            res.headers_mut().set(Location(loc));
        }
        res.send(&output).unwrap();
    };

    {
        // We *append* to the status file because other server components
        // also write to this file when they are done starting up, and we
        // don't want to clobber those messages.
        let mut status_out = OpenOptions::new()
            .append(true)
            .create(true)
            .open(&env::args().nth(2).unwrap())
            .unwrap();
        writeln!(status_out, "web-server.rs loaded").unwrap();
    }

    println!("On 8001");
    let _listening = hyper::Server::http("0.0.0.0:8001").unwrap().handle(handler);
}
<commit_msg>web-server: handle clients cutting the connection early more gracefully.<commit_after>extern crate env_logger;
extern crate hyper;
extern crate tools;

use std::collections::HashMap;
use std::env;
use std::fs::{File, OpenOptions};
use std::io::BufReader;
use std::io::{Read, Write};
use std::path::Path;
use std::process::Command;
use std::sync::Mutex;

use hyper::header::{ContentType, Location};
use hyper::method::Method;
use hyper::mime::Mime;
use hyper::server::{Request, Response};
use hyper::status::StatusCode;
use hyper::uri;

use tools::blame;
use tools::config;
use tools::file_format::identifiers::IdentMap;
use tools::format;
use tools::git_ops;

struct WebRequest<'a> {
    path: &'a str,
}

struct WebResponse {
    status: StatusCode,
    content_type: String,
    redirect_location: Option<String>,
    output: String,
}

impl Default for WebResponse {
    fn default() -> WebResponse {
        WebResponse {
            status: StatusCode::Ok,
            content_type: "text/plain".to_owned(),
            redirect_location: None,
            output: String::new(),
        }
    }
}

impl WebResponse {
    fn html(body: String) -> WebResponse {
        WebResponse {
            content_type: "text/html".to_owned(),
            output: body,
            .. WebResponse::default()
        }
    }

    fn json(body: String) -> WebResponse {
        WebResponse {
            content_type: "application/json".to_owned(),
            output: body,
            .. WebResponse::default()
        }
    }

    fn internal_error(body: String) -> WebResponse {
        WebResponse {
            status: StatusCode::InternalServerError,
            output: body,
            .. WebResponse::default()
        }
    }

    fn not_found() -> WebResponse {
        WebResponse {
            status: StatusCode::NotFound,
            output: "Not found".to_owned(),
            .. WebResponse::default()
        }
    }

    fn redirect(url: String) -> WebResponse {
        WebResponse {
            status: StatusCode::MovedPermanently,
            redirect_location: Some(url),
            .. WebResponse::default()
        }
    }
}

fn handle_static(path: String, content_type: Option<&str>) -> WebResponse {
    let source_file = match File::open(&path) {
        Ok(f) => f,
        Err(_) => {
            return WebResponse::not_found();
        }
    };
    let mut reader = BufReader::new(&source_file);
    let mut input = String::new();
    match reader.read_to_string(&mut input) {
        Ok(_) => {}
        Err(_) => {
            return WebResponse::not_found();
        }
    }

    let inferred_content_type = match Path::new(&path).extension() {
        Some(ext) => match ext.to_str().unwrap() {
            "css" => "text/css",
            "js" => "text/javascript",
            _ => "text/html",
        },
        None => "text/html",
    };
    let content_type = match content_type {
        Some(ct) => ct,
        None => inferred_content_type,
    };

    WebResponse {
        content_type: content_type.to_owned(),
        output: input,
        .. WebResponse::default()
    }
}

fn handle(
    cfg: &config::Config,
    ident_map: &HashMap<String, IdentMap>,
    req: WebRequest,
) -> WebResponse {
    let path = req.path.to_owned();
    let path = path[1..].split('/').collect::<Vec<_>>();

    if path.len() > 0 && path[0] == "static" {
        let path = cfg.mozsearch_path.clone() + &req.path;
        return handle_static(path, None);
    }

    if path.len() < 2 {
        return WebResponse::not_found();
    }

    let tree_name = &path[0];
    let kind = &path[1];

    println!("DBG {:?} {} {}", path, tree_name, kind);

    match &kind[..] {
        "rev" => {
            if path.len() < 3 {
                return WebResponse::not_found();
            }

            let rev = &path[2];
            let path = path.clone().split_off(3);
            let path = path.join("/");

            let mut writer = Vec::new();
            match format::format_path(cfg, &tree_name, &rev, &path, &mut writer) {
                Ok(()) => WebResponse::html(String::from_utf8(writer).unwrap()),
                Err(err) => WebResponse::internal_error(err.to_owned()),
            }
        }

        "hgrev" => {
            let tree_config = &cfg.trees[*tree_name];
            let git_path = match config::get_git_path(tree_config) {
                Ok(git_path) => git_path,
                Err(_) => return WebResponse::not_found(),
            };

            let hg_rev = path[2];
            let output_result = Command::new("git")
                .arg("cinnabar")
                .arg("hg2git")
                .arg(hg_rev)
                .current_dir(&git_path)
                .output();
            match output_result {
                Ok(output) if output.status.success() => {
                    WebResponse::redirect(format!("/{}/rev/{}/{}",
                        tree_name,
                        git_ops::decode_bytes(output.stdout).trim(),
                        path[3..].join("/"))
                    )
                }
                Ok(_) => WebResponse::not_found(),
                Err(err) => WebResponse::internal_error(format!("{:?}", err)),
            }
        }

        "source" => {
            let path = path.clone().split_off(2);
            let path = path.join("/");

            let tree_config = &cfg.trees[*tree_name];

            let path = format!("{}/file/{}", tree_config.paths.index_path, path);
            return handle_static(path, Some("text/html"));
        }

        "diff" => {
            if path.len() < 3 {
                return WebResponse::not_found();
            }

            let rev = &path[2];
            let path = path.clone().split_off(3);
            let path = path.join("/");

            let mut writer = Vec::new();
            match format::format_diff(cfg, &tree_name, &rev, &path, &mut writer) {
                Ok(()) => WebResponse::html(String::from_utf8(writer).unwrap()),
                Err(err) => WebResponse::internal_error(err.to_owned()),
            }
        }

        "commit" => {
            if path.len() < 3 {
                return WebResponse::not_found();
            }

            let rev = &path[2];

            let mut writer = Vec::new();
            match format::format_commit(cfg, &tree_name, &rev, &mut writer) {
                Ok(()) => WebResponse::html(String::from_utf8(writer).unwrap()),
                Err(err) => WebResponse::internal_error(err.to_owned()),
            }
        }

        "commit-info" => {
            if path.len() < 3 {
                return WebResponse::not_found();
            }

            let rev = &path[2];
            match blame::get_commit_info(&cfg, tree_name, rev) {
                Ok(json) => WebResponse::json(json),
                Err(err) => WebResponse::internal_error(err.to_owned()),
            }
        }

        "complete" => {
            let ids = ident_map.get(&tree_name.to_string()).unwrap();
            let json = ids.lookup_json(&path[2], false, false, 6);
            WebResponse::json(json)
        }

        _ => WebResponse::not_found(),
    }
}

fn main() {
    env_logger::init();

    let cfg = config::load(&env::args().nth(1).unwrap(), true);

    // Dump config memory usage
    println!("{}", cfg.describe_mem_usage());

    let ident_map = IdentMap::load(&cfg);

    let internal_data = Mutex::new((cfg, ident_map));

    let handler = move |req: Request, mut res: Response| {
        if req.method != Method::Get {
            *res.status_mut() = StatusCode::MethodNotAllowed;
            let resp = format!("Invalid method").into_bytes();
            if let Err(e) = res.send(&resp) {
                eprintln!("Error when replying to {}: {:?}", req.uri, e);
            }
            return;
        }

        let path = match req.uri {
            uri::RequestUri::AbsolutePath(path) => path,
            uri::RequestUri::AbsoluteUri(url) => url.path().to_owned(),
            _ => panic!("Unexpected URI"),
        };

        let guard = match internal_data.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };
        let (ref cfg, ref ident_map) = *guard;

        let response = handle(&cfg, &ident_map, WebRequest { path: &path });

        *res.status_mut() = response.status;
        let output = response.output.into_bytes();
        let mime: Mime = response.content_type.parse().unwrap();
        res.headers_mut().set(ContentType(mime));
        if let Some(loc) = response.redirect_location {
            res.headers_mut().set(Location(loc));
        }
        if let Err(e) = res.send(&output) {
            eprintln!("Error when replying to {}: {:?}", path, e);
        }
    };

    {
        // We *append* to the status file because other server components
        // also write to this file when they are done starting up, and we
        // don't want to clobber those messages.
        let mut status_out = OpenOptions::new()
            .append(true)
            .create(true)
            .open(&env::args().nth(2).unwrap())
            .unwrap();
        writeln!(status_out, "web-server.rs loaded").unwrap();
    }

    println!("On 8001");
    let _listening = hyper::Server::http("0.0.0.0:8001").unwrap().handle(handler);
}
<|endoftext|>


<commit_before>#[cfg(test)]
mod tests;

pub mod response;

pub use client::response::*;
use std::io::Read;
use message::Message;
use hyper::client::Client as HttpClient;
use hyper::header::{Authorization, ContentType, Connection};
use hyper::status::StatusCode;
use rustc_serialize::json::{self, ToJson};

pub struct Client<'a> {
    http_client: HttpClient,
    fcm_uri: &'a str,
}

impl<'a> Client<'a> {
    /// Get a new instance of Client.
    pub fn new() -> Client<'a> {
        Client {
            http_client: HttpClient::new(),
            fcm_uri: "https://fcm.googleapis.com/fcm/send",
        }
    }

    /// Send a message using your FCM API Key.
    /// # Examples:
    /// ```no_run
    /// use fcm::{MessageBuilder, Client};
    /// use std::collections::HashMap;
    ///
    /// let mut map = HashMap::new();
    /// map.insert("message", "Howdy!");
    ///
    /// let message = MessageBuilder::new("<registration id>").data(map).finalize();
    /// let client = Client::new();
    /// let result = client.send(message, "<FCM API Key>");
    /// ```
    pub fn send(&self, message: Message, api_key: &str) -> Result<FcmResponse, FcmError> {
        let payload = message.to_json().to_string();
        let auth_header = format!("key={}", api_key);

        let response = self.http_client.
            post(self.fcm_uri).
            header(Connection::keep_alive()).
            header(ContentType::json()).
            body(&payload).
            header(Authorization(auth_header)).
            header(ContentType::json()).
            send();

        match response {
            Ok(mut response) => {
                let mut body = String::new();
                response.read_to_string(&mut body).unwrap();

                Client::parse_response(response.status, &body)
            },
            Err(_) => {
                Client::parse_response(StatusCode::InternalServerError, "Server Error")
            }
        }
    }

    /// Set fcm uri if need to connect to other uri than default.
    pub fn fcm_uri(&mut self, fcm_uri: &'a str) -> &mut Client<'a> {
        self.fcm_uri = fcm_uri;
        self
    }

    fn parse_response(status: StatusCode, body: &str) -> Result<response::FcmResponse, response::FcmError> {
        use hyper::status::StatusCode::*;
        match status {
            Ok =>
                Result::Ok(json::decode(body).unwrap()),
            Unauthorized =>
                Err(response::FcmError::Unauthorized),
            BadRequest =>
                Err(response::FcmError::InvalidMessage(body.to_string())),
            status if status.is_server_error() =>
                Err(response::FcmError::ServerError),
            _ =>
                Err(response::FcmError::InvalidMessage("Unknown Error".to_string())),
        }
    }
}
<commit_msg>Allow setting fcm uri from a enviroment variable<commit_after>#[cfg(test)]
mod tests;

pub mod response;

pub use client::response::*;
use std::io::Read;
use message::Message;
use hyper::client::Client as HttpClient;
use hyper::header::{Authorization, ContentType, Connection};
use hyper::status::StatusCode;
use rustc_serialize::json::{self, ToJson};
use std::env;

pub struct Client {
    http_client: HttpClient,
    fcm_uri: String,
}

impl Client {
    /// Get a new instance of Client.
    pub fn new() -> Client {
        let fcm_uri = match env::var("FCM_URI") {
            Ok(val) => String::from(val),
            Err(_) => String::from("https://fcm.googleapis.com/fcm/send")
        };

        Client {
            http_client: HttpClient::new(),
            fcm_uri: fcm_uri,
        }
    }

    /// Send a message using your FCM API Key.
    /// # Examples:
    /// ```no_run
    /// use fcm::{MessageBuilder, Client};
    /// use std::collections::HashMap;
    ///
    /// let mut map = HashMap::new();
    /// map.insert("message", "Howdy!");
    ///
    /// let message = MessageBuilder::new("<registration id>").data(map).finalize();
    /// let client = Client::new();
    /// let result = client.send(message, "<FCM API Key>");
    /// ```
    pub fn send(&self, message: Message, api_key: &str) -> Result<FcmResponse, FcmError> {
        let payload = message.to_json().to_string();
        let auth_header = format!("key={}", api_key);

        let response = self.http_client.
            post(&self.fcm_uri).
            header(Connection::keep_alive()).
            header(ContentType::json()).
            body(&payload).
            header(Authorization(auth_header)).
            header(ContentType::json()).
            send();

        match response {
            Ok(mut response) => {
                let mut body = String::new();
                response.read_to_string(&mut body).unwrap();

                Client::parse_response(response.status, &body)
            },
            Err(_) => {
                Client::parse_response(StatusCode::InternalServerError, "Server Error")
            }
        }
    }

    fn parse_response(status: StatusCode, body: &str) -> Result<response::FcmResponse, response::FcmError> {
        use hyper::status::StatusCode::*;
        match status {
            Ok =>
                Result::Ok(json::decode(body).unwrap()),
            Unauthorized =>
                Err(response::FcmError::Unauthorized),
            BadRequest =>
                Err(response::FcmError::InvalidMessage(body.to_string())),
            status if status.is_server_error() =>
                Err(response::FcmError::ServerError),
            _ =>
                Err(response::FcmError::InvalidMessage("Unknown Error".to_string())),
        }
    }
}
<|endoftext|>