<commit_before>use ra_syntax::{
    AstNode, SourceFile, SyntaxKind::*,
    SyntaxNode, TextUnit, TextRange,
    algo::{find_node_at_offset, find_leaf_at_offset, LeafAtOffset},
    ast::{self, AstToken},
};

use crate::{LocalEdit, TextEditBuilder, formatting::leading_indent};

pub fn on_enter(file: &SourceFile, offset: TextUnit) -> Option<LocalEdit> {
    let comment = find_leaf_at_offset(file.syntax(), offset)
        .left_biased()
        .and_then(ast::Comment::cast)?;

    if let ast::CommentFlavor::Multiline = comment.flavor() {
        return None;
    }

    let prefix = comment.prefix();
    if offset < comment.syntax().range().start() + TextUnit::of_str(prefix) + TextUnit::from(1) {
        return None;
    }

    let indent = node_indent(file, comment.syntax())?;
    let inserted = format!("\n{}{} ", indent, prefix);
    let cursor_position = offset + TextUnit::of_str(&inserted);
    let mut edit = TextEditBuilder::default();
    edit.insert(offset, inserted);
    Some(LocalEdit {
        label: "on enter".to_string(),
        edit: edit.finish(),
        cursor_position: Some(cursor_position),
    })
}

fn node_indent<'a>(file: &'a SourceFile, node: &SyntaxNode) -> Option<&'a str> {
    let ws = match find_leaf_at_offset(file.syntax(), node.range().start()) {
        LeafAtOffset::Between(l, r) => {
            assert!(r == node);
            l
        }
        LeafAtOffset::Single(n) => {
            assert!(n == node);
            return Some("");
        }
        LeafAtOffset::None => unreachable!(),
    };
    if ws.kind() != WHITESPACE {
        return None;
    }
    let text = ws.leaf_text().unwrap();
    let pos = text.as_str().rfind('\n').map(|it| it + 1).unwrap_or(0);
    Some(&text[pos..])
}

pub fn on_eq_typed(file: &SourceFile, eq_offset: TextUnit) -> Option<LocalEdit> {
    assert_eq!(file.syntax().text().char_at(eq_offset), Some('='));
    let let_stmt: &ast::LetStmt = find_node_at_offset(file.syntax(), eq_offset)?;
    if let_stmt.has_semi() {
        return None;
    }
    if let Some(expr) = let_stmt.initializer() {
        let expr_range = expr.syntax().range();
        if expr_range.contains(eq_offset) && eq_offset != expr_range.start() {
            return None;
        }
        if file
            .syntax()
            .text()
            .slice(eq_offset..expr_range.start())
            .contains('\n')
        {
            return None;
        }
    } else {
        return None;
    }
    let offset = let_stmt.syntax().range().end();
    let mut edit = TextEditBuilder::default();
    edit.insert(offset, ";".to_string());
    Some(LocalEdit {
        label: "add semicolon".to_string(),
        edit: edit.finish(),
        cursor_position: None,
    })
}

pub fn on_dot_typed(file: &SourceFile, dot_offset: TextUnit) -> Option<LocalEdit> {
    assert_eq!(file.syntax().text().char_at(dot_offset), Some('.'));

    let whitespace = find_leaf_at_offset(file.syntax(), dot_offset)
        .left_biased()
        .and_then(ast::Whitespace::cast)?;

    let current_indent = {
        let text = whitespace.text();
        let newline = text.rfind('\n')?;
        &text[newline + 1..]
    };
    let current_indent_len = TextUnit::of_str(current_indent);

    // Make sure dot is a part of call chain
    let field_expr = whitespace
        .syntax()
        .parent()
        .and_then(ast::FieldExpr::cast)?;
    let prev_indent = leading_indent(field_expr.syntax())?;
    let target_indent = format!("    {}", prev_indent);
    let target_indent_len = TextUnit::of_str(&target_indent);
    if current_indent_len == target_indent_len {
        return None;
    }
    let mut edit = TextEditBuilder::default();
    edit.replace(
        TextRange::from_to(dot_offset - current_indent_len, dot_offset),
        target_indent.into(),
    );
    let res = LocalEdit {
        label: "reindent dot".to_string(),
        edit: edit.finish(),
        cursor_position: Some(
            dot_offset + target_indent_len - current_indent_len + TextUnit::of_char('.'),
        ),
    };
    Some(res)
}

#[cfg(test)]
mod tests {
    use crate::test_utils::{add_cursor, assert_eq_text, extract_offset};

    use super::*;

    #[test]
    fn test_on_eq_typed() {
        fn do_check(before: &str, after: &str) {
            let (offset, before) = extract_offset(before);
            let file = SourceFile::parse(&before);
            let result = on_eq_typed(&file, offset).unwrap();
            let actual = result.edit.apply(&before);
            assert_eq_text!(after, &actual);
        }

        //     do_check(r"
        // fn foo() {
        //     let foo =<|>
        // }
        // ", r"
        // fn foo() {
        //     let foo =;
        // }
        // ");
        do_check(
            r"
fn foo() {
    let foo <|>= 1 + 1
}
",
            r"
fn foo() {
    let foo = 1 + 1;
}
",
        );
        //     do_check(r"
        // fn foo() {
        //     let foo =<|>
        //     let bar = 1;
        // }
        // ", r"
        // fn foo() {
        //     let foo =;
        //     let bar = 1;
        // }
        // ");
    }

    #[test]
    fn test_on_dot_typed() {
        fn do_check(before: &str, after: &str) {
            let (offset, before) = extract_offset(before);
            let file = SourceFile::parse(&before);
            if let Some(result) = on_dot_typed(&file, offset) {
                let actual = result.edit.apply(&before);
                assert_eq_text!(after, &actual);
            } else {
                assert_eq_text!(&before, after)
            };
        }
        // indent if continuing chain call
        do_check(
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                <|>.
            }
            ",
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .
            }
            ",
        );

        // do not indent if already indented
        do_check(
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    <|>.
            }
            ",
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .
            }
            ",
        );

        // indent if the previous line is already indented
        do_check(
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .first()
                <|>.
            }
            ",
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .first()
                    .
            }
            ",
        );

        // don't indent if indent matches previous line
        do_check(
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .first()
                    <|>.
            }
            ",
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .first()
                    .
            }
            ",
        );

        // don't indent if there is no method call on previous line
        do_check(
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                <|>.
            }
            ",
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                .
            }
            ",
        );

        // indent to match previous expr
        do_check(
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
            <|>.
                }
            ",
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .
            }
            ",
        );
    }

    #[test]
    fn test_on_enter() {
        fn apply_on_enter(before: &str) -> Option<String> {
            let (offset, before) = extract_offset(before);
            let file = SourceFile::parse(&before);
            let result = on_enter(&file, offset)?;
            let actual = result.edit.apply(&before);
            let actual = add_cursor(&actual, result.cursor_position.unwrap());
            Some(actual)
        }

        fn do_check(before: &str, after: &str) {
            let actual = apply_on_enter(before).unwrap();
            assert_eq_text!(after, &actual);
        }

        fn do_check_noop(text: &str) {
            assert!(apply_on_enter(text).is_none())
        }

        do_check(
            r"
/// Some docs<|>
fn foo() {
}
",
            r"
/// Some docs
/// <|>
fn foo() {
}
",
        );
        do_check(
            r"
impl S {
    /// Some<|> docs.
    fn foo() {}
}
",
            r"
impl S {
    /// Some
    /// <|> docs.
    fn foo() {}
}
",
        );
        do_check_noop(r"<|>//! docz");
    }
}
<commit_msg>fine grained on typed tests<commit_after>use ra_syntax::{
    AstNode, SourceFile, SyntaxKind::*,
    SyntaxNode, TextUnit, TextRange,
    algo::{find_node_at_offset, find_leaf_at_offset, LeafAtOffset},
    ast::{self, AstToken},
};

use crate::{LocalEdit, TextEditBuilder, formatting::leading_indent};

pub fn on_enter(file: &SourceFile, offset: TextUnit) -> Option<LocalEdit> {
    let comment = find_leaf_at_offset(file.syntax(), offset)
        .left_biased()
        .and_then(ast::Comment::cast)?;

    if let ast::CommentFlavor::Multiline = comment.flavor() {
        return None;
    }

    let prefix = comment.prefix();
    if offset < comment.syntax().range().start() + TextUnit::of_str(prefix) + TextUnit::from(1) {
        return None;
    }

    let indent = node_indent(file, comment.syntax())?;
    let inserted = format!("\n{}{} ", indent, prefix);
    let cursor_position = offset + TextUnit::of_str(&inserted);
    let mut edit = TextEditBuilder::default();
    edit.insert(offset, inserted);
    Some(LocalEdit {
        label: "on enter".to_string(),
        edit: edit.finish(),
        cursor_position: Some(cursor_position),
    })
}

fn node_indent<'a>(file: &'a SourceFile, node: &SyntaxNode) -> Option<&'a str> {
    let ws = match find_leaf_at_offset(file.syntax(), node.range().start()) {
        LeafAtOffset::Between(l, r) => {
            assert!(r == node);
            l
        }
        LeafAtOffset::Single(n) => {
            assert!(n == node);
            return Some("");
        }
        LeafAtOffset::None => unreachable!(),
    };
    if ws.kind() != WHITESPACE {
        return None;
    }
    let text = ws.leaf_text().unwrap();
    let pos = text.as_str().rfind('\n').map(|it| it + 1).unwrap_or(0);
    Some(&text[pos..])
}

