<commit_before>#![allow(warnings)]

use crate::config::Config;
use crate::cpp_checker::cpp_checker_step;
use crate::cpp_data::CppPath;
use crate::cpp_type::CppType;
use crate::database::CppDatabaseItem;
use crate::database::CppFfiItem;
use crate::database::CppItemData;
use crate::database::Database;
use crate::processor::ProcessingStep;
use crate::processor::ProcessorData;
use crate::rust_info::RustDatabase;
use crate::rust_info::RustDatabaseItem;
use crate::rust_info::RustEnumValue;
use crate::rust_info::RustEnumValueDoc;
use crate::rust_info::RustFfiClassTypeDoc;
use crate::rust_info::RustItemKind;
use crate::rust_info::RustModule;
use crate::rust_info::RustModuleDoc;
use crate::rust_info::RustPathScope;
use crate::rust_info::RustStruct;
use crate::rust_info::RustStructKind;
use crate::rust_info::RustWrapperType;
use crate::rust_info::RustWrapperTypeDocData;
use crate::rust_info::RustWrapperTypeKind;
use crate::rust_type::RustPath;
use log::trace;
use ritual_common::errors::*;
use std::collections::HashMap;
use std::collections::HashSet;
use std::iter::once;

/// Adds "_" to a string if it is a reserved word in Rust
fn sanitize_rust_identifier(name: &str) -> String {
    match name {
        "abstract" | "alignof" | "as" | "become" | "box" | "break" | "const" | "continue"
        | "crate" | "do" | "else" | "enum" | "extern" | "false" | "final" | "fn" | "for" | "if"
        | "impl" | "in" | "let" | "loop" | "macro" | "match" | "mod" | "move" | "mut"
        | "offsetof" | "override" | "priv" | "proc" | "pub" | "pure" | "ref" | "return"
        | "Self" | "self" | "sizeof" | "static" | "struct" | "super" | "trait" | "true"
        | "type" | "typeof" | "unsafe" | "unsized" | "use" | "virtual" | "where" | "while"
        | "yield" => format!("{}_", name),
        _ => name.to_string(),
    }
}

#[derive(Debug, Clone, Copy)]
enum NameType {
    General,
    FfiItem,
    SizedItem,
    ClassPtr,
}

struct State<'a> {
    dep_databases: &'a [Database],
    rust_database: &'a mut RustDatabase,
    config: &'a Config,
    cpp_path_to_index: HashMap<CppPath, usize>,
}

impl State<'_> {
    fn get_strategy(&self, parent_path: &CppPath) -> Result<RustPathScope> {
        let index = match self.cpp_path_to_index.get(parent_path) {
            Some(index) => index,
            None => unexpected!("unknown parent path: {}", parent_path),
        };

        let rust_item = self
            .rust_database
            .items
            .iter()
            .find(|item| item.cpp_item_index == *index)
            .ok_or_else(|| err_msg(format!("rust item not found for path: {:?}", parent_path)))?;

        let rust_path = rust_item.path().ok_or_else(|| {
            err_msg(format!(
                "rust item doesn't have rust path (cpp_path = {:?})",
                parent_path
            ))
        })?;

        Ok(RustPathScope {
            path: rust_path.clone(),
            prefix: None,
        })
    }

    fn default_strategy(&self) -> RustPathScope {
        RustPathScope {
            path: RustPath {
                parts: vec![self.config.crate_properties().name().into()],
            },
            prefix: None,
        }
    }

    fn generate_rust_path(&mut self, cpp_path: &CppPath, name_type: NameType) -> Result<RustPath> {
        let strategy = if let Some(parent) = cpp_path.parent() {
            self.get_strategy(&parent)?
        } else {
            self.default_strategy()
        };

        if cpp_path.last().template_arguments.is_some() {
            bail!("naming items with template arguments is not supported");
        }

        let sanitized_name = sanitize_rust_identifier(&cpp_path.last().name);
        let rust_path = strategy.apply(&sanitized_name);
        if let Some(rust_item) = self.rust_database.find(&rust_path) {
            bail!(
                "name {:?} already exists! Rust item: {:?}",
                rust_path,
                rust_item
            );
        }
        // TODO: forbid reserved module names: `lib`, `main`
        // TODO: check for conflicts with things that are not in rust database:
        // - `crate::ffi`
        // - `crate::_types`
        // - types from crate template (how?)
        Ok(rust_path)
    }

    fn generate_rust_items(
        &mut self,
        cpp_item: &mut CppDatabaseItem,
        cpp_item_index: usize,
        modified: &mut bool,
    ) -> Result<()> {
        match &cpp_item.cpp_data {
            CppItemData::Namespace(path) => {
                let rust_path = self.generate_rust_path(path, NameType::General)?;
                let rust_item = RustDatabaseItem {
                    kind: RustItemKind::Module(RustModule {
                        path: rust_path,
                        doc: RustModuleDoc {
                            extra_doc: None,
                            cpp_path: Some(path.clone()),
                        },
                    }),
                    cpp_item_index,
                };
                self.rust_database.items.push(rust_item);
                *modified = true;
                cpp_item.is_rust_processed = true;
            }
            CppItemData::Type(data) => {
                if data.kind.is_class() {
                    let internal_name_type = if data.is_movable {
                        NameType::SizedItem
                    } else {
                        NameType::FfiItem
                    };
                    let public_name_type = if data.is_movable {
                        NameType::General
                    } else {
                        NameType::ClassPtr
                    };
                    let internal_path = self.generate_rust_path(&data.path, internal_name_type)?;
                    let public_path = self.generate_rust_path(&data.path, public_name_type)?;
                    if internal_path == public_path {
                        bail!(
                            "internal path is the same as public path: {:?}",
                            internal_path
                        );
                    }

                    let internal_wrapper_kind = if data.is_movable {
                        RustStructKind::SizedType(data.path.clone())
                    } else {
                        RustStructKind::FfiClassType(RustFfiClassTypeDoc {
                            cpp_path: data.path.clone(),
                            public_rust_path: public_path.clone(),
                        })
                    };

                    let internal_rust_item = RustDatabaseItem {
                        kind: RustItemKind::Struct(RustStruct {
                            extra_doc: None,
                            path: internal_path.clone(),
                            kind: internal_wrapper_kind,
                            is_public: true,
                        }),
                        cpp_item_index,
                    };
                    self.rust_database.items.push(internal_rust_item);

                    let wrapper_kind = if data.is_movable {
                        RustWrapperTypeKind::MovableClassWrapper {
                            sized_type_path: internal_path,
                        }
                    } else {
                        RustWrapperTypeKind::ImmovableClassWrapper {
                            raw_type_path: internal_path,
                        }
                    };

                    let public_rust_item = RustDatabaseItem {
                        kind: RustItemKind::Struct(RustStruct {
                            extra_doc: None,
                            path: public_path,
                            kind: RustStructKind::WrapperType(RustWrapperType {
                                doc_data: RustWrapperTypeDocData {
                                    cpp_path: data.path.clone(),
                                    cpp_doc: data.doc.clone(),
                                    raw_qt_slot_wrapper: None, // TODO: fix this
                                },
                                kind: wrapper_kind,
                            }),
                            is_public: true,
                        }),
                        cpp_item_index,
                    };
                    self.rust_database.items.push(public_rust_item);

                    *modified = true;
                    cpp_item.is_rust_processed = true;
                } else {
                    let rust_path = self.generate_rust_path(&data.path, NameType::General)?;
                    let rust_item = RustDatabaseItem {
                        kind: RustItemKind::Struct(RustStruct {
                            extra_doc: None,
                            path: rust_path,
                            kind: RustStructKind::WrapperType(RustWrapperType {
                                doc_data: RustWrapperTypeDocData {
                                    cpp_path: data.path.clone(),
                                    cpp_doc: data.doc.clone(),
                                    raw_qt_slot_wrapper: None,
                                },
                                kind: RustWrapperTypeKind::EnumWrapper,
                            }),
                            is_public: true,
                        }),
                        cpp_item_index,
                    };
                    self.rust_database.items.push(rust_item);
                    *modified = true;
                    cpp_item.is_rust_processed = true;
                }
            }
            CppItemData::EnumValue(value) => {
                let rust_path = self.generate_rust_path(&value.path, NameType::General)?;

                let rust_item = RustDatabaseItem {
                    kind: RustItemKind::EnumValue(RustEnumValue {
                        path: rust_path,
                        value: value.value,
                        doc: RustEnumValueDoc {
                            cpp_path: value.path.clone(),
                            cpp_doc: value.doc.clone(),
                            extra_doc: None,
                        },
                    }),
                    cpp_item_index,
                };
                self.rust_database.items.push(rust_item);
                *modified = true;
                cpp_item.is_rust_processed = true;
            }
            _ => bail!("unimplemented"),
            /*
            CppItemData::Type(t) => unimplemented!(),
            CppItemData::EnumValue(value) => unimplemented!(),
            _ => unimplemented!(),*/
        }
        Ok(())
    }
}