pub fn on_eq_typed(file: &SourceFile, eq_offset: TextUnit) -> Option<LocalEdit> {
    assert_eq!(file.syntax().text().char_at(eq_offset), Some('='));
    let let_stmt: &ast::LetStmt = find_node_at_offset(file.syntax(), eq_offset)?;
    if let_stmt.has_semi() {
        return None;
    }
    if let Some(expr) = let_stmt.initializer() {
        let expr_range = expr.syntax().range();
        if expr_range.contains(eq_offset) && eq_offset != expr_range.start() {
            return None;
        }
        if file
            .syntax()
            .text()
            .slice(eq_offset..expr_range.start())
            .contains('\n')
        {
            return None;
        }
    } else {
        return None;
    }
    let offset = let_stmt.syntax().range().end();
    let mut edit = TextEditBuilder::default();
    edit.insert(offset, ";".to_string());
    Some(LocalEdit {
        label: "add semicolon".to_string(),
        edit: edit.finish(),
        cursor_position: None,
    })
}

pub fn on_dot_typed(file: &SourceFile, dot_offset: TextUnit) -> Option<LocalEdit> {
    assert_eq!(file.syntax().text().char_at(dot_offset), Some('.'));

    let whitespace = find_leaf_at_offset(file.syntax(), dot_offset)
        .left_biased()
        .and_then(ast::Whitespace::cast)?;

    let current_indent = {
        let text = whitespace.text();
        let newline = text.rfind('\n')?;
        &text[newline + 1..]
    };
    let current_indent_len = TextUnit::of_str(current_indent);

    // Make sure dot is a part of call chain
    let field_expr = whitespace
        .syntax()
        .parent()
        .and_then(ast::FieldExpr::cast)?;
    let prev_indent = leading_indent(field_expr.syntax())?;
    let target_indent = format!("    {}", prev_indent);
    let target_indent_len = TextUnit::of_str(&target_indent);
    if current_indent_len == target_indent_len {
        return None;
    }
    let mut edit = TextEditBuilder::default();
    edit.replace(
        TextRange::from_to(dot_offset - current_indent_len, dot_offset),
        target_indent.into(),
    );
    let res = LocalEdit {
        label: "reindent dot".to_string(),
        edit: edit.finish(),
        cursor_position: Some(
            dot_offset + target_indent_len - current_indent_len + TextUnit::of_char('.'),
        ),
    };
    Some(res)
}

#[cfg(test)]
mod tests {
    use crate::test_utils::{add_cursor, assert_eq_text, extract_offset};

    use super::*;

    #[test]
    fn test_on_eq_typed() {
        fn type_eq(before: &str, after: &str) {
            let (offset, before) = extract_offset(before);
            let mut edit = TextEditBuilder::default();
            edit.insert(offset, "=".to_string());
            let before = edit.finish().apply(&before);
            let file = SourceFile::parse(&before);
            if let Some(result) = on_eq_typed(&file, offset) {
                let actual = result.edit.apply(&before);
                assert_eq_text!(after, &actual);
            } else {
                assert_eq_text!(&before, after)
            };
        }

        //     do_check(r"
        // fn foo() {
        //     let foo =<|>
        // }
        // ", r"
        // fn foo() {
        //     let foo =;
        // }
        // ");
        type_eq(
            r"
fn foo() {
    let foo <|> 1 + 1
}
",
            r"
fn foo() {
    let foo = 1 + 1;
}
",
        );
        //     do_check(r"
        // fn foo() {
        //     let foo =<|>
        //     let bar = 1;
        // }
        // ", r"
        // fn foo() {
        //     let foo =;
        //     let bar = 1;
        // }
        // ");
    }

    fn type_dot(before: &str, after: &str) {
        let (offset, before) = extract_offset(before);
        let mut edit = TextEditBuilder::default();
        edit.insert(offset, ".".to_string());
        let before = edit.finish().apply(&before);
        let file = SourceFile::parse(&before);
        if let Some(result) = on_dot_typed(&file, offset) {
            let actual = result.edit.apply(&before);
            assert_eq_text!(after, &actual);
        } else {
            assert_eq_text!(&before, after)
        };
    }

    #[test]
    fn indents_new_chain_call() {
        type_dot(
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                <|>
            }
            ",
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .
            }
            ",
        );
        type_dot(
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    <|>
            }
            ",
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .
            }
            ",
        )
    }

    #[test]
    fn indents_continued_chain_call() {
        type_dot(
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .first()
                <|>
            }
            ",
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .first()
                    .
            }
            ",
        );
        type_dot(
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .first()
                    <|>
            }
            ",
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .first()
                    .
            }
            ",
        );
    }

    #[test]
    fn dont_indent_freestanding_dot() {
        type_dot(
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                <|>
            }
            ",
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                .
            }
            ",
        );
        type_dot(
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
            <|>
            }
            ",
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
            .
            }
            ",
        );
    }

    #[test]
    fn test_on_enter() {
        fn apply_on_enter(before: &str) -> Option<String> {
            let (offset, before) = extract_offset(before);
            let file = SourceFile::parse(&before);
            let result = on_enter(&file, offset)?;
            let actual = result.edit.apply(&before);
            let actual = add_cursor(&actual, result.cursor_position.unwrap());
            Some(actual)
        }

        fn do_check(before: &str, after: &str) {
            let actual = apply_on_enter(before).unwrap();
            assert_eq_text!(after, &actual);
        }

        fn do_check_noop(text: &str) {
            assert!(apply_on_enter(text).is_none())
        }

        do_check(
            r"
/// Some docs<|>
fn foo() {
}
",
            r"
/// Some docs
/// <|>
fn foo() {
}
",
        );
        do_check(
            r"
impl S {
    /// Some<|> docs.
    fn foo() {}
}
",
            r"
impl S {
    /// Some
    /// <|> docs.
    fn foo() {}
}
",
        );
        do_check_noop(r"<|>//! docz");
    }
}
<|endoftext|>


<commit_before>use libc;

use std::char;
use std::io::{self, Write};
use std::mem;
use std::process;
use std::str;
use std::thread;
use std::time::Duration;

use crate::boots;
use crate::gc::{Address, GcReason};
use crate::handle::{handle_scope, Handle};
use crate::object::{Obj, Ref, Str, UInt8Array};
use crate::stack::stacktrace_from_last_dtn;
use crate::threads::{
    current_thread, deinit_current_thread, init_current_thread, parked_scope, DoraThread,
    ManagedThread, ThreadState, STACK_SIZE,
};
use crate::ty::SourceTypeArray;
use crate::vm::{get_vm, stack_pointer, ManagedCondition, ManagedMutex, Trap};

pub extern "C" fn uint8_to_string(val: u8) -> Ref<Str> {
    handle_scope(|| {
        let buffer = val.to_string();
        let vm = get_vm();

        Str::from_buffer(vm, buffer.as_bytes())
    })
}

pub extern "C" fn char_to_string(val: u32) -> Ref<Str> {
    handle_scope(|| {
        let buffer = unsafe { char::from_u32_unchecked(val) }.to_string();
        let vm = get_vm();

        Str::from_buffer(vm, buffer.as_bytes())
    })
}

pub extern "C" fn int32_to_string(val: i32) -> Ref<Str> {
    handle_scope(|| {
        let buffer = val.to_string();
        let vm = get_vm();

        Str::from_buffer(vm, buffer.as_bytes())
    })
}

pub extern "C" fn int64_to_string(val: i64) -> Ref<Str> {
    handle_scope(|| {
        let buffer = val.to_string();
        let vm = get_vm();

        Str::from_buffer(vm, buffer.as_bytes())
    })
}

pub extern "C" fn float32_to_string(val: f32) -> Ref<Str> {
    handle_scope(|| {
        let buffer = val.to_string();
        let vm = get_vm();

        Str::from_buffer(vm, buffer.as_bytes())
    })
}

pub extern "C" fn float64_to_string(val: f64) -> Ref<Str> {
    handle_scope(|| {
        let buffer = val.to_string();
        let vm = get_vm();

        Str::from_buffer(vm, buffer.as_bytes())
    })
}

pub extern "C" fn print(val: Handle<Str>) {
    io::stdout().write(val.content()).unwrap();
}

pub extern "C" fn fatal_error(msg: Handle<Str>) {
    eprint!("fatal error: ");
    io::stderr().write(msg.content()).unwrap();
    eprintln!("");

    let vm = get_vm();
    let stacktrace = stacktrace_from_last_dtn(vm);
    stacktrace.dump_err(vm);

    process::exit(1);
}

pub extern "C" fn abort() {
    eprintln!("program aborted.");
    process::exit(1);
}

pub extern "C" fn exit(status: i32) {
    process::exit(status);
}

pub extern "C" fn unreachable() {
    eprintln!("unreachable code executed.");
    process::exit(1);
}

pub extern "C" fn timestamp() -> u64 {
    use crate::timer;

    timer::timestamp()
}

pub extern "C" fn println(val: Handle<Str>) {
    let stdout = io::stdout();
    let mut handle = stdout.lock();
    handle.write(val.content()).unwrap();
    handle.write(b"\n").unwrap();
}

pub extern "C" fn sleep(seconds: i32) {
    assert!(seconds >= 0);
    thread::sleep(Duration::from_secs(seconds as u64));
}

pub extern "C" fn call(fct: Handle<Str>) {
    let fct_name = fct.to_cstring();
    let fct_name = fct_name.to_str().unwrap();

    let vm = get_vm();
    let name = vm.interner.intern(fct_name);

    if let Some(fct_id) = vm
        .namespace_table(vm.global_namespace_id)
        .read()
        .get_fct(name)
    {
        {
            let fct = vm.fcts.idx(fct_id);
            let fct = fct.read();

            if !fct.param_types.is_empty() {
                writeln!(&mut io::stderr(), "fct `{}` takes arguments.", fct_name)
                    .expect("could not print to stderr");
                process::exit(1);
            }
        }

        vm.run(fct_id);
    } else {
        writeln!(&mut io::stderr(), "fct `{}` not found.", fct_name)
            .expect("could not print to stderr");
        process::exit(1);
    }
}