fn run(data: &mut ProcessorData) -> Result<()> {
    let mut state = State {
        dep_databases: data.dep_databases,
        rust_database: &mut data.current_database.rust_database,
        config: data.config,
        cpp_path_to_index: data
            .current_database
            .cpp_items
            .iter()
            .enumerate()
            .filter_map(|(index, item)| match item.cpp_data {
                CppItemData::Namespace(ref path) => Some((path.clone(), index)),
                CppItemData::Type(ref data) => Some((data.path.clone(), index)),
                _ => None,
            })
            .collect(),
    };
    let cpp_items = &mut data.current_database.cpp_items;

    loop {
        let mut something_changed = false;

        for (index, mut cpp_item) in cpp_items.iter_mut().enumerate() {
            if cpp_item.is_rust_processed {
                continue;
            }

            let _ = state.generate_rust_items(&mut cpp_item, index, &mut something_changed);
        }

        if !something_changed {
            break;
        }
    }

    for (index, mut cpp_item) in cpp_items.iter_mut().enumerate() {
        if cpp_item.is_rust_processed {
            continue;
        }

        let err = state
            .generate_rust_items(&mut cpp_item, index, &mut true)
            .err()
            .expect("previous iteration had no success, so fail is expected");
        trace!("skipping item: {}: {}", &cpp_item.cpp_data, err);
    }
    Ok(())
}

pub fn rust_name_resolver_step() -> ProcessingStep {
    // TODO: set dependencies
    ProcessingStep::new("rust_name_resolver", vec![cpp_checker_step().name], run)
}

pub fn clear_rust_info(data: &mut ProcessorData) -> Result<()> {
    data.current_database.rust_database.items.clear();
    for item in &mut data.current_database.cpp_items {
        item.is_rust_processed = false;
        if let Some(ffi_items) = &mut item.ffi_items {
            for item in ffi_items {
                item.is_rust_processed = false;
            }
        }
    }
    Ok(())
}

pub fn clear_rust_info_step() -> ProcessingStep {
    ProcessingStep::new_custom("clear_rust_info", clear_rust_info)
}
<commit_msg>Smarter name resolver<commit_after>#![allow(warnings)]

use crate::config::Config;
use crate::cpp_checker::cpp_checker_step;
use crate::cpp_data::CppPath;
use crate::cpp_data::CppPathItem;
use crate::cpp_type::CppType;
use crate::database::CppDatabaseItem;
use crate::database::CppFfiItem;
use crate::database::CppItemData;
use crate::database::Database;
use crate::processor::ProcessingStep;
use crate::processor::ProcessorData;
use crate::rust_info::RustDatabase;
use crate::rust_info::RustDatabaseItem;
use crate::rust_info::RustEnumValue;
use crate::rust_info::RustEnumValueDoc;
use crate::rust_info::RustFfiClassTypeDoc;
use crate::rust_info::RustItemKind;
use crate::rust_info::RustModule;
use crate::rust_info::RustModuleDoc;
use crate::rust_info::RustPathScope;
use crate::rust_info::RustStruct;
use crate::rust_info::RustStructKind;
use crate::rust_info::RustWrapperType;
use crate::rust_info::RustWrapperTypeDocData;
use crate::rust_info::RustWrapperTypeKind;
use crate::rust_type::RustPath;
use itertools::Itertools;
use log::trace;
use ritual_common::errors::*;
use ritual_common::utils::MapIfOk;
use std::collections::HashMap;
use std::collections::HashSet;
use std::iter::once;