pub extern "C" fn bytecode(name: Handle<Str>) -> Ref<Obj> {
    let fct_name = name.to_cstring();
    let fct_name = fct_name.to_str().unwrap();

    let vm = get_vm();

    boots::bytecode(vm, &fct_name)
}

pub extern "C" fn strcmp(lhs: Handle<Str>, rhs: Handle<Str>) -> i32 {
    unsafe {
        libc::strcmp(
            lhs.data() as *const libc::c_char,
            rhs.data() as *const libc::c_char,
        )
    }
}

pub extern "C" fn strcat(lhs: Handle<Str>, rhs: Handle<Str>) -> Ref<Str> {
    handle_scope(|| {
        let vm = get_vm();
        Str::concat(vm, lhs, rhs).direct()
    })
}

pub extern "C" fn str_clone(val: Handle<Str>) -> Ref<Str> {
    handle_scope(|| {
        let vm = get_vm();

        val.dup(vm)
    })
}

pub extern "C" fn str_from_bytes(val: Handle<UInt8Array>, offset: usize, len: usize) -> Ref<Str> {
    handle_scope(|| {
        let vm = get_vm();
        let val: Handle<Str> = val.cast();

        Str::from_str(vm, val, offset, len)
    })
}

pub extern "C" fn gc_alloc(size: usize, array_ref: bool) -> *mut Obj {
    let vm = get_vm();
    vm.gc.alloc(vm, size, array_ref).to_mut_ptr()
}

pub extern "C" fn gc_collect() {
    let vm = get_vm();
    vm.gc.collect(vm, GcReason::ForceCollect);
}

pub extern "C" fn gc_minor_collect() {
    let vm = get_vm();
    vm.gc.minor_collect(vm, GcReason::ForceMinorCollect);
}

pub extern "C" fn argc() -> i32 {
    let vm = get_vm();

    if let Some(ref args) = vm.args.arg_argument {
        args.len() as i32
    } else {
        0
    }
}

pub extern "C" fn argv(ind: i32) -> Ref<Str> {
    let vm = get_vm();

    if let Some(ref args) = vm.args.arg_argument {
        if ind >= 0 && ind < args.len() as i32 {
            let value = &args[ind as usize];

            return Str::from_buffer(vm, value.as_bytes());
        }
    }

    panic!("argument does not exist");
}

pub extern "C" fn str_to_int32_success(val: Handle<Str>) -> bool {
    let slice = val.content();
    let val = str::from_utf8(slice).unwrap();

    val.parse::<i32>().is_ok()
}

pub extern "C" fn str_to_int32(val: Handle<Str>) -> i32 {
    let slice = val.content();
    let val = str::from_utf8(slice).unwrap();

    val.parse::<i32>().unwrap_or(0)
}

pub extern "C" fn str_to_int64_success(val: Handle<Str>) -> bool {
    let slice = val.content();
    let val = str::from_utf8(slice).unwrap();

    val.parse::<i64>().is_ok()
}

pub extern "C" fn str_to_int64(val: Handle<Str>) -> i64 {
    let slice = val.content();
    let val = str::from_utf8(slice).unwrap();

    val.parse::<i64>().unwrap_or(0)
}

pub extern "C" fn str_to_float32_success(val: Handle<Str>) -> bool {
    let slice = val.content();
    let val = str::from_utf8(slice).unwrap();

    val.parse::<f32>().is_ok()
}

pub extern "C" fn str_to_float32(val: Handle<Str>) -> f32 {
    let slice = val.content();
    let val = str::from_utf8(slice).unwrap();

    val.parse::<f32>().unwrap_or(0.0f32)
}

pub extern "C" fn str_to_float64_success(val: Handle<Str>) -> bool {
    let slice = val.content();
    let val = str::from_utf8(slice).unwrap();

    val.parse::<f64>().is_ok()
}

pub extern "C" fn str_to_float64(val: Handle<Str>) -> f64 {
    let slice = val.content();
    let val = str::from_utf8(slice).unwrap();

    val.parse::<f64>().unwrap_or(0.0)
}

pub extern "C" fn trap(trap_id: u32) {
    let vm = get_vm();
    let trap = Trap::from(trap_id).expect("invalid trap id!");

    let msg = match trap {
        Trap::DIV0 => "division by 0",
        Trap::ASSERT => "assert failed",
        Trap::INDEX_OUT_OF_BOUNDS => "array index out of bounds",
        Trap::NIL => "nil check failed",
        Trap::CAST => "cast failed",
        Trap::OOM => "out of memory",
        Trap::STACK_OVERFLOW => "stack overflow",
        Trap::ILLEGAL => "illegal state",
    };

    eprintln!("{}", msg);
    let stacktrace = stacktrace_from_last_dtn(vm);
    stacktrace.dump_err(vm);
    unsafe {
        libc::_exit(100 + trap_id as i32);
    }
}

pub extern "C" fn spawn_thread(managed_thread: Handle<ManagedThread>) {
    use crate::compiler;
    use crate::stack::DoraToNativeInfo;

    let vm = get_vm();

    // Create new thread in Parked state.
    let thread = DoraThread::new(vm, ThreadState::Parked);

    if !managed_thread.install_native_thread(&thread) {
        panic!("Thread was already started!");
    }

    vm.gc
        .add_finalizer(managed_thread.direct_ptr(), thread.clone());

    // Add thread to our list of all threads first. This method parks
    // and unparks the current thread, this means the handle needs to be created
    // afterwards.
    vm.threads.attach_thread(thread.clone());

    // Now we can create a handle for that newly created thread. Since the thread
    // is now registered, the handle is updated as well by the GC.
    // We create the handle in the new Parked thread, normally this would be unsafe.
    // Here it should be safe though, because the current thread is still Running
    // and therefore the GC can't run at this point.
    debug_assert!(current_thread().state_relaxed().is_running());
    let location = thread.handles.handle(managed_thread.direct()).location();

    thread::spawn(move || {
        // Initialize thread-local variable with thread
        let thread = init_current_thread(thread);

        let handle: Handle<Obj> = Handle::from_address(location);

        let stack_top = stack_pointer();
        let stack_limit = stack_top.sub(STACK_SIZE);
        thread.tld.set_stack_limit(stack_limit);

        // Thread was created in Parked state, so we need to Unpark
        // before we dereference handle.
        thread.unpark(vm);

        let main = {
            let cls_id = handle.header().vtbl().class_def().cls_id;
            let cls_id = cls_id.expect("no corresponding class");
            let cls = vm.classes.idx(cls_id);
            let cls = cls.read();
            let name = vm.interner.intern("run");
            cls.find_method(vm, name, false)
                .expect("run() method not found")
        };

        let tld = thread.tld_address();

        let fct_ptr = {
            let mut dtn = DoraToNativeInfo::new();
            let type_params = SourceTypeArray::empty();

            thread.use_dtn(&mut dtn, || compiler::generate(vm, main, &type_params))
        };

        // execute the thread object's run-method
        let dora_stub_address = vm.dora_stub();
        let fct: extern "C" fn(Address, Address, Ref<Obj>) =
            unsafe { mem::transmute(dora_stub_address) };
        fct(tld, fct_ptr, handle.direct());

        // remove thread from list of all threads
        vm.threads.detach_current_thread();

        // notify threads waiting in join() for this thread's end
        let mut running = thread.thread_state.lock();
        *running = false;
        thread.cv_thread_state.notify_all();

        deinit_current_thread();
    });
}

pub extern "C" fn join_thread(managed_thread: Handle<ManagedThread>) {
    let native_thread = managed_thread.native_thread();

    parked_scope(|| {
        let mut running = native_thread.thread_state.lock();

        while *running {
            native_thread.cv_thread_state.wait(&mut running);
        }
    });
}

pub extern "C" fn mutex_wait(mutex: Handle<ManagedMutex>, value: i32) {
    let vm = get_vm();
    vm.wait_lists.block(mutex, value);
}

pub extern "C" fn mutex_notify(mutex: Handle<ManagedMutex>) {
    let vm = get_vm();
    vm.wait_lists.wakeup(mutex.direct_ptr());
}

pub extern "C" fn condition_enqueue(cond: Handle<ManagedCondition>) {
    let vm = get_vm();
    vm.wait_lists.enqueue(cond);
}

pub extern "C" fn condition_block_after_enqueue(_cond: Handle<Obj>) {
    let thread = current_thread();
    thread.block();
}

pub extern "C" fn condition_wakeup_one(cond: Handle<Obj>) {
    let vm = get_vm();
    vm.wait_lists.wakeup(cond.direct_ptr());
}

pub extern "C" fn condition_wakeup_all(cond: Handle<Obj>) {
    let vm = get_vm();
    vm.wait_lists.wakeup_all(cond.direct_ptr());
}
<commit_msg>fix use-after-free when spawning threads<commit_after>use libc;

use std::char;
use std::io::{self, Write};
use std::mem;
use std::process;
use std::str;
use std::thread;
use std::time::Duration;

use crate::boots;
use crate::gc::{Address, GcReason};
use crate::handle::{handle_scope, Handle};
use crate::object::{Obj, Ref, Str, UInt8Array};
use crate::stack::stacktrace_from_last_dtn;
use crate::threads::{
    current_thread, deinit_current_thread, init_current_thread, parked_scope, DoraThread,
    ManagedThread, ThreadState, STACK_SIZE,
};
use crate::ty::SourceTypeArray;
use crate::vm::{get_vm, stack_pointer, ManagedCondition, ManagedMutex, Trap};

pub extern "C" fn uint8_to_string(val: u8) -> Ref<Str> {
    handle_scope(|| {
        let buffer = val.to_string();
        let vm = get_vm();

        Str::from_buffer(vm, buffer.as_bytes())
    })
}

pub extern "C" fn char_to_string(val: u32) -> Ref<Str> {
    handle_scope(|| {
        let buffer = unsafe { char::from_u32_unchecked(val) }.to_string();
        let vm = get_vm();

        Str::from_buffer(vm, buffer.as_bytes())
    })
}

pub extern "C" fn int32_to_string(val: i32) -> Ref<Str> {
    handle_scope(|| {
        let buffer = val.to_string();
        let vm = get_vm();

        Str::from_buffer(vm, buffer.as_bytes())
    })
}

pub extern "C" fn int64_to_string(val: i64) -> Ref<Str> {
    handle_scope(|| {
        let buffer = val.to_string();
        let vm = get_vm();

        Str::from_buffer(vm, buffer.as_bytes())
    })
}

pub extern "C" fn float32_to_string(val: f32) -> Ref<Str> {
    handle_scope(|| {
        let buffer = val.to_string();
        let vm = get_vm();

        Str::from_buffer(vm, buffer.as_bytes())
    })
}

pub extern "C" fn float64_to_string(val: f64) -> Ref<Str> {
    handle_scope(|| {
        let buffer = val.to_string();
        let vm = get_vm();

        Str::from_buffer(vm, buffer.as_bytes())
    })
}

pub extern "C" fn print(val: Handle<Str>) {
    io::stdout().write(val.content()).unwrap();
}

pub extern "C" fn fatal_error(msg: Handle<Str>) {
    eprint!("fatal error: ");
    io::stderr().write(msg.content()).unwrap();
    eprintln!("");

    let vm = get_vm();
    let stacktrace = stacktrace_from_last_dtn(vm);
    stacktrace.dump_err(vm);

    process::exit(1);
}

pub extern "C" fn abort() {
    eprintln!("program aborted.");
    process::exit(1);
}

pub extern "C" fn exit(status: i32) {
    process::exit(status);
}

pub extern "C" fn unreachable() {
    eprintln!("unreachable code executed.");
    process::exit(1);
}

pub extern "C" fn timestamp() -> u64 {
    use crate::timer;

    timer::timestamp()
}

pub extern "C" fn println(val: Handle<Str>) {
    let stdout = io::stdout();
    let mut handle = stdout.lock();
    handle.write(val.content()).unwrap();
    handle.write(b"\n").unwrap();
}

pub extern "C" fn sleep(seconds: i32) {
    assert!(seconds >= 0);
    thread::sleep(Duration::from_secs(seconds as u64));
}

pub extern "C" fn call(fct: Handle<Str>) {
    let fct_name = fct.to_cstring();
    let fct_name = fct_name.to_str().unwrap();

    let vm = get_vm();
    let name = vm.interner.intern(fct_name);

    if let Some(fct_id) = vm
        .namespace_table(vm.global_namespace_id)
        .read()
        .get_fct(name)
    {
        {
            let fct = vm.fcts.idx(fct_id);
            let fct = fct.read();

            if !fct.param_types.is_empty() {
                writeln!(&mut io::stderr(), "fct `{}` takes arguments.", fct_name)
                    .expect("could not print to stderr");
                process::exit(1);
            }
        }

        vm.run(fct_id);
    } else {
        writeln!(&mut io::stderr(), "fct `{}` not found.", fct_name)
            .expect("could not print to stderr");
        process::exit(1);
    }
}

pub extern "C" fn bytecode(name: Handle<Str>) -> Ref<Obj> {
    let fct_name = name.to_cstring();
    let fct_name = fct_name.to_str().unwrap();

    let vm = get_vm();

    boots::bytecode(vm, &fct_name)
}

pub extern "C" fn strcmp(lhs: Handle<Str>, rhs: Handle<Str>) -> i32 {
    unsafe {
        libc::strcmp(
            lhs.data() as *const libc::c_char,
            rhs.data() as *const libc::c_char,
        )
    }
}

pub extern "C" fn strcat(lhs: Handle<Str>, rhs: Handle<Str>) -> Ref<Str> {
    handle_scope(|| {
        let vm = get_vm();
        Str::concat(vm, lhs, rhs).direct()
    })
}

pub extern "C" fn str_clone(val: Handle<Str>) -> Ref<Str> {
    handle_scope(|| {
        let vm = get_vm();

        val.dup(vm)
    })
}

pub extern "C" fn str_from_bytes(val: Handle<UInt8Array>, offset: usize, len: usize) -> Ref<Str> {
    handle_scope(|| {
        let vm = get_vm();
        let val: Handle<Str> = val.cast();

        Str::from_str(vm, val, offset, len)
    })
}

pub extern "C" fn gc_alloc(size: usize, array_ref: bool) -> *mut Obj {
    let vm = get_vm();
    vm.gc.alloc(vm, size, array_ref).to_mut_ptr()
}

pub extern "C" fn gc_collect() {
    let vm = get_vm();
    vm.gc.collect(vm, GcReason::ForceCollect);
}

pub extern "C" fn gc_minor_collect() {
    let vm = get_vm();
    vm.gc.minor_collect(vm, GcReason::ForceMinorCollect);
}

pub extern "C" fn argc() -> i32 {
    let vm = get_vm();

    if let Some(ref args) = vm.args.arg_argument {
        args.len() as i32
    } else {
        0
    }
}

pub extern "C" fn argv(ind: i32) -> Ref<Str> {
    let vm = get_vm();

    if let Some(ref args) = vm.args.arg_argument {
        if ind >= 0 && ind < args.len() as i32 {
            let value = &args[ind as usize];

            return Str::from_buffer(vm, value.as_bytes());
        }
    }

    panic!("argument does not exist");
}

pub extern "C" fn str_to_int32_success(val: Handle<Str>) -> bool {
    let slice = val.content();
    let val = str::from_utf8(slice).unwrap();

    val.parse::<i32>().is_ok()
}

pub extern "C" fn str_to_int32(val: Handle<Str>) -> i32 {
    let slice = val.content();
    let val = str::from_utf8(slice).unwrap();

    val.parse::<i32>().unwrap_or(0)
}

pub extern "C" fn str_to_int64_success(val: Handle<Str>) -> bool {
    let slice = val.content();
    let val = str::from_utf8(slice).unwrap();

    val.parse::<i64>().is_ok()
}

pub extern "C" fn str_to_int64(val: Handle<Str>) -> i64 {
    let slice = val.content();
    let val = str::from_utf8(slice).unwrap();

    val.parse::<i64>().unwrap_or(0)
}

pub extern "C" fn str_to_float32_success(val: Handle<Str>) -> bool {
    let slice = val.content();
    let val = str::from_utf8(slice).unwrap();

    val.parse::<f32>().is_ok()
}

pub extern "C" fn str_to_float32(val: Handle<Str>) -> f32 {
    let slice = val.content();
    let val = str::from_utf8(slice).unwrap();

    val.parse::<f32>().unwrap_or(0.0f32)
}

pub extern "C" fn str_to_float64_success(val: Handle<Str>) -> bool {
    let slice = val.content();
    let val = str::from_utf8(slice).unwrap();

    val.parse::<f64>().is_ok()
}

pub extern "C" fn str_to_float64(val: Handle<Str>) -> f64 {
    let slice = val.content();
    let val = str::from_utf8(slice).unwrap();

    val.parse::<f64>().unwrap_or(0.0)
}

pub extern "C" fn trap(trap_id: u32) {
    let vm = get_vm();
    let trap = Trap::from(trap_id).expect("invalid trap id!");

    let msg = match trap {
        Trap::DIV0 => "division by 0",
        Trap::ASSERT => "assert failed",
        Trap::INDEX_OUT_OF_BOUNDS => "array index out of bounds",
        Trap::NIL => "nil check failed",
        Trap::CAST => "cast failed",
        Trap::OOM => "out of memory",
        Trap::STACK_OVERFLOW => "stack overflow",
        Trap::ILLEGAL => "illegal state",
    };

    eprintln!("{}", msg);
    let stacktrace = stacktrace_from_last_dtn(vm);
    stacktrace.dump_err(vm);
    unsafe {
        libc::_exit(100 + trap_id as i32);
    }
}

pub extern "C" fn spawn_thread(managed_thread: Handle<ManagedThread>) {
    let vm = get_vm();

    // Create new thread in Parked state.
    let thread = DoraThread::new(vm, ThreadState::Parked);

    if !managed_thread.install_native_thread(&thread) {
        panic!("Thread was already started!");
    }

    vm.gc
        .add_finalizer(managed_thread.direct_ptr(), thread.clone());

    // Add thread to our list of all threads first. This method parks
    // and unparks the current thread, this means the handle needs to be created
    // afterwards.
    vm.threads.attach_thread(thread.clone());

    // Now we can create a handle for that newly created thread. Since the thread
    // is now registered, the handle is updated as well by the GC.
    // We create the handle in the new Parked thread, normally this would be unsafe.
    // Here it should be safe though, because the current thread is still Running
    // and therefore the GC can't run at this point.
    debug_assert!(current_thread().state_relaxed().is_running());
    let location = thread.handles.handle(managed_thread.direct()).location();

    thread::spawn(move || {
        // Initialize thread-local variable with thread
        let thread = init_current_thread(thread);
        thread_main(thread, location);
        deinit_current_thread();
    });
}