/// Adds "_" to a string if it is a reserved word in Rust
fn sanitize_rust_identifier(name: &str) -> String {
    match name {
        "abstract" | "alignof" | "as" | "become" | "box" | "break" | "const" | "continue"
        | "crate" | "do" | "else" | "enum" | "extern" | "false" | "final" | "fn" | "for" | "if"
        | "impl" | "in" | "let" | "loop" | "macro" | "match" | "mod" | "move" | "mut"
        | "offsetof" | "override" | "priv" | "proc" | "pub" | "pure" | "ref" | "return"
        | "Self" | "self" | "sizeof" | "static" | "struct" | "super" | "trait" | "true"
        | "type" | "typeof" | "unsafe" | "unsized" | "use" | "virtual" | "where" | "while"
        | "yield" => format!("{}_", name),
        _ => name.to_string(),
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum NameType {
    General,
    FfiItem,
    SizedItem,
    ClassPtr,
}

struct State<'a> {
    dep_databases: &'a [Database],
    rust_database: &'a mut RustDatabase,
    config: &'a Config,
    cpp_path_to_index: HashMap<CppPath, usize>,
}

impl State<'_> {
    fn get_strategy(&self, parent_path: &CppPath) -> Result<RustPathScope> {
        let index = match self.cpp_path_to_index.get(parent_path) {
            Some(index) => index,
            None => unexpected!("unknown parent path: {}", parent_path),
        };

        let rust_item = self
            .rust_database
            .items
            .iter()
            .find(|item| item.cpp_item_index == *index)
            .ok_or_else(|| err_msg(format!("rust item not found for path: {:?}", parent_path)))?;

        let rust_path = rust_item.path().ok_or_else(|| {
            err_msg(format!(
                "rust item doesn't have rust path (cpp_path = {:?})",
                parent_path
            ))
        })?;

        Ok(RustPathScope {
            path: rust_path.clone(),
            prefix: None,
        })
    }

    fn default_strategy(&self) -> RustPathScope {
        RustPathScope {
            path: RustPath {
                parts: vec![self.config.crate_properties().name().into()],
            },
            prefix: None,
        }
    }

    fn generate_rust_path(&mut self, cpp_path: &CppPath, name_type: NameType) -> Result<RustPath> {
        let strategy = match name_type {
            NameType::FfiItem => RustPathScope {
                path: RustPath {
                    parts: vec![self.config.crate_properties().name().into(), "ffi".into()],
                },
                prefix: None,
            },
            NameType::SizedItem => RustPathScope {
                path: RustPath {
                    parts: vec![
                        self.config.crate_properties().name().into(),
                        "ffi".into(),
                        "sized_types".into(),
                    ],
                },
                prefix: None,
            },
            NameType::General | NameType::ClassPtr => {
                if let Some(parent) = cpp_path.parent() {
                    self.get_strategy(&parent)?
                } else {
                    self.default_strategy()
                }
            }
        };

        let cpp_path_item_to_name = |item: &CppPathItem| {
            if item.template_arguments.is_some() {
                bail!("naming items with template arguments is not supported");
            }
            Ok(item.name.clone())
        };

        let namespaced_name = if name_type == NameType::FfiItem || name_type == NameType::SizedItem
        {
            cpp_path
                .items
                .iter()
                .map_if_ok(|item| cpp_path_item_to_name(item))?
                .join("_")
        } else {
            cpp_path_item_to_name(&cpp_path.last())?
        };

        let full_name = if name_type == NameType::ClassPtr {
            format!("{}Ptr", namespaced_name)
        } else {
            namespaced_name
        };

        let mut number = None;
        loop {
            let name_try = match number {
                None => full_name.clone(),
                Some(n) => format!("{}{}", full_name, n),
            };
            let sanitized_name = sanitize_rust_identifier(&name_try);
            let rust_path = strategy.apply(&sanitized_name);
            if self.rust_database.find(&rust_path).is_none() {
                return Ok(rust_path);
            }

            number = Some(number.unwrap_or(0) + 1);
        }
        // TODO: forbid reserved module names: `lib`, `main`
        // TODO: check for conflicts with things that are not in rust database:
        // - `crate::ffi`
        // - `crate::_types`
        // - types from crate template (how?)
    }

    fn generate_rust_items(
        &mut self,
        cpp_item: &mut CppDatabaseItem,
        cpp_item_index: usize,
        modified: &mut bool,
    ) -> Result<()> {
        match &cpp_item.cpp_data {
            CppItemData::Namespace(path) => {
                let rust_path = self.generate_rust_path(path, NameType::General)?;
                let rust_item = RustDatabaseItem {
                    kind: RustItemKind::Module(RustModule {
                        path: rust_path,
                        doc: RustModuleDoc {
                            extra_doc: None,
                            cpp_path: Some(path.clone()),
                        },
                    }),
                    cpp_item_index,
                };
                self.rust_database.items.push(rust_item);
                *modified = true;
                cpp_item.is_rust_processed = true;
            }
            CppItemData::Type(data) => {
                if data.kind.is_class() {
                    let internal_name_type = if data.is_movable {
                        NameType::SizedItem
                    } else {
                        NameType::FfiItem
                    };
                    let public_name_type = if data.is_movable {
                        NameType::General
                    } else {
                        NameType::ClassPtr
                    };
                    let internal_path = self.generate_rust_path(&data.path, internal_name_type)?;
                    let public_path = self.generate_rust_path(&data.path, public_name_type)?;
                    if internal_path == public_path {
                        bail!(
                            "internal path is the same as public path: {:?}",
                            internal_path
                        );
                    }

                    let internal_wrapper_kind = if data.is_movable {
                        RustStructKind::SizedType(data.path.clone())
                    } else {
                        RustStructKind::FfiClassType(RustFfiClassTypeDoc {
                            cpp_path: data.path.clone(),
                            public_rust_path: public_path.clone(),
                        })
                    };

                    let internal_rust_item = RustDatabaseItem {
                        kind: RustItemKind::Struct(RustStruct {
                            extra_doc: None,
                            path: internal_path.clone(),
                            kind: internal_wrapper_kind,
                            is_public: true,
                        }),
                        cpp_item_index,
                    };
                    self.rust_database.items.push(internal_rust_item);

                    let wrapper_kind = if data.is_movable {
                        RustWrapperTypeKind::MovableClassWrapper {
                            sized_type_path: internal_path,
                        }
                    } else {
                        RustWrapperTypeKind::ImmovableClassWrapper {
                            raw_type_path: internal_path,
                        }
                    };

                    let public_rust_item = RustDatabaseItem {
                        kind: RustItemKind::Struct(RustStruct {
                            extra_doc: None,
                            path: public_path,
                            kind: RustStructKind::WrapperType(RustWrapperType {
                                doc_data: RustWrapperTypeDocData {
                                    cpp_path: data.path.clone(),
                                    cpp_doc: data.doc.clone(),
                                    raw_qt_slot_wrapper: None, // TODO: fix this
                                },
                                kind: wrapper_kind,
                            }),
                            is_public: true,
                        }),
                        cpp_item_index,
                    };
                    self.rust_database.items.push(public_rust_item);

                    *modified = true;
                    cpp_item.is_rust_processed = true;
                } else {
                    let rust_path = self.generate_rust_path(&data.path, NameType::General)?;
                    let rust_item = RustDatabaseItem {
                        kind: RustItemKind::Struct(RustStruct {
                            extra_doc: None,
                            path: rust_path,
                            kind: RustStructKind::WrapperType(RustWrapperType {
                                doc_data: RustWrapperTypeDocData {
                                    cpp_path: data.path.clone(),
                                    cpp_doc: data.doc.clone(),
                                    raw_qt_slot_wrapper: None,
                                },
                                kind: RustWrapperTypeKind::EnumWrapper,
                            }),
                            is_public: true,
                        }),
                        cpp_item_index,
                    };
                    self.rust_database.items.push(rust_item);
                    *modified = true;
                    cpp_item.is_rust_processed = true;
                }
            }
            CppItemData::EnumValue(value) => {
                let rust_path = self.generate_rust_path(&value.path, NameType::General)?;

                let rust_item = RustDatabaseItem {
                    kind: RustItemKind::EnumValue(RustEnumValue {
                        path: rust_path,
                        value: value.value,
                        doc: RustEnumValueDoc {
                            cpp_path: value.path.clone(),
                            cpp_doc: value.doc.clone(),
                            extra_doc: None,
                        },
                    }),
                    cpp_item_index,
                };
                self.rust_database.items.push(rust_item);
                *modified = true;
                cpp_item.is_rust_processed = true;
            }
            _ => bail!("unimplemented"),
            /*
            CppItemData::Type(t) => unimplemented!(),
            CppItemData::EnumValue(value) => unimplemented!(),
            _ => unimplemented!(),*/
        }
        Ok(())
    }
}

fn run(data: &mut ProcessorData) -> Result<()> {
    let mut state = State {
        dep_databases: data.dep_databases,
        rust_database: &mut data.current_database.rust_database,
        config: data.config,
        cpp_path_to_index: data
            .current_database
            .cpp_items
            .iter()
            .enumerate()
            .filter_map(|(index, item)| match item.cpp_data {
                CppItemData::Namespace(ref path) => Some((path.clone(), index)),
                CppItemData::Type(ref data) => Some((data.path.clone(), index)),
                _ => None,
            })
            .collect(),
    };
    let cpp_items = &mut data.current_database.cpp_items;

    loop {
        let mut something_changed = false;

        for (index, mut cpp_item) in cpp_items.iter_mut().enumerate() {
            if cpp_item.is_rust_processed {
                continue;
            }

            let _ = state.generate_rust_items(&mut cpp_item, index, &mut something_changed);
        }

        if !something_changed {
            break;
        }
    }

    for (index, mut cpp_item) in cpp_items.iter_mut().enumerate() {
        if cpp_item.is_rust_processed {
            continue;
        }

        let err = state
            .generate_rust_items(&mut cpp_item, index, &mut true)
            .err()
            .expect("previous iteration had no success, so fail is expected");
        trace!("skipping item: {}: {}", &cpp_item.cpp_data, err);
    }
    Ok(())
}

pub fn rust_name_resolver_step() -> ProcessingStep {
    // TODO: set dependencies
    ProcessingStep::new("rust_name_resolver", vec![cpp_checker_step().name], run)
}

pub fn clear_rust_info(data: &mut ProcessorData) -> Result<()> {
    data.current_database.rust_database.items.clear();
    for item in &mut data.current_database.cpp_items {
        item.is_rust_processed = false;
        if let Some(ffi_items) = &mut item.ffi_items {
            for item in ffi_items {
                item.is_rust_processed = false;
            }
        }
    }
    Ok(())
}

pub fn clear_rust_info_step() -> ProcessingStep {
    ProcessingStep::new_custom("clear_rust_info", clear_rust_info)
}
<|endoftext|>


<commit_before>//! Lua functionality

use hlua;
use hlua::{Lua, LuaError, LuaTable, AnyLuaValue};

use rustc_serialize::json::Json;

use std::thread;
use std::fs::{File};
use std::path::Path;
use std::io::Write;
use std::fmt::{Debug, Formatter};
use std::fmt::Result as FmtResult;

use std::sync::{Mutex, RwLock};
use std::sync::mpsc::{channel, Sender, Receiver};

#[macro_use]
mod funcs;
#[cfg(test)]
mod tests;

lazy_static! {
    /// Sends requests to the lua thread
    static ref SENDER: Mutex<Option<Sender<LuaMessage>>> = Mutex::new(None);

    /// Whether the lua thread is currently running
    pub static ref RUNNING: RwLock<bool> = RwLock::new(false);
}

/// Represents an identifier for dealing with nested tables.
///
/// To access foo.bar.baz, use vec!["foo", "bar", "baz"].
///
/// To access foo[2], use vec!["foo", 2].
pub type LuaIdentifier = Vec<AnyLuaValue>;

/// Messages sent to the lua thread
#[derive(Debug)]
pub enum LuaQuery {
    /// Pings the lua thread
    Ping,
    /// Halt the lua thread
    Terminate,
    // Restart the lua thread
    Restart,

    /// Execute a string
    Execute(String),
    /// Execute a file
    ExecFile(String),

    /// Get a variable, expecting an AnyLuaValue
    GetValue(LuaIdentifier),
    /// Invoke a function found at the position,
    /// with the specified arguments.
    Invoke(LuaIdentifier, Vec<AnyLuaValue>),
    /// Set a value
    SetValue {
        /// The name of the thing to stuff
        name: LuaIdentifier,
        /// The value to store.
        val: Json
    },
    /// Create a new table
    NewTable(LuaIdentifier),
}

/// Messages received from lua thread
pub enum LuaResponse {
    /// If the identifier had length 0
    InvalidName,
    /// Lua variable obtained
    Variable(Option<AnyLuaValue>),
    /// Lua error
    Error(hlua::LuaError),
    /// A function is returned
    Function(hlua::functions_read::LuaFunction<String>),
    /// Pong response from lua ping
    Pong,
}

/// Struct sent to the lua query
struct LuaMessage {
    reply: Sender<LuaResponse>,
    query: LuaQuery
}

unsafe impl Send for LuaQuery { }
unsafe impl Sync for LuaQuery { }
unsafe impl Send for LuaResponse { }
unsafe impl Sync for LuaResponse { }
unsafe impl Send for LuaMessage { }
unsafe impl Sync for LuaMessage { }

impl Debug for LuaResponse {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            LuaResponse::Variable(ref var) =>
                write!(f, "LuaResponse::Variable({:?})", var),
            LuaResponse::Error(ref err) =>
                write!(f, "LuaResponse::Error({:?})", err),
            LuaResponse::Function(_) =>
                write!(f, "LuaResponse::Function"),
            LuaResponse::Pong =>
                write!(f, "LuaResponse::Pong")
        }
    }
}

impl Debug for LuaMessage {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "LuaMessage({:?})", self.query)
    }
}

/// Whether the lua thread is currently available
pub fn thread_running() -> bool {
    *RUNNING.read().unwrap()
}

/// Errors which may arise from attempting
/// to sending a message to the lua thread.
#[derive(Debug)]
pub enum LuaSendError {
    /// The thread crashed, was shut down, or rebooted.
    ThreadClosed,
    /// The thread has not been initialized yet (maybe not used)
    ThreadUninitialized,
    /// The sender had an issue, most likey because the thread panicked.
    /// Following the `Sender` API, the original value sent is returned.
    Sender(LuaQuery)
}

/// Attemps to send a LuaQuery to the lua thread.
pub fn try_send(query: LuaQuery) -> Result<Receiver<LuaResponse>,LuaSendError> {
    if !thread_running() {
        Err(LuaSendError::ThreadClosed)
    }
    else if let Some(sender) = SENDER.lock().unwrap().clone() {
        let (tx, rx) = channel();
        let message = LuaMessage { reply: tx, query: query };
        match sender.send(message) {
            Ok(_) => Ok(rx),
            Err(e) => Err(LuaSendError::Sender(e.0.query))
        }
    }
    else {
        Err(LuaSendError::ThreadUninitialized)
    }
}

/// Initialize the lua thread
pub fn init() {
    trace!("Initializing...");
    let (query_tx, query_rx) = channel::<LuaMessage>();
    {
        let mut sender = SENDER.lock().unwrap();
        *sender = Some(query_tx);
    }

    thread::spawn(move || {
        thread_init(query_rx);
    });
    trace!("Created thread. Init finished.");
}

fn thread_init(receiver: Receiver<LuaMessage>) {
    trace!("thread: initializing.");
    let mut lua = Lua::new();
    //unsafe {
    //    hlua_ffi::lua_atpanic(&mut lua.as_mut_lua().0, thread_on_panic);
    //}
    debug!("thread: Loading Lua libraries...");
    lua.openlibs();
    trace!("thread: Loading way-cooler lua extensions...");
    // We should have some good file handling, read files from /usr by default,
    // but for now we're reading directly from the source.
    lua.execute_from_reader::<(), File>(
        File::open("lib/lua/init.lua").unwrap()
    ).unwrap();
    trace!("thread: loading way-cooler libraries...");
    funcs::register_libraries(&mut lua);
    // Only ready after loading libs
    *RUNNING.write().unwrap() = true;
    debug!("thread: entering main loop...");
    thread_main_loop(receiver, &mut lua);
}

fn thread_main_loop(receiver: Receiver<LuaMessage>, lua: &mut Lua) {
    loop {
        let request = receiver.recv();
        match request {
            Err(e) => {
                error!("Lua thread: unable to receive message: {}", e);
                error!("Lua thread: now panicking!");
                *RUNNING.write().unwrap() = false;

                panic!("Lua thread: lost contact with host, exiting!");
            }
            Ok(message) => {
                trace!("Handling a request");
                thread_handle_message(message, lua);
            }
        }
    }
}

fn thread_handle_message(request: LuaMessage, lua: &mut Lua) {
    match request.query {
        LuaQuery::Terminate => {
            trace!("thread: Received terminate signal");
            *RUNNING.write().unwrap() = false;

            info!("thread: Lua thread terminating!");
            thread_send(request.reply, LuaResponse::Pong);
            return;
        },

        LuaQuery::Restart => {
            trace!("thread: Received restart signal!");
            error!("thread: Lua thread restart not supported!");

            *RUNNING.write().unwrap() = false;
            thread_send(request.reply, LuaResponse::Pong);

            panic!("Lua thread: Restart not supported!");
        },

        LuaQuery::Execute(code) => {
            trace!("thread: Received request to execute code");
            trace!("thread: Executing {}", code);

            match lua.execute::<()>(&code) {
                Err(error) => {
                    warn!("thread: Error executing code: {:?}", error);
                    thread_send(request.reply, LuaResponse::Error(error));
                }
                Ok(_) => {
                    trace!("thread: Code executed okay.");
                    thread_send(request.reply, LuaResponse::Pong);
                }
            }
        },

        LuaQuery::ExecFile(name) => {
            info!("thread: Executing {}", name);

            let path = Path::new(&name);
            let try_file = File::open(path);

            if let Ok(file) = try_file {
                let result = lua.execute_from_reader::<(), File>(file);
                if let Err(err) = result {
                    warn!("thread: Error executing {}!", name);

                    thread_send(request.reply, LuaResponse::Error(err));
                }
                else {
                    trace!("thread: Execution of {} successful.", name);
                    thread_send(request.reply, LuaResponse::Pong);
                }
            }
            else { // Could not open file
                // Unwrap_err is used because we're in the else of let
                let read_error =
                    LuaError::ReadError(try_file.unwrap_err());

                thread_send(request.reply, LuaResponse::Error(read_error));
            }
        },

        LuaQuery::GetValue(varname) => {
            trace!("thread: Received request to get variable {:?}", varname);

            if varname.len() == 0 {
                thread_send(request.reply, LuaResponse::Variable(None));
            }
            let mut maybe_table = lua.get(varname[0]);
            if varname.len() == 1 {
                thread_send(request.reply,
                    LuaResponse::Variable(table));
            }
            for name in varname {

            }
        },

        LuaQuery::SetValue { name: _name, val: _val } => {
            panic!("thread: unimplemented LuaQuery::SetValue!");
        },

        LuaQuery::NewTable(name_list) => {
            if name_list.len() == 0 {
                thread_send(request.reply, LuaResponse::Error(
                    LuaError::SyntaxError("Name cannot be empty")));
            }
            else {
                let mut curr_table = lua.empty_array(name_list[0]);
                for name in name_list.into_iter().skip(1) {
                    curr_table = lua.empty_array(name);
                }
                thread_send(request.reply, LuaResponse::Pong);
            }
        },

        LuaQuery::Ping => {
            thread_send(request.reply, LuaResponse::Pong);
        },
        _ => {
            panic!("Unimplemented send type for lua thread!");
        }
    }
}

fn thread_send(sender: Sender<LuaResponse>, response: LuaResponse) {
    match sender.send(response) {
        Err(err) => {
            match err.0 {
                LuaResponse::Pong => {}, // Those are boring
                _ => {
                    warn!("thread: Someone ignored an important Lua response!");
                }
            }
        }
        Ok(_) => {}
    }
}