fn thread_main(thread: &DoraThread, location: Address) {
    use crate::compiler;
    use crate::stack::DoraToNativeInfo;

    let vm = get_vm();
    let handle: Handle<Obj> = Handle::from_address(location);

    let stack_top = stack_pointer();
    let stack_limit = stack_top.sub(STACK_SIZE);
    thread.tld.set_stack_limit(stack_limit);

    // Thread was created in Parked state, so we need to Unpark
    // before we dereference handle.
    thread.unpark(vm);

    let main = {
        let cls_id = handle.header().vtbl().class_def().cls_id;
        let cls_id = cls_id.expect("no corresponding class");
        let cls = vm.classes.idx(cls_id);
        let cls = cls.read();
        let name = vm.interner.intern("run");
        cls.find_method(vm, name, false)
            .expect("run() method not found")
    };

    let tld = thread.tld_address();

    let fct_ptr = {
        let mut dtn = DoraToNativeInfo::new();
        let type_params = SourceTypeArray::empty();

        thread.use_dtn(&mut dtn, || compiler::generate(vm, main, &type_params))
    };

    // execute the thread object's run-method
    let dora_stub_address = vm.dora_stub();
    let fct: extern "C" fn(Address, Address, Ref<Obj>) =
        unsafe { mem::transmute(dora_stub_address) };
    fct(tld, fct_ptr, handle.direct());

    // remove thread from list of all threads
    vm.threads.detach_current_thread();

    // notify threads waiting in join() for this thread's end
    let mut running = thread.thread_state.lock();
    *running = false;
    thread.cv_thread_state.notify_all();
}

pub extern "C" fn join_thread(managed_thread: Handle<ManagedThread>) {
    let native_thread = managed_thread.native_thread();

    parked_scope(|| {
        let mut running = native_thread.thread_state.lock();

        while *running {
            native_thread.cv_thread_state.wait(&mut running);
        }
    });
}

pub extern "C" fn mutex_wait(mutex: Handle<ManagedMutex>, value: i32) {
    let vm = get_vm();
    vm.wait_lists.block(mutex, value);
}

pub extern "C" fn mutex_notify(mutex: Handle<ManagedMutex>) {
    let vm = get_vm();
    vm.wait_lists.wakeup(mutex.direct_ptr());
}

pub extern "C" fn condition_enqueue(cond: Handle<ManagedCondition>) {
    let vm = get_vm();
    vm.wait_lists.enqueue(cond);
}

pub extern "C" fn condition_block_after_enqueue(_cond: Handle<Obj>) {
    let thread = current_thread();
    thread.block();
}

pub extern "C" fn condition_wakeup_one(cond: Handle<Obj>) {
    let vm = get_vm();
    vm.wait_lists.wakeup(cond.direct_ptr());
}

pub extern "C" fn condition_wakeup_all(cond: Handle<Obj>) {
    let vm = get_vm();
    vm.wait_lists.wakeup_all(cond.direct_ptr());
}
<|endoftext|>


<commit_before>use std::collections::{BTreeMap, HashMap};

use search::term::Term;
use search::document::Document;

#[derive(Debug)]
pub struct MemoryIndexStore {
    docs: BTreeMap<u64, Document>,
    index: BTreeMap<Term, BTreeMap<String, Vec<(u64, u32)>>>,
    next_doc_num: u64,
    doc_id_map: HashMap<String, u64>,
}

impl MemoryIndexStore {
    pub fn new() -> MemoryIndexStore {
        MemoryIndexStore {
            docs: BTreeMap::new(),
            index: BTreeMap::new(),
            next_doc_num: 1,
            doc_id_map: HashMap::new(),
        }
    }

    pub fn get_document_by_id(&self, id: &str) -> Option<&Document> {
        match self.doc_id_map.get(id) {
            Some(doc_num) => self.docs.get(doc_num),
            None => None,
        }
    }

    pub fn contains_document_id(&self, id: &str) -> bool {
        self.doc_id_map.contains_key(id)
    }

    pub fn remove_document_by_id(&mut self, id: &str) -> bool {
        match self.doc_id_map.remove(id) {
            Some(doc_num) => {
                self.docs.remove(&doc_num);

                true
            }
            None => false
        }
    }

    pub fn insert_or_update_document(&mut self, doc: Document) {
        let doc_num = self.next_doc_num;
        self.next_doc_num += 1;

        // Put field contents in inverted index
        for (field_name, tokens) in doc.fields.iter() {
            let mut position: u32 = 1;

            for token in tokens.iter() {
                if !self.index.contains_key(&token.term) {
                    self.index.insert(token.term.clone(), BTreeMap::new());
                }

                let mut index_fields = self.index.get_mut(&token.term).unwrap();

                if !index_fields.contains_key(field_name) {
                    index_fields.insert(field_name.clone(), Vec::new());
                }

                let mut postings_list = index_fields.get_mut(field_name).unwrap();
                postings_list.push((doc_num, token.position));
            }
        }

        self.doc_id_map.insert(doc.id.clone(), doc_num);
        self.docs.insert(doc_num, doc);
    }

    pub fn num_docs(&self) -> usize {
        self.docs.len()
    }

    pub fn iter_docs<'a>(&'a self) -> Box<Iterator<Item=&'a Document> + 'a> {
        Box::new(self.docs.values())
    }
}
<commit_msg>Changed internal format of MemoryIndexStore.index<commit_after>use std::collections::{BTreeMap, HashMap};

use search::term::Term;
use search::document::Document;

#[derive(Debug)]
pub struct MemoryIndexStore {
    docs: BTreeMap<u64, Document>,
    index: BTreeMap<Term, BTreeMap<String, BTreeMap<u64, Vec<u32>>>>,
    next_doc_num: u64,
    doc_id_map: HashMap<String, u64>,
}

impl MemoryIndexStore {
    pub fn new() -> MemoryIndexStore {
        MemoryIndexStore {
            docs: BTreeMap::new(),
            index: BTreeMap::new(),
            next_doc_num: 1,
            doc_id_map: HashMap::new(),
        }
    }

    pub fn get_document_by_id(&self, id: &str) -> Option<&Document> {
        match self.doc_id_map.get(id) {
            Some(doc_num) => self.docs.get(doc_num),
            None => None,
        }
    }

    pub fn contains_document_id(&self, id: &str) -> bool {
        self.doc_id_map.contains_key(id)
    }

    pub fn remove_document_by_id(&mut self, id: &str) -> bool {
        match self.doc_id_map.remove(id) {
            Some(doc_num) => {
                self.docs.remove(&doc_num);

                true
            }
            None => false
        }
    }

    pub fn insert_or_update_document(&mut self, doc: Document) {
        let doc_num = self.next_doc_num;
        self.next_doc_num += 1;

        // Put field contents in inverted index
        for (field_name, tokens) in doc.fields.iter() {
            let mut position: u32 = 1;

            for token in tokens.iter() {
                if !self.index.contains_key(&token.term) {
                    self.index.insert(token.term.clone(), BTreeMap::new());
                }

                let mut index_fields = self.index.get_mut(&token.term).unwrap();

                if !index_fields.contains_key(field_name) {
                    index_fields.insert(field_name.clone(), BTreeMap::new());
                }

                let mut index_docs = index_fields.get_mut(field_name).unwrap();
                if !index_docs.contains_key(&doc_num) {
                    index_docs.insert(doc_num, Vec::new());
                }

                let mut postings_list = index_docs.get_mut(&doc_num).unwrap();
                postings_list.push(token.position);
            }
        }

        self.doc_id_map.insert(doc.id.clone(), doc_num);
        self.docs.insert(doc_num, doc);
    }

    pub fn num_docs(&self) -> usize {
        self.docs.len()
    }

    pub fn iter_docs<'a>(&'a self) -> Box<Iterator<Item=&'a Document> + 'a> {
        Box::new(self.docs.values())
    }
}
<|endoftext|>


<commit_before>// xfail-test

// FIXME: + should allow immutable or mutable vectors on the right
// hand side in all cases. We are getting compiler errors about this
// now, so I'm xfailing the test for now. -eholk

fn add(i: [int], m: [mut int], c: [const int]) {

    // Check that:
    //  (1) vectors of any two mutabilities can be added
    //  (2) result has mutability of lhs

   add(i + [3],
       m + [3],
       [3]);

   add(i + [mut 3],
       m + [mut 3],
       [mut 3]);

   add(i + i,
       m + i,
       i);

   add(i + m,
       m + m,
       m);

   add(i + c,
       m + c,
       c);

   add(m + [3], //! ERROR mismatched types
       m + [3],
       m + [3]);

   add(i + [3],
       i + [3], //! ERROR mismatched types
       i + [3]);

   add(c + [3], //! ERROR mismatched types
                //!^ ERROR binary operation + cannot be applied
       c + [3], //! ERROR binary operation + cannot be applied
                //!^ mismatched types
       [3]);

   add(m + [mut 3], //! ERROR mismatched types
       m + [mut 3],
       m + [mut 3]);

   add(i + [mut 3],
       i + [mut 3], //! ERROR mismatched types
       i + [mut 3]);

   add(c + [mut 3], //! ERROR binary operation + cannot be applied
                    //!^ mismatched types
       c + [mut 3], //! ERROR binary operation + cannot be applied
                    //!^ mismatched types
       [mut 3]);

   add(m + i, //! ERROR mismatched types
       m + i,
       m + i);

   add(i + i,
       i + i, //! ERROR mismatched types
       i + i);

   add(c + i, //! ERROR binary operation + cannot be applied
              //!^ ERROR mismatched types
       c + i, //! ERROR binary operation + cannot be applied
              //!^ ERROR mismatched types
       i);

   add(m + m, //! ERROR mismatched types
       m + m,
       m + m);

   add(i + m,
       i + m, //! ERROR mismatched types
       i + m);

   add(c + m, //! ERROR binary operation + cannot be applied
              //!^ ERROR mismatched types
       c + m, //! ERROR binary operation + cannot be applied
              //!^ ERROR mismatched types
       m);

   add(m + c, //! ERROR mismatched types
       m + c,
       m + c);

   add(i + c,
       i + c, //! ERROR mismatched types
       i + c);

   add(c + c, //! ERROR binary operation + cannot be applied
              //!^ ERROR mismatched types
       c + c, //! ERROR binary operation + cannot be applied
              //!^ ERROR mismatched types
       c);
}

fn main() {
}
<commit_msg>Adding an issue number to vec-add.rs<commit_after>// xfail-test

// FIXME (Issue #2711): + should allow immutable or mutable vectors on
// the right hand side in all cases. We are getting compiler errors
// about this now, so I'm xfailing the test for now. -eholk

fn add(i: [int], m: [mut int], c: [const int]) {

    // Check that:
    //  (1) vectors of any two mutabilities can be added
    //  (2) result has mutability of lhs

   add(i + [3],
       m + [3],
       [3]);

   add(i + [mut 3],
       m + [mut 3],
       [mut 3]);

   add(i + i,
       m + i,
       i);

   add(i + m,
       m + m,
       m);

   add(i + c,
       m + c,
       c);

   add(m + [3], //! ERROR mismatched types
       m + [3],
       m + [3]);

   add(i + [3],
       i + [3], //! ERROR mismatched types
       i + [3]);

   add(c + [3], //! ERROR mismatched types
                //!^ ERROR binary operation + cannot be applied
       c + [3], //! ERROR binary operation + cannot be applied
                //!^ mismatched types
       [3]);

   add(m + [mut 3], //! ERROR mismatched types
       m + [mut 3],
       m + [mut 3]);

   add(i + [mut 3],
       i + [mut 3], //! ERROR mismatched types
       i + [mut 3]);

   add(c + [mut 3], //! ERROR binary operation + cannot be applied
                    //!^ mismatched types
       c + [mut 3], //! ERROR binary operation + cannot be applied
                    //!^ mismatched types
       [mut 3]);

   add(m + i, //! ERROR mismatched types
       m + i,
       m + i);

   add(i + i,
       i + i, //! ERROR mismatched types
       i + i);

   add(c + i, //! ERROR binary operation + cannot be applied
              //!^ ERROR mismatched types
       c + i, //! ERROR binary operation + cannot be applied
              //!^ ERROR mismatched types
       i);

   add(m + m, //! ERROR mismatched types
       m + m,
       m + m);

   add(i + m,
       i + m, //! ERROR mismatched types
       i + m);

   add(c + m, //! ERROR binary operation + cannot be applied
              //!^ ERROR mismatched types
       c + m, //! ERROR binary operation + cannot be applied
              //!^ ERROR mismatched types
       m);

   add(m + c, //! ERROR mismatched types
       m + c,
       m + c);

   add(i + c,
       i + c, //! ERROR mismatched types
       i + c);

   add(c + c, //! ERROR binary operation + cannot be applied
              //!^ ERROR mismatched types
       c + c, //! ERROR binary operation + cannot be applied
              //!^ ERROR mismatched types
       c);
}