fn get_name_table<'a, 'b>(lua: &mut Lua<'b>, names: LuaIdentifier)
                          -> LuaTable<PushGuard<&'a mut Lua<'b>>> {
    if names.len == 0 {

    }
}
<commit_msg>Fix build, add walk_name for nested lua values<commit_after>//! Lua functionality

use hlua;
use hlua::{Lua, LuaError, LuaTable};
use hlua::any::AnyLuaValue;

use rustc_serialize::json::Json;

use std::thread;
use std::fs::{File};
use std::path::Path;
use std::io::Write;
use std::fmt::{Debug, Formatter};
use std::fmt::Result as FmtResult;
use std::borrow::Borrow;

use std::sync::{Mutex, RwLock};
use std::sync::mpsc::{channel, Sender, Receiver};

#[macro_use]
mod funcs;
#[cfg(test)]
mod tests;

lazy_static! {
    /// Sends requests to the lua thread
    static ref SENDER: Mutex<Option<Sender<LuaMessage>>> = Mutex::new(None);

    /// Whether the lua thread is currently running
    pub static ref RUNNING: RwLock<bool> = RwLock::new(false);
}

/// Represents an identifier for dealing with nested tables.
///
/// To access foo.bar.baz, use vec!["foo", "bar", "baz"].
///
/// To access foo[2], use vec!["foo", 2].
pub type LuaIdent = Vec<String>;

/// Messages sent to the lua thread
#[derive(Debug)]
pub enum LuaQuery {
    /// Pings the lua thread
    Ping,
    /// Halt the lua thread
    Terminate,
    // Restart the lua thread
    Restart,

    /// Execute a string
    Execute(String),
    /// Execute a file
    ExecFile(String),

    /// Get a variable, expecting an AnyLuaValue
    GetValue(LuaIdent),
    /// Invoke a function found at the position,
    /// with the specified arguments.
    Invoke(LuaIdent, Vec<AnyLuaValue>),
    /// Set a value
    SetValue {
        /// The name of the thing to stuff
        name: LuaIdent,
        /// The value to store.
        val: Json
    },
    /// Create a new table
    NewTable(LuaIdent),
}

/// Messages received from lua thread
pub enum LuaResponse {
    /// If the identifier had length 0
    InvalidName,
    /// Lua variable obtained
    Variable(Option<AnyLuaValue>),
    /// Lua error
    Error(hlua::LuaError),
    /// A function is returned
    Function(hlua::functions_read::LuaFunction<String>),
    /// Pong response from lua ping
    Pong,
}

/// Struct sent to the lua query
struct LuaMessage {
    reply: Sender<LuaResponse>,
    query: LuaQuery
}

unsafe impl Send for LuaQuery { }
unsafe impl Sync for LuaQuery { }
unsafe impl Send for LuaResponse { }
unsafe impl Sync for LuaResponse { }
unsafe impl Send for LuaMessage { }
unsafe impl Sync for LuaMessage { }

impl Debug for LuaResponse {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            LuaResponse::InvalidName =>
                write!(f, "LuaResponse::InvalidName"),
            LuaResponse::Variable(ref var) =>
                write!(f, "LuaResponse::Variable({:?})", var),
            LuaResponse::Error(ref err) =>
                write!(f, "LuaResponse::Error({:?})", err),
            LuaResponse::Function(_) =>
                write!(f, "LuaResponse::Function"),
            LuaResponse::Pong =>
                write!(f, "LuaResponse::Pong")
        }
    }
}

impl Debug for LuaMessage {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "LuaMessage({:?})", self.query)
    }
}

/// Whether the lua thread is currently available
pub fn thread_running() -> bool {
    *RUNNING.read().unwrap()
}

/// Errors which may arise from attempting
/// to sending a message to the lua thread.
#[derive(Debug)]
pub enum LuaSendError {
    /// The thread crashed, was shut down, or rebooted.
    ThreadClosed,
    /// The thread has not been initialized yet (maybe not used)
    ThreadUninitialized,
    /// The sender had an issue, most likey because the thread panicked.
    /// Following the `Sender` API, the original value sent is returned.
    Sender(LuaQuery)
}

/// Attemps to send a LuaQuery to the lua thread.
pub fn try_send(query: LuaQuery) -> Result<Receiver<LuaResponse>,LuaSendError> {
    if !thread_running() {
        Err(LuaSendError::ThreadClosed)
    }
    else if let Some(sender) = SENDER.lock().unwrap().clone() {
        let (tx, rx) = channel();
        let message = LuaMessage { reply: tx, query: query };
        match sender.send(message) {
            Ok(_) => Ok(rx),
            Err(e) => Err(LuaSendError::Sender(e.0.query))
        }
    }
    else {
        Err(LuaSendError::ThreadUninitialized)
    }
}

/// Initialize the lua thread
pub fn init() {
    trace!("Initializing...");
    let (query_tx, query_rx) = channel::<LuaMessage>();
    {
        let mut sender = SENDER.lock().unwrap();
        *sender = Some(query_tx);
    }

    thread::spawn(move || {
        thread_init(query_rx);
    });
    trace!("Created thread. Init finished.");
}

fn thread_init(receiver: Receiver<LuaMessage>) {
    trace!("thread: initializing.");
    let mut lua = Lua::new();
    //unsafe {
    //    hlua_ffi::lua_atpanic(&mut lua.as_mut_lua().0, thread_on_panic);
    //}
    debug!("thread: Loading Lua libraries...");
    lua.openlibs();
    trace!("thread: Loading way-cooler lua extensions...");
    // We should have some good file handling, read files from /usr by default,
    // but for now we're reading directly from the source.
    lua.execute_from_reader::<(), File>(
        File::open("lib/lua/init.lua").unwrap()
    ).unwrap();
    trace!("thread: loading way-cooler libraries...");
    funcs::register_libraries(&mut lua);
    // Only ready after loading libs
    *RUNNING.write().unwrap() = true;
    debug!("thread: entering main loop...");
    thread_main_loop(receiver, &mut lua);
}

fn thread_main_loop(receiver: Receiver<LuaMessage>, lua: &mut Lua) {
    loop {
        let request = receiver.recv();
        match request {
            Err(e) => {
                error!("Lua thread: unable to receive message: {}", e);
                error!("Lua thread: now panicking!");
                *RUNNING.write().unwrap() = false;

                panic!("Lua thread: lost contact with host, exiting!");
            }
            Ok(message) => {
                trace!("Handling a request");
                thread_handle_message(message, lua);
            }
        }
    }
}

fn thread_handle_message(request: LuaMessage, lua: &mut Lua) {
    match request.query {
        LuaQuery::Terminate => {
            trace!("thread: Received terminate signal");
            *RUNNING.write().unwrap() = false;

            info!("thread: Lua thread terminating!");
            thread_send(request.reply, LuaResponse::Pong);
            return;
        },

        LuaQuery::Restart => {
            trace!("thread: Received restart signal!");
            error!("thread: Lua thread restart not supported!");

            *RUNNING.write().unwrap() = false;
            thread_send(request.reply, LuaResponse::Pong);

            panic!("Lua thread: Restart not supported!");
        },

        LuaQuery::Execute(code) => {
            trace!("thread: Received request to execute code");
            trace!("thread: Executing {}", code);

            match lua.execute::<()>(&code) {
                Err(error) => {
                    warn!("thread: Error executing code: {:?}", error);
                    thread_send(request.reply, LuaResponse::Error(error));
                }
                Ok(_) => {
                    trace!("thread: Code executed okay.");
                    thread_send(request.reply, LuaResponse::Pong);
                }
            }
        },

        LuaQuery::ExecFile(name) => {
            info!("thread: Executing {}", name);

            let path = Path::new(&name);
            let try_file = File::open(path);

            if let Ok(file) = try_file {
                let result = lua.execute_from_reader::<(), File>(file);
                if let Err(err) = result {
                    warn!("thread: Error executing {}!", name);

                    thread_send(request.reply, LuaResponse::Error(err));
                }
                else {
                    trace!("thread: Execution of {} successful.", name);
                    thread_send(request.reply, LuaResponse::Pong);
                }
            }
            else { // Could not open file
                // Unwrap_err is used because we're in the else of let
                let read_error =
                    LuaError::ReadError(try_file.unwrap_err());

                thread_send(request.reply, LuaResponse::Error(read_error));
            }
        },

        LuaQuery::GetValue(varname) => {
            trace!("thread: Received request to get variable {:?}", varname);

            if varname.len() == 0 {
                thread_send(request.reply, LuaResponse::InvalidName);
                return;
            }
            // Table[0] String had to be cloned, it'd be nice if Rust let us
            // borrow out parts of memory
            match lua.get::<AnyLuaValue, _>(varname[0].clone()) {
                Some(mut table) => {
                    let full_table = walk_table(table, &varname[1..]);
                    thread_send(request.reply,
                                LuaResponse::Variable(full_table));
                },
                None => thread_send(request.reply, LuaResponse::Variable(None))
            }
        },

        LuaQuery::SetValue { name: _name, val: _val } => {
            panic!("thread: unimplemented LuaQuery::SetValue!");
        },

        LuaQuery::NewTable(name_list) => {
            /*
            if name_list.len() == 0 {
                thread_send(request.reply, LuaResponse::Error(
                    LuaError::SyntaxError("Name cannot be empty".to_string())));
            }
            else {
                let mut curr_table = lua.empty_array(name_list[0]);
                for name in name_list.into_iter().skip(1) {
                    curr_table = lua.empty_array(name);
                }
                thread_send(request.reply, LuaResponse::Pong);
            }
            */
        },

        LuaQuery::Ping => {
            thread_send(request.reply, LuaResponse::Pong);
        },
        _ => {
            panic!("Unimplemented send type for lua thread!");
        }
    }
}