fn main() {
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![forbid(warnings)]

// Pretty printing tests complain about `use std::predule::*`
#![allow(unused_imports)]

// A var moved into a proc, that has a mutable loan path should
// not trigger a misleading unused_mut warning.

use std::thread;

pub fn main() {
    let mut stdin = std::old_io::stdin();
    thread::spawn(move|| {
        let _ = stdin.read_to_end();
    });
}
<commit_msg>Rollup merge of #22941 - sfackler:re-disable, r=sfackler<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// DON'T REENABLE THIS UNLESS YOU'VE ACTUALLY FIXED THE UNDERLYING ISSUE
// ignore-android seems to block forever

#![forbid(warnings)]

// Pretty printing tests complain about `use std::predule::*`
#![allow(unused_imports)]

// A var moved into a proc, that has a mutable loan path should
// not trigger a misleading unused_mut warning.

use std::thread;

pub fn main() {
    let mut stdin = std::old_io::stdin();
    thread::spawn(move|| {
        let _ = stdin.read_to_end();
    });
}
<|endoftext|>


<commit_before>use std::io::{Read, Write};
use std::io;
use std::fs::File;
use std::error::Error;
use hyper;
use hyper::net::Fresh;
use hyper::server::{Server, Request, Response};
use hyper::uri::RequestUri;
use hyper::Url;
use hyper::header::{Headers,ContentType,Location,SetCookie};
use hyper::header;
use cookie::Cookie;
use mime::Mime;
use url::SchemeData::Relative;
use rustc_serialize::json;
use rustc_serialize::json::ToJson;
use std::path::Path;
use std::fs::PathExt;
use conduit_mime_types::Types;
use std::str::FromStr;
use std::env;
use websocket::{client,Client,stream,Message,Sender,Receiver};
use std::thread;
use cbor;
use cbor::ToCbor;

use value::Value;
use server::Event;
use relation::Change;

// The (increasingly misnamed) login server is responsible for:
// * handling authentication via authrocket
// * serving static files for the editor and clients
// TODO needs review

#[derive(RustcDecodable, RustcEncodable, Debug, Clone)]
struct Session {
    client: String,
    id: String,
    user_id: String,
    object: String,
    created_at: f64,
    expires_at: f64,
    ip: String,
    user: User,
}

#[derive(RustcDecodable, RustcEncodable, Debug, Clone)]
struct User {
    custom: Custom,
    id: String,
    realm_id: String,
    username: String,
    state: String,
    user_type: String,
    reference: Option<String>,
    name: String,
    email: String,
    object: String,
    last_login_at: f64,
    last_login_on: f64,
    created_at: f64,
    first_name: Option<String>,
    last_name: Option<String>,
    credentials: Vec<Credential>,
    membership_count: f64,
}

#[derive(RustcDecodable, RustcEncodable, Debug, Clone)]
struct Custom;

#[derive(RustcDecodable, RustcEncodable, Debug, Clone)]
struct Credential {
    id: String,
    credential_type: String,
    object: String,
}

pub fn run(socket_addr: ::std::net::SocketAddr) {

    // TODO high thread-count is a workaround for https://github.com/hyperium/hyper/issues/368
    Server::http(socket_addr).unwrap().handle_threads(login, 100).unwrap();
}

fn read_file_bytes(filename: &str) -> Vec<u8> {
    let mut file = File::open(&filename).unwrap();
    let mut contents: Vec<u8> = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    contents
}

fn file_exists(path: &str) -> bool {
    let path_obj = Path::new(path);
    path_obj.is_file() && path_obj.exists()
}

fn serve_local_or_file(mut res: Response<Fresh>, path: &Vec<String>, default_file: &str) -> io::Result<()> {
    let mime_types = Types::new().unwrap();
    let local_path = path[1..].iter().fold("../ui".to_owned(), |end, cur| end + "/" + cur);
    let file;
    let file_path: &str;
    if file_exists(&local_path) {
        file = read_file_bytes(&local_path);
        file_path = &local_path;
    } else {
        let content = read_file_bytes(default_file);
        //@HACK, @TODO: absolutize the html file so that it looks in the right place
        //this allows us to use file:/// locally, while still doing the right thing
        //when we're hooked up to the server
        let mut str = String::from_utf8(content).unwrap();
        str = str.replace("href=\"", "href=\"/app/");
        str = str.replace("src=\"", "src=\"/app/");
        str = str.replace("/app/http", "http");
        file = str.as_bytes().to_owned();
        file_path = default_file;
    }
    let mime: Mime = Mime::from_str(mime_types.mime_for_path(Path::new(&file_path))).unwrap();
    res.headers_mut().set(ContentType(mime));
    let mut res = try!(res.start());
    try!(res.write_all(&file));
    try!(res.end());
    Ok(())
}

pub fn get_user_id(cookies: Option<&header::Cookie>) -> Option<String> {
    match cookies {
        Some(cookies) => {
            match cookies.iter().find(|cookie| cookie.name == "userid") {
                Some(user_id) => Some(user_id.value.clone()),
                None => None,
            }
        },
        None => None,
    }
}

fn login(req: Request, mut res: Response<Fresh>) {

    // Don't use a port if we're running in local mode
    let mut port = ":8000";
    for argument in env::args() {
        match &*argument {
            "local" => {
                port = "";
            },
            _ => continue,
        }
    }

    match (&req.method.clone(), &req.uri.clone()) {
        (&hyper::Get, &RequestUri::AbsolutePath(ref relative_path)) => {

            // Form a url from the path
            let absolute_path = "http://localhost".to_string() + relative_path;
            let url = Url::parse(&*absolute_path).unwrap();

            // Get the file name
            let scheme_data = &url.scheme_data;

            let requested_file = match scheme_data {
                &Relative(ref rsd) => rsd.path[0].clone(),
                _ => panic!("Expected relative path"),
            };
            let path_info = match scheme_data {
                &Relative(ref rsd) => rsd,
                _ => panic!("Expected relative path"),
            };

            // Parse the query string
            let query_pairs = &url.query_pairs();

            // Handle login
            match &*requested_file {
                "app.html" | "app" => {
                    let result = serve_local_or_file(res, &path_info.path, "../ui/app.html");
                    if let Err(error) = result {
                        println!("Warning: serve error {:?}", error);
                    }
                },
                "editor.html" | "editor" | "" => {
                    let result = serve_local_or_file(res, &path_info.path, "../ui/editor.html");
                    if let Err(error) = result {
                        println!("Warning: serve error {:?}", error);
                    }
                },
                "login.html" => {
                    println!("Authenticating User");

                    let referer = format!("{}",req.headers.get::<hyper::header::Referer>().unwrap());
                    let referer_url = Url::parse(&*referer).unwrap();

                    let pairs = query_pairs.clone().unwrap();
                    match &pairs[..] {
                        [(_ , ref page), (ref token_type, ref token), ..] if token_type.clone() == "token".to_string() => {

                            // We have a login token, now to authenticate
                            let client = hyper::client::Client::new();
                            let api_call = "https://api-e1.authrocket.com/v1/sessions/".to_string() + token;
                            let req = client.request(hyper::method::Method::Get,&*api_call);

                            // Set the appropriate headers for authorocket
                            let mut headers = Headers::new();
                            let json: Mime = "application/json".parse().unwrap();
                            let content_type = ContentType(json);
                            headers.set_raw("X-Authrocket-Account",vec!["org_0vC7wPw9XphPGQnSqYB6bz".to_string().into_bytes()]);
                            headers.set_raw("X-Authrocket-Api-Key",vec!["key_jtnCRWxQvDD0p5HATR9RBIe4WnxnwV6pWNzwmZQLnSZ".to_string().into_bytes()]);
                            headers.set_raw("X-Authrocket-Realm",vec!["rl_0vC7wd03CqwhpK7kT8fvAc".to_string().into_bytes()]);
                            headers.set_raw("Accept",vec!["application/json".to_string().into_bytes()]);
                            headers.set(content_type);

                            // Send the request and receive a response with session data
                            let mut client_res = req.headers(headers).send().unwrap();

                            match client_res.status_raw() {

                                &hyper::http::RawStatus(200,_) => {

                                    let mut body = String::new();
                                    client_res.read_to_string(&mut body).unwrap();
                                    let session_data: Session = json::decode(&body).unwrap();
                                    println!("Welcome to Eve, {:?}!",session_data.user.username);
                                    println!("Login Successful. Redirecting to user area.");

                                    // Connect to the Eve runtime and add the user to the eveusers table
                                    let ws_result = open_websocket("ws://0.0.0.0:2794");
                                    match ws_result {
                                        // If things went okay, redirect to the Eve UI
                                        Ok(mut sender) => {
                                            // Form the response
                                            *res.status_mut() = hyper::status::StatusCode::PermanentRedirect;

                                            // Form the response headers
                                            let mut headers = Headers::new();
                                            let redirect_url = referer_url.scheme.clone() + "://" + referer_url.domain().unwrap().clone() + port + "/" + page;
                                            let location = Location(redirect_url);
                                            let user_cookie = Cookie::new("userid".to_string(),session_data.user.id.clone());
                                            let cookies = SetCookie(vec![user_cookie]);
                                            headers.set(location);
                                            headers.set(cookies);
                                            *res.headers_mut() = headers;

                                            // Create eveusers table and insert the new user
                                            let change = ("eveusers".to_owned(),
                                                Change{
                                                    fields: vec!["eveusers: id".to_owned(), "eveusers: username".to_owned()],
                                                    insert: vec![vec![
                                                        Value::String(session_data.user.id.clone()),
                                                        Value::String(session_data.user.username.clone())
                                                    ]],
                                                    remove: vec![],
                                                });
                                            let event = Event{changes: vec![change], commands: vec![]};
                                            send_event(event, &mut sender);
                                            let _ = sender.send_message(Message::Close(None));
                                        }
                                        // Otherwise, throw an error... maybe redirect to a special page.
                                        Err(e) => panic!("ERROR: Had trouble connecting to the Eve runtime: {}. Is the server running?",e),
                                    }
                                    println!("Login complete.");
                                }
                                _ => {
                                    println!("ERROR: Could not authenticate user with token {}",token);
                                    *res.status_mut() = hyper::status::StatusCode::Forbidden;
                                    let result = serve_local_or_file(res, &path_info.path, "../ui/403.html");
                                    if let Err(error) = result {
                                        println!("Warning: serve error {:?}", error);
                                    }
                                }
                            };
                        },
                        _ => {
                        	*res.status_mut() = hyper::status::StatusCode::NotFound;
                        	let result = serve_local_or_file(res, &path_info.path, "../ui/404.html");
                        	if let Err(error) = result {
                        		println!("Warning: serve error {:?}", error);
                        	}
                        },
                    }
                },
                "logout.html" => {
                    println!("Logging out...");
                    let user_id = get_user_id(req.headers.get::<hyper::header::Cookie>());
                    println!("{:?}",user_id);
                },
                "favicon.ico" => (),
                _ => {
    				*res.status_mut() = hyper::status::StatusCode::NotFound;
    				let result = serve_local_or_file(res, &path_info.path, "../ui/404.html");
    				if let Err(error) = result {
    					println!("Warning: serve error {:?}", error);
    				}
                }
            };
        }
        _ => {
            *res.status_mut() = hyper::status::StatusCode::NotFound;
            let result = serve_local_or_file(res, &path_info.path, "../ui/404.html");
            if let Err(error) = result {
                println!("Warning: serve error {:?}", error);
            }
        }
    }
}

pub fn open_websocket(url_string: &str) -> Result<client::sender::Sender<stream::WebSocketStream>,String> {

    //let mut context = SslContext::new(SslMethod::Tlsv1).unwrap();
    //let _ = context.set_certificate_file(&(Path::new("server.crt")), X509FileType::PEM);
    //let _ = context.set_private_key_file(&(Path::new("server.key")), X509FileType::PEM);

    let url = Url::parse(url_string).unwrap();
    println!("Connecting to {}", url);

    let request = match Client::connect(url) {
        Ok(t) => t,
        Err(e) => {
            return Err(format!("{}", e).to_string());
        }
    };

    let response = match request.send() {
        Ok(t) => t,
        Err(e) => {
            return Err(format!("{}", e).to_string());
        }
    };

    match response.validate() {
        Ok(_) => println!("Response valid. Start sending/receiving..."),
        Err(e) => {
            return Err(format!("{}", e).to_string());
        }
    };

    let (sender, mut receiver) = response.begin().split();

    thread::spawn(move || {
        for message in receiver.incoming_messages() {
            let message = match message {
                Ok(m) => m,
                Err(_) => return,
            };
            match message {
                Message::Text(_) => {
                    //let json = Json::from_str(&text).unwrap();
                    //let event: Event = FromJson::from_json(&json);
                },
                Message::Close(_) => {
                    println!("Received close message");
                    return;
                }
                _ => println!("Unknown message: {:?}", message)
            }
        }
    });

    Ok(sender)
}

pub fn send_event(event: Event, sender: &mut client::sender::Sender<stream::WebSocketStream>) {
    let mut e = cbor::Encoder::from_memory();
    let json = event.to_json();
    let cbor = json.to_cbor();
    e.encode(vec![cbor]).unwrap();
    sender.send_message(Message::Binary(e.into_bytes())).unwrap();
}<commit_msg>Revert "Serve 404 on unsupported http method"<commit_after>use std::io::{Read, Write};
use std::io;
use std::fs::File;
use std::error::Error;
use hyper;
use hyper::net::Fresh;
use hyper::server::{Server, Request, Response};
use hyper::uri::RequestUri;
use hyper::Url;
use hyper::header::{Headers,ContentType,Location,SetCookie};
use hyper::header;
use cookie::Cookie;
use mime::Mime;
use url::SchemeData::Relative;
use rustc_serialize::json;
use rustc_serialize::json::ToJson;
use std::path::Path;
use std::fs::PathExt;
use conduit_mime_types::Types;
use std::str::FromStr;
use std::env;
use websocket::{client,Client,stream,Message,Sender,Receiver};
use std::thread;
use cbor;
use cbor::ToCbor;

use value::Value;
use server::Event;
use relation::Change;

// The (increasingly misnamed) login server is responsible for:
// * handling authentication via authrocket
// * serving static files for the editor and clients
// TODO needs review

#[derive(RustcDecodable, RustcEncodable, Debug, Clone)]
struct Session {
    client: String,
    id: String,
    user_id: String,
    object: String,
    created_at: f64,
    expires_at: f64,
    ip: String,
    user: User,
}

#[derive(RustcDecodable, RustcEncodable, Debug, Clone)]
struct User {
    custom: Custom,
    id: String,
    realm_id: String,
    username: String,
    state: String,
    user_type: String,
    reference: Option<String>,
    name: String,
    email: String,
    object: String,
    last_login_at: f64,
    last_login_on: f64,
    created_at: f64,
    first_name: Option<String>,
    last_name: Option<String>,
    credentials: Vec<Credential>,
    membership_count: f64,
}

#[derive(RustcDecodable, RustcEncodable, Debug, Clone)]
struct Custom;

#[derive(RustcDecodable, RustcEncodable, Debug, Clone)]
struct Credential {
    id: String,
    credential_type: String,
    object: String,
}

pub fn run(socket_addr: ::std::net::SocketAddr) {

    // TODO high thread-count is a workaround for https://github.com/hyperium/hyper/issues/368
    Server::http(socket_addr).unwrap().handle_threads(login, 100).unwrap();
}

fn read_file_bytes(filename: &str) -> Vec<u8> {
    let mut file = File::open(&filename).unwrap();
    let mut contents: Vec<u8> = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    contents
}

fn file_exists(path: &str) -> bool {
    let path_obj = Path::new(path);
    path_obj.is_file() && path_obj.exists()
}

fn serve_local_or_file(mut res: Response<Fresh>, path: &Vec<String>, default_file: &str) -> io::Result<()> {
    let mime_types = Types::new().unwrap();
    let local_path = path[1..].iter().fold("../ui".to_owned(), |end, cur| end + "/" + cur);
    let file;
    let file_path: &str;
    if file_exists(&local_path) {
        file = read_file_bytes(&local_path);
        file_path = &local_path;
    } else {
        let content = read_file_bytes(default_file);
        //@HACK, @TODO: absolutize the html file so that it looks in the right place
        //this allows us to use file:/// locally, while still doing the right thing
        //when we're hooked up to the server
        let mut str = String::from_utf8(content).unwrap();
        str = str.replace("href=\"", "href=\"/app/");
        str = str.replace("src=\"", "src=\"/app/");
        str = str.replace("/app/http", "http");
        file = str.as_bytes().to_owned();
        file_path = default_file;
    }
    let mime: Mime = Mime::from_str(mime_types.mime_for_path(Path::new(&file_path))).unwrap();
    res.headers_mut().set(ContentType(mime));
    let mut res = try!(res.start());
    try!(res.write_all(&file));
    try!(res.end());
    Ok(())
}

pub fn get_user_id(cookies: Option<&header::Cookie>) -> Option<String> {
    match cookies {
        Some(cookies) => {
            match cookies.iter().find(|cookie| cookie.name == "userid") {
                Some(user_id) => Some(user_id.value.clone()),
                None => None,
            }
        },
        None => None,
    }
}

fn login(req: Request, mut res: Response<Fresh>) {

    // Don't use a port if we're running in local mode
    let mut port = ":8000";
    for argument in env::args() {
        match &*argument {
            "local" => {
                port = "";
            },
            _ => continue,
        }
    }

    match (&req.method.clone(), &req.uri.clone()) {
        (&hyper::Get, &RequestUri::AbsolutePath(ref relative_path)) => {

            // Form a url from the path
            let absolute_path = "http://localhost".to_string() + relative_path;
            let url = Url::parse(&*absolute_path).unwrap();

            // Get the file name
            let scheme_data = &url.scheme_data;

            let requested_file = match scheme_data {
                &Relative(ref rsd) => rsd.path[0].clone(),
                _ => panic!("Expected relative path"),
            };
            let path_info = match scheme_data {
                &Relative(ref rsd) => rsd,
                _ => panic!("Expected relative path"),
            };

            // Parse the query string
            let query_pairs = &url.query_pairs();

            // Handle login
            match &*requested_file {
                "app.html" | "app" => {
                    let result = serve_local_or_file(res, &path_info.path, "../ui/app.html");
                    if let Err(error) = result {
                        println!("Warning: serve error {:?}", error);
                    }
                },
                "editor.html" | "editor" | "" => {
                    let result = serve_local_or_file(res, &path_info.path, "../ui/editor.html");
                    if let Err(error) = result {
                        println!("Warning: serve error {:?}", error);
                    }
                },
                "login.html" => {
                    println!("Authenticating User");

                    let referer = format!("{}",req.headers.get::<hyper::header::Referer>().unwrap());
                    let referer_url = Url::parse(&*referer).unwrap();

                    let pairs = query_pairs.clone().unwrap();
                    match &pairs[..] {
                        [(_ , ref page), (ref token_type, ref token), ..] if token_type.clone() == "token".to_string() => {

                            // We have a login token, now to authenticate
                            let client = hyper::client::Client::new();
                            let api_call = "https://api-e1.authrocket.com/v1/sessions/".to_string() + token;
                            let req = client.request(hyper::method::Method::Get,&*api_call);

                            // Set the appropriate headers for authorocket
                            let mut headers = Headers::new();
                            let json: Mime = "application/json".parse().unwrap();
                            let content_type = ContentType(json);
                            headers.set_raw("X-Authrocket-Account",vec!["org_0vC7wPw9XphPGQnSqYB6bz".to_string().into_bytes()]);
                            headers.set_raw("X-Authrocket-Api-Key",vec!["key_jtnCRWxQvDD0p5HATR9RBIe4WnxnwV6pWNzwmZQLnSZ".to_string().into_bytes()]);
                            headers.set_raw("X-Authrocket-Realm",vec!["rl_0vC7wd03CqwhpK7kT8fvAc".to_string().into_bytes()]);
                            headers.set_raw("Accept",vec!["application/json".to_string().into_bytes()]);
                            headers.set(content_type);

                            // Send the request and receive a response with session data
                            let mut client_res = req.headers(headers).send().unwrap();

                            match client_res.status_raw() {

                                &hyper::http::RawStatus(200,_) => {

                                    let mut body = String::new();
                                    client_res.read_to_string(&mut body).unwrap();
                                    let session_data: Session = json::decode(&body).unwrap();
                                    println!("Welcome to Eve, {:?}!",session_data.user.username);
                                    println!("Login Successful. Redirecting to user area.");

                                    // Connect to the Eve runtime and add the user to the eveusers table
                                    let ws_result = open_websocket("ws://0.0.0.0:2794");
                                    match ws_result {
                                        // If things went okay, redirect to the Eve UI
                                        Ok(mut sender) => {
                                            // Form the response
                                            *res.status_mut() = hyper::status::StatusCode::PermanentRedirect;

                                            // Form the response headers
                                            let mut headers = Headers::new();
                                            let redirect_url = referer_url.scheme.clone() + "://" + referer_url.domain().unwrap().clone() + port + "/" + page;
                                            let location = Location(redirect_url);
                                            let user_cookie = Cookie::new("userid".to_string(),session_data.user.id.clone());
                                            let cookies = SetCookie(vec![user_cookie]);
                                            headers.set(location);
                                            headers.set(cookies);
                                            *res.headers_mut() = headers;

                                            // Create eveusers table and insert the new user
                                            let change = ("eveusers".to_owned(),
                                                Change{
                                                    fields: vec!["eveusers: id".to_owned(), "eveusers: username".to_owned()],
                                                    insert: vec![vec![
                                                        Value::String(session_data.user.id.clone()),
                                                        Value::String(session_data.user.username.clone())
                                                    ]],
                                                    remove: vec![],
                                                });
                                            let event = Event{changes: vec![change], commands: vec![]};
                                            send_event(event, &mut sender);
                                            let _ = sender.send_message(Message::Close(None));
                                        }
                                        // Otherwise, throw an error... maybe redirect to a special page.
                                        Err(e) => panic!("ERROR: Had trouble connecting to the Eve runtime: {}. Is the server running?",e),
                                    }
                                    println!("Login complete.");
                                }
                                _ => {
                                    println!("ERROR: Could not authenticate user with token {}",token);
                                    *res.status_mut() = hyper::status::StatusCode::Forbidden;
                                    let result = serve_local_or_file(res, &path_info.path, "../ui/403.html");
                                    if let Err(error) = result {
                                        println!("Warning: serve error {:?}", error);
                                    }
                                }
                            };
                        },
                        _ => {
                        	*res.status_mut() = hyper::status::StatusCode::NotFound;
                        	let result = serve_local_or_file(res, &path_info.path, "../ui/404.html");
                        	if let Err(error) = result {
                        		println!("Warning: serve error {:?}", error);
                        	}
                        },
                    }
                },
                "logout.html" => {
                    println!("Logging out...");
                    let user_id = get_user_id(req.headers.get::<hyper::header::Cookie>());
                    println!("{:?}",user_id);
                },
                "favicon.ico" => (),
                _ => {
					*res.status_mut() = hyper::status::StatusCode::NotFound;
					let result = serve_local_or_file(res, &path_info.path, "../ui/404.html");
					if let Err(error) = result {
						println!("Warning: serve error {:?}", error);
					}
                }
            };
        }
        _ => panic!("Unsupported HTTP Method."),
    }
}

pub fn open_websocket(url_string: &str) -> Result<client::sender::Sender<stream::WebSocketStream>,String> {

    //let mut context = SslContext::new(SslMethod::Tlsv1).unwrap();
    //let _ = context.set_certificate_file(&(Path::new("server.crt")), X509FileType::PEM);
    //let _ = context.set_private_key_file(&(Path::new("server.key")), X509FileType::PEM);

    let url = Url::parse(url_string).unwrap();
    println!("Connecting to {}", url);

    let request = match Client::connect(url) {
        Ok(t) => t,
        Err(e) => {
            return Err(format!("{}", e).to_string());
        }
    };

    let response = match request.send() {
        Ok(t) => t,
        Err(e) => {
            return Err(format!("{}", e).to_string());
        }
    };

    match response.validate() {
        Ok(_) => println!("Response valid. Start sending/receiving..."),
        Err(e) => {
            return Err(format!("{}", e).to_string());
        }
    };

    let (sender, mut receiver) = response.begin().split();

    thread::spawn(move || {
        for message in receiver.incoming_messages() {
            let message = match message {
                Ok(m) => m,
                Err(_) => return,
            };
            match message {
                Message::Text(_) => {
                    //let json = Json::from_str(&text).unwrap();
                    //let event: Event = FromJson::from_json(&json);
                },
                Message::Close(_) => {
                    println!("Received close message");
                    return;
                }
                _ => println!("Unknown message: {:?}", message)
            }
        }
    });

    Ok(sender)
}

pub fn send_event(event: Event, sender: &mut client::sender::Sender<stream::WebSocketStream>) {
    let mut e = cbor::Encoder::from_memory();
    let json = event.to_json();
    let cbor = json.to_cbor();
    e.encode(vec![cbor]).unwrap();
    sender.send_message(Message::Binary(e.into_bytes())).unwrap();
}<|endoftext|>