fn thread_send(sender: Sender<LuaResponse>, response: LuaResponse) {
    match sender.send(response) {
        Err(err) => {
            match err.0 {
                LuaResponse::Pong => {}, // Those are boring
                _ => {
                    warn!("thread: Someone ignored an important Lua response!");
                }
            }
        }
        Ok(_) => {}
    }
}

fn walk_table(mut table: AnyLuaValue, names: &[String]) -> Option<AnyLuaValue> {
    if let Some(name) = names.first() {
        if let AnyLuaValue::LuaArray(mut arr) = table {
            for (mut key, mut val) in arr {
                if let AnyLuaValue::LuaString(key_str) = key {
                    if *key_str == *name {
                        return walk_table(val, &names[1..]);
                    }
                }
            }
            return None;
        }
        else {
            return None;
        }
    }
    else {
        return Some(table); // ???
    }
}
<|endoftext|>


<commit_before>
<commit_msg>acronym: Added template to stub file<commit_after>pub fn abbreviate(phrase: &str) -> String {
    unimplemented!("Given the phrase '{}', return it's acronym", phrase);
}
<|endoftext|>


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

use futures::{future, unsync, Future, IntoFuture, Sink, Stream};
use futures::future::Either;
use futures::sync::mpsc;
use tokio_core::net::{TcpListener, TcpStream, TcpStreamNew};
use tokio_core::reactor::Handle;
use tokio_io::AsyncRead;
use tokio_retry::{Action, Retry};
use tokio_retry::strategy::{jitter, FixedInterval};

use std::net::SocketAddr;
use std::time::Duration;
use std::collections::HashMap;
use std::rc::Rc;
use std::cell::RefCell;

use messages::{Any, Connect, RawMessage};
use node::{ExternalMessage, NodeTimeout};
use helpers::Milliseconds;

use super::error::{into_other, log_error, other_error, result_ok};
use super::codec::MessagesCodec;
use super::{EventsAggregator, EventHandler};

#[derive(Debug)]
pub enum NetworkEvent {
    MessageReceived(SocketAddr, RawMessage),
    PeerConnected(SocketAddr, Connect),
    PeerDisconnected(SocketAddr),
}

#[derive(Debug, Clone)]
pub enum NetworkRequest {
    SendMessage(SocketAddr, RawMessage),
    DisconnectWithPeer(SocketAddr),
    Shutdown,
}

#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub struct NetworkConfiguration {
    // TODO: think more about config parameters
    pub max_incoming_connections: usize,
    pub max_outgoing_connections: usize,
    pub tcp_nodelay: bool,
    pub tcp_keep_alive: Option<u64>,
    pub tcp_connect_retry_timeout: Milliseconds,
    pub tcp_connect_max_retries: u64,
}

impl Default for NetworkConfiguration {
    fn default() -> NetworkConfiguration {
        NetworkConfiguration {
            max_incoming_connections: 128,
            max_outgoing_connections: 128,
            tcp_keep_alive: None,
            tcp_nodelay: false,
            tcp_connect_retry_timeout: 15_000,
            tcp_connect_max_retries: 10,
        }
    }
}

#[derive(Debug)]
pub struct HandlerPart<H: EventHandler> {
    pub handler: H,
    pub timeout_rx: mpsc::Receiver<NodeTimeout>,
    pub network_rx: mpsc::Receiver<NetworkEvent>,
    pub api_rx: mpsc::Receiver<ExternalMessage>,
}

#[derive(Debug)]
pub struct NetworkPart {
    pub listen_address: SocketAddr,
    pub network_config: NetworkConfiguration,
    pub network_requests: (mpsc::Sender<NetworkRequest>, mpsc::Receiver<NetworkRequest>),
    pub network_tx: mpsc::Sender<NetworkEvent>,
}

#[derive(Debug, Default, Clone)]
struct ConnectionsPool {
    inner: Rc<RefCell<HashMap<SocketAddr, mpsc::Sender<RawMessage>>>>,
}

impl ConnectionsPool {
    fn new() -> ConnectionsPool {
        ConnectionsPool::default()
    }

    fn insert(&self, peer: SocketAddr, sender: &mpsc::Sender<RawMessage>) {
        self.inner.borrow_mut().insert(peer, sender.clone());
    }

    fn remove(&self, peer: &SocketAddr) {
        self.inner.borrow_mut().remove(peer);
    }

    fn get(&self, peer: SocketAddr) -> Option<mpsc::Sender<RawMessage>> {
        self.inner.borrow_mut().get(&peer).cloned()
    }

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

impl<H: EventHandler + 'static> HandlerPart<H> {
    pub fn run(self) -> Box<Future<Item=(), Error=()>> {
        let mut handler = self.handler;

        let fut = EventsAggregator::new(self.timeout_rx, self.network_rx, self.api_rx)
            .for_each(move |event| {
                handler.handle_event(event);
                Ok(())
            });

        Box::new(fut)
    }
}

impl NetworkPart {
    pub fn run(self, handle_orig: Handle) -> Box<Future<Item=(), Error=()>> {
        let network_config = self.network_config;
        // Cancelation token
        let (cancel_sender, cancel_handler) = unsync::oneshot::channel();
        let mut cancel_sender = Some(cancel_sender);
        // Outgoing connections
        let outgoing_connections = ConnectionsPool::new();
        let network_tx = self.network_tx.clone();
        // Outgoing connections limiter
        let outgoing_connections_limit = network_config.max_outgoing_connections;
        let handle = handle_orig.clone();
        let requests_handle = self.network_requests.1.for_each(move |request| {
            let handle = handle.clone();
            let network_tx = network_tx.clone();
            let outgoing_connections = outgoing_connections.clone();
            match request {
                NetworkRequest::SendMessage(peer, msg) => {
                    let conn_tx = if let Some(conn_tx) = outgoing_connections.get(peer) {
                        conn_tx
                    } else {
                        // Check limit
                        if outgoing_connections.len() >= outgoing_connections_limit {
                            warn!(
                                "Rejected outgoing connection with peer={}, \
                                 connections limit reached.",
                                peer
                            );
                            Self::send_event(
                                &handle,
                                &network_tx,
                                NetworkEvent::PeerDisconnected(peer),
                            );
                            return Ok(());
                        }
                        // Register outgoing channel.
                        let (conn_tx, conn_rx) = mpsc::channel(10);
                        outgoing_connections.insert(peer, &conn_tx);
                        // Enable retry feature for outgoing connection.
                        let timeout = network_config.tcp_connect_retry_timeout;
                        let max_tries = network_config.tcp_connect_max_retries as usize;
                        let connect_handle = Retry::spawn(
                            handle.clone(),
                            FixedInterval::from_millis(timeout).map(jitter).take(
                                max_tries,
                            ),
                            TcpStreamConnectAction {
                                handle: handle.clone(),
                                peer,
                            },
                        ).map_err(into_other)
                            // Configure socket
                            .and_then(move |sock| {
                                sock.set_nodelay(network_config.tcp_nodelay)?;
                                Ok(sock)
                            })
                            .and_then(move |sock| {
                                let duration =
                                    network_config.tcp_keep_alive.map(Duration::from_millis);
                                sock.set_keepalive(duration)?;
                                Ok(sock)
                            })
                            // Connect socket with the outgoing channel
                            .and_then(move |sock| {
                                trace!("Established connection with peer={}", peer);

                                let stream = sock.framed(MessagesCodec);
                                let (sink, stream) = stream.split();

                                let writer = conn_rx
                                    .map_err(|_| other_error("Can't send data into socket"))
                                    .forward(sink);
                                let reader = stream.for_each(result_ok);

                                reader
                                    .select2(writer)
                                    .map_err(|_| other_error("Socket error"))
                                    .and_then(|res| match res {
                                        Either::A((_, _reader)) => Ok(()).into_future(),
                                        Either::B((_, _writer)) => Ok(()).into_future(),
                                    })
                            })
                            .then(move |res| {
                                trace!("Connection with peer={} closed, reason={:?}", peer, res);
                                outgoing_connections.clone().remove(&peer);
                                network_tx
                                    .clone()
                                    .send(NetworkEvent::PeerDisconnected(peer))
                                    .map(drop)
                            })
                            .map_err(log_error);
                        handle.spawn(connect_handle);
                        conn_tx
                    };

                    let send_handle = conn_tx.send(msg).map_err(log_error).map(drop);
                    handle.spawn(send_handle);
                }
                NetworkRequest::DisconnectWithPeer(peer) => {
                    outgoing_connections.remove(&peer);
                    Self::send_event(&handle, &network_tx, NetworkEvent::PeerDisconnected(peer));
                }
                // Immediately stop the event loop.
                NetworkRequest::Shutdown => {
                    cancel_sender
                        .take()
                        .ok_or_else(|| other_error("Shutdown twice"))
                        .and_then(|sender| {
                            sender.send(()).map_err(
                                |_| other_error("Can't send shutdown signal"),
                            )
                        })
                        .map_err(log_error)?
                }
            }

            Ok(())
        });

        // Incoming connections limiter
        let incoming_connections_limit = network_config.max_incoming_connections;
        let incoming_connections_counter: Rc<()> = Rc::default();
        // Incoming connections handler
        // TODO Don't use unwrap here!
        let listener = TcpListener::bind(&self.listen_address, &handle_orig).unwrap();
        let network_tx = self.network_tx.clone();
        let handle = handle_orig.clone();
        let server = listener
            .incoming()
            .for_each(move |(sock, addr)| {
                let handle = handle.clone();
                // Increment reference counter
                let holder = Rc::downgrade(&incoming_connections_counter);
                // Check incoming connections count
                let connections_count = Rc::weak_count(&incoming_connections_counter);
                if connections_count > incoming_connections_limit {
                    warn!(
                        "Rejected incoming connection with peer={}, \
                         connections limit reached.",
                        addr
                    );
                    return Ok(());
                }
                trace!("Accepted incoming connection with peer={}", addr);
                let stream = sock.framed(MessagesCodec);
                let (_, stream) = stream.split();
                let network_tx = network_tx.clone();
                let connection_handler = stream
                    .into_future()
                    .map_err(|e| e.0)
                    .and_then(move |(raw, stream)| match raw.map(Any::from_raw) {
                        Some(Ok(Any::Connect(msg))) => Ok((msg, stream)),
                        Some(Ok(other)) => Err(other_error(&format!(
                            "First message is not Connect, got={:?}",
                            other
                        ))),
                        Some(Err(e)) => Err(into_other(e)),
                        None => Err(other_error("Incoming socket closed")),
                    })
                    .and_then(move |(connect, stream)| {
                        trace!("Received handshake message={:?}", connect);
                        let event = NetworkEvent::PeerConnected(addr, connect);
                        let stream = network_tx
                            .clone()
                            .send(event)
                            .map_err(into_other)
                            .and_then(move |_| Ok(stream))
                            .flatten_stream();

                        stream.for_each(move |raw| {
                            let event = NetworkEvent::MessageReceived(addr, raw);
                            network_tx.clone().send(event).map_err(into_other).map(drop)
                        })
                    })
                    .map(|_| {
                        // Ensure that holder lives until the stream ends.
                        let _holder = holder;
                    })
                    .map_err(log_error);
                handle.spawn(connection_handler);
                Ok(())
            })
            .map_err(log_error);

        let cancel_handler = cancel_handler.map_err(|_| ());
        let tasks = vec![
            tobox(server),
            tobox(requests_handle),
            tobox(cancel_handler)
        ];
        tobox(future::join_all(tasks))
    }

    fn send_event(handle: &Handle, network_tx: &mpsc::Sender<NetworkEvent>, event: NetworkEvent) {
        handle.spawn(network_tx.clone().send(event).map(drop).map_err(log_error));
    }
}

fn tobox<F: Future + 'static>(f: F) -> Box<Future<Item=(), Error=F::Error>> {
    let fut = f.map(|_| ());
    Box::new(fut)
}

// Tcp streams source for retry action.
struct TcpStreamConnectAction {
    handle: Handle,
    peer: SocketAddr,
}

impl Action for TcpStreamConnectAction {
    type Future = TcpStreamNew;
    type Item = <TcpStreamNew as Future>::Item;
    type Error = <TcpStreamNew as Future>::Error;

    fn run(&mut self) -> Self::Future {
        TcpStream::connect(&self.peer, &self.handle)
    }
}
<commit_msg>Remove handler from listener of NetworkPart<commit_after>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use futures::{future, unsync, Future, IntoFuture, Sink, Stream};
use futures::future::Either;
use futures::sync::mpsc;
use tokio_core::net::{TcpListener, TcpStream, TcpStreamNew};
use tokio_core::reactor::Handle;
use tokio_io::AsyncRead;
use tokio_retry::{Action, Retry};
use tokio_retry::strategy::{jitter, FixedInterval};

use std::net::SocketAddr;
use std::time::Duration;
use std::collections::HashMap;
use std::rc::Rc;
use std::cell::RefCell;

use messages::{Any, Connect, RawMessage};
use node::{ExternalMessage, NodeTimeout};
use helpers::Milliseconds;

use super::error::{into_other, log_error, other_error, result_ok};
use super::codec::MessagesCodec;
use super::{EventsAggregator, EventHandler};

#[derive(Debug)]
pub enum NetworkEvent {
    MessageReceived(SocketAddr, RawMessage),
    PeerConnected(SocketAddr, Connect),
    PeerDisconnected(SocketAddr),
}

#[derive(Debug, Clone)]
pub enum NetworkRequest {
    SendMessage(SocketAddr, RawMessage),
    DisconnectWithPeer(SocketAddr),
    Shutdown,
}

#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub struct NetworkConfiguration {
    // TODO: think more about config parameters
    pub max_incoming_connections: usize,
    pub max_outgoing_connections: usize,
    pub tcp_nodelay: bool,
    pub tcp_keep_alive: Option<u64>,
    pub tcp_connect_retry_timeout: Milliseconds,
    pub tcp_connect_max_retries: u64,
}

impl Default for NetworkConfiguration {
    fn default() -> NetworkConfiguration {
        NetworkConfiguration {
            max_incoming_connections: 128,
            max_outgoing_connections: 128,
            tcp_keep_alive: None,
            tcp_nodelay: false,
            tcp_connect_retry_timeout: 15_000,
            tcp_connect_max_retries: 10,
        }
    }
}

#[derive(Debug)]
pub struct HandlerPart<H: EventHandler> {
    pub handler: H,
    pub timeout_rx: mpsc::Receiver<NodeTimeout>,
    pub network_rx: mpsc::Receiver<NetworkEvent>,
    pub api_rx: mpsc::Receiver<ExternalMessage>,
}

#[derive(Debug)]
pub struct NetworkPart {
    pub listen_address: SocketAddr,
    pub network_config: NetworkConfiguration,
    pub network_requests: (mpsc::Sender<NetworkRequest>, mpsc::Receiver<NetworkRequest>),
    pub network_tx: mpsc::Sender<NetworkEvent>,
}

#[derive(Debug, Default, Clone)]
struct ConnectionsPool {
    inner: Rc<RefCell<HashMap<SocketAddr, mpsc::Sender<RawMessage>>>>,
}

impl ConnectionsPool {
    fn new() -> ConnectionsPool {
        ConnectionsPool::default()
    }

    fn insert(&self, peer: SocketAddr, sender: &mpsc::Sender<RawMessage>) {
        self.inner.borrow_mut().insert(peer, sender.clone());
    }

    fn remove(&self, peer: &SocketAddr) {
        self.inner.borrow_mut().remove(peer);
    }

    fn get(&self, peer: SocketAddr) -> Option<mpsc::Sender<RawMessage>> {
        self.inner.borrow_mut().get(&peer).cloned()
    }

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

impl<H: EventHandler + 'static> HandlerPart<H> {
    pub fn run(self) -> Box<Future<Item=(), Error=()>> {
        let mut handler = self.handler;

        let fut = EventsAggregator::new(self.timeout_rx, self.network_rx, self.api_rx)
            .for_each(move |event| {
                handler.handle_event(event);
                Ok(())
            });

        Box::new(fut)
    }
}

impl NetworkPart {
    pub fn run(self, handle_orig: Handle) -> Box<Future<Item=(), Error=()>> {
        let network_config = self.network_config;
        // Cancelation token
        let (cancel_sender, cancel_handler) = unsync::oneshot::channel();
        let mut cancel_sender = Some(cancel_sender);
        // Outgoing connections
        let outgoing_connections = ConnectionsPool::new();
        let network_tx = self.network_tx.clone();
        // Outgoing connections limiter
        let outgoing_connections_limit = network_config.max_outgoing_connections;
        let handle = handle_orig.clone();
        let requests_handle = self.network_requests.1.for_each(move |request| {
            let handle = handle.clone();
            let network_tx = network_tx.clone();
            let outgoing_connections = outgoing_connections.clone();
            match request {
                NetworkRequest::SendMessage(peer, msg) => {
                    let conn_tx = if let Some(conn_tx) = outgoing_connections.get(peer) {
                        conn_tx
                    } else {
                        // Check limit
                        if outgoing_connections.len() >= outgoing_connections_limit {
                            warn!(
                                "Rejected outgoing connection with peer={}, \
                                 connections limit reached.",
                                peer
                            );
                            let event = NetworkEvent::PeerDisconnected(peer);
                            handle.spawn(network_tx.clone().send(event).map(drop).map_err(log_error));
                        }
                        // Register outgoing channel.
                        let (conn_tx, conn_rx) = mpsc::channel(10);
                        outgoing_connections.insert(peer, &conn_tx);
                        // Enable retry feature for outgoing connection.
                        let timeout = network_config.tcp_connect_retry_timeout;
                        let max_tries = network_config.tcp_connect_max_retries as usize;
                        let connect_handle = Retry::spawn(
                            handle.clone(),
                            FixedInterval::from_millis(timeout).map(jitter).take(
                                max_tries,
                            ),
                            TcpStreamConnectAction {
                                handle: handle.clone(),
                                peer,
                            },
                        ).map_err(into_other)
                            // Configure socket
                            .and_then(move |sock| {
                                sock.set_nodelay(network_config.tcp_nodelay)?;
                                Ok(sock)
                            })
                            .and_then(move |sock| {
                                let duration =
                                    network_config.tcp_keep_alive.map(Duration::from_millis);
                                sock.set_keepalive(duration)?;
                                Ok(sock)
                            })
                            // Connect socket with the outgoing channel
                            .and_then(move |sock| {
                                trace!("Established connection with peer={}", peer);

                                let stream = sock.framed(MessagesCodec);
                                let (sink, stream) = stream.split();

                                let writer = conn_rx
                                    .map_err(|_| other_error("Can't send data into socket"))
                                    .forward(sink);
                                let reader = stream.for_each(result_ok);

                                reader
                                    .select2(writer)
                                    .map_err(|_| other_error("Socket error"))
                                    .and_then(|res| match res {
                                        Either::A((_, _reader)) => Ok(()).into_future(),
                                        Either::B((_, _writer)) => Ok(()).into_future(),
                                    })
                            })
                            .then(move |res| {
                                trace!("Connection with peer={} closed, reason={:?}", peer, res);
                                outgoing_connections.clone().remove(&peer);
                                network_tx
                                    .clone()
                                    .send(NetworkEvent::PeerDisconnected(peer))
                                    .map(drop)
                            })
                            .map_err(log_error);
                        handle.spawn(connect_handle);
                        conn_tx
                    };

                    let send_handle = conn_tx.send(msg).map_err(log_error).map(drop);
                    handle.spawn(send_handle);
                }
                NetworkRequest::DisconnectWithPeer(peer) => {
                    outgoing_connections.remove(&peer);
                    let event = NetworkEvent::PeerDisconnected(peer);
                    handle.spawn(network_tx.clone().send(event).map(drop).map_err(log_error));
                }
                // Immediately stop the event loop.
                NetworkRequest::Shutdown => {
                    cancel_sender
                        .take()
                        .ok_or_else(|| other_error("Shutdown twice"))
                        .and_then(|sender| {
                            sender.send(()).map_err(
                                |_| other_error("Can't send shutdown signal"),
                            )
                        })
                        .map_err(log_error)?
                }
            }

            Ok(())
        });

        // Incoming connections limiter
        let incoming_connections_limit = network_config.max_incoming_connections;
        let incoming_connections_counter: Rc<()> = Rc::default();
        // Incoming connections handler
        // TODO Don't use unwrap here!
        let listener = TcpListener::bind(&self.listen_address, &handle_orig).unwrap();
        let network_tx = self.network_tx.clone();
        let server = listener
            .incoming()
            .for_each(move |(sock, addr)| {
                // Increment reference counter
                let holder = Rc::downgrade(&incoming_connections_counter);
                // Check incoming connections count
                let connections_count = Rc::weak_count(&incoming_connections_counter);
                if connections_count > incoming_connections_limit {
                    warn!(
                        "Rejected incoming connection with peer={}, \
                         connections limit reached.",
                        addr
                    );
                    tobox(future::ok(()))
                } else {
                    trace!("Accepted incoming connection with peer={}", addr);
                    let stream = sock.framed(MessagesCodec);
                    let (_, stream) = stream.split();
                    let network_tx = network_tx.clone();
                    let connection_handler = stream
                        .into_future()
                        .map_err(|e| e.0)
                        .and_then(move |(raw, stream)| match raw.map(Any::from_raw) {
                            Some(Ok(Any::Connect(msg))) => Ok((msg, stream)),
                            Some(Ok(other)) => Err(other_error(&format!(
                                "First message is not Connect, got={:?}",
                                other
                            ))),
                            Some(Err(e)) => Err(into_other(e)),
                            None => Err(other_error("Incoming socket closed")),
                        })
                        .and_then(move |(connect, stream)| {
                            trace!("Received handshake message={:?}", connect);
                            let event = NetworkEvent::PeerConnected(addr, connect);
                            let stream = network_tx
                                .clone()
                                .send(event)
                                .map_err(into_other)
                                .and_then(move |_| Ok(stream))
                                .flatten_stream();

                            stream.for_each(move |raw| {
                                let event = NetworkEvent::MessageReceived(addr, raw);
                                network_tx.clone().send(event).map_err(into_other).map(drop)
                            })
                        })
                        .map(|_| {
                            // Ensure that holder lives until the stream ends.
                            let _holder = holder;
                        });
                    tobox(connection_handler)
                }
            })
            .map_err(log_error);

        let cancel_handler = cancel_handler.map_err(|_| ());
        let tasks = vec![
            tobox(server),
            tobox(requests_handle),
            tobox(cancel_handler)
        ];
        tobox(future::join_all(tasks))
    }
}

fn tobox<F: Future + 'static>(f: F) -> Box<Future<Item=(), Error=F::Error>> {
    let fut = f.map(|_| ());
    Box::new(fut)
}

// Tcp streams source for retry action.
struct TcpStreamConnectAction {
    handle: Handle,
    peer: SocketAddr,
}

impl Action for TcpStreamConnectAction {
    type Future = TcpStreamNew;
    type Item = <TcpStreamNew as Future>::Item;
    type Error = <TcpStreamNew as Future>::Error;

    fn run(&mut self) -> Self::Future {
        TcpStream::connect(&self.peer, &self.handle)
    }
}
<|endoftext|>