<commit_before>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// 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 std::collections::HashMap;
use std::fmt;
use std::iter::FromIterator;
use std::str::FromStr;

use error::{Error, Result};

#[derive(Debug)]
pub struct Bind {
    pub service: String,
    pub exports: Vec<String>,
}

impl FromStr for Bind {
    type Err = Error;

    fn from_str(line: &str) -> Result<Self> {
        let mut parts = line.split('=');
        let service = match parts.next() {
            None => return Err(Error::MetaFileBadBind),
            Some(service) => service.to_string(),
        };
        let exports = match parts.next() {
            None => return Err(Error::MetaFileBadBind),
            Some(exports) => exports.split(' ').map(|t| t.to_string()).collect(),
        };
        Ok(Bind {
               service: service,
               exports: exports,
           })
    }
}

#[derive(Debug, PartialEq)]
pub struct EnvVar {
    pub key: String,
    pub value: String,
    pub separator: Option<char>,
}

#[derive(Debug)]
pub struct EnvVars {
    inner: Vec<EnvVar>,
}

pub fn parse_key_value(s: &str) -> Result<HashMap<String, String>> {
    Ok(HashMap::from_iter(s.lines()
                              .map(|l| l.splitn(2, '=').collect::<Vec<_>>())
                              .map(|kv| (kv[0].to_string(), kv[1].to_string()))))
}

impl EnvVars {
    pub fn new(values: HashMap<String, String>, separators: HashMap<String, String>) -> Self {
        Self {
            inner: values
                .into_iter()
                .map(|(key, value)| if let Some(sep) = separators.get(&key) {
                         EnvVar {
                             key,
                             value,
                             separator: sep.to_owned().pop(),
                         }
                     } else {
                         EnvVar {
                             key,
                             value,
                             separator: None,
                         }
                     })
                .collect(),
        }
    }
}

#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub enum MetaFile {
    Binds,
    BindsOptional,
    CFlags,
    Config,
    Deps,
    TDeps,
    Environment,
    EnvironmentSep,
    Exports,
    Exposes,
    Ident,
    LdRunPath,
    LdFlags,
    Manifest,
    Path,
    SvcUser,
    SvcGroup,
    Target,
}

impl fmt::Display for MetaFile {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let id = match *self {
            MetaFile::Binds => "BINDS",
            MetaFile::BindsOptional => "BINDS_OPTIONAL",
            MetaFile::CFlags => "CFLAGS",
            MetaFile::Config => "default.toml",
            MetaFile::Deps => "DEPS",
            MetaFile::TDeps => "TDEPS",
            MetaFile::Environment => "ENVIRONMENT",
            MetaFile::EnvironmentSep => "ENVIRONMENT_SEP",
            MetaFile::Exports => "EXPORTS",
            MetaFile::Exposes => "EXPOSES",
            MetaFile::Ident => "IDENT",
            MetaFile::LdRunPath => "LD_RUN_PATH",
            MetaFile::LdFlags => "LDFLAGS",
            MetaFile::Manifest => "MANIFEST",
            MetaFile::Path => "PATH",
            MetaFile::SvcUser => "SVC_USER",
            MetaFile::SvcGroup => "SVC_GROUP",
            MetaFile::Target => "TARGET",
        };
        write!(f, "{}", id)
    }
}

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

    static ENVIRONMENT: &str = r#"PATH=/hab/pkgs/python/setuptools/35.0.1/20170424072606/bin
PYTHONPATH=/hab/pkgs/python/setuptools/35.0.1/20170424072606/lib/python3.6/site-packages
"#;
    static ENVIRONMENT_SEP: &str = r#"PATH=:
PYTHONPATH=:
"#;

    #[test]
    #[should_panic]
    fn malformed_file() {
        parse_key_value(&"PATH").unwrap();
    }

    #[test]
    fn can_parse_environment_file() {
        let mut m: HashMap<String, String> = HashMap::new();
        m.insert("PATH".to_string(),
                 "/hab/pkgs/python/setuptools/35.0.1/20170424072606/bin".to_string());
        m.insert("PYTHONPATH".to_string(),
                 "/hab/pkgs/python/setuptools/35.0.1/20170424072606/lib/python3.6/site-packages"
                     .to_string());

        assert_eq!(parse_key_value(&ENVIRONMENT).unwrap(), m);
    }

    #[test]
    fn can_parse_environment_sep_file() {
        let mut m: HashMap<String, String> = HashMap::new();
        m.insert("PATH".to_string(), ":".to_string());
        m.insert("PYTHONPATH".to_string(), ":".to_string());

        assert_eq!(parse_key_value(&ENVIRONMENT_SEP).unwrap(), m);
    }

    #[test]
    fn new_env_vars() {
        let ev = EnvVars::new(parse_key_value(&ENVIRONMENT).unwrap(),
                              parse_key_value(&ENVIRONMENT_SEP).unwrap());
        let r =
            vec![EnvVar {
                     key: "PATH".to_string(),
                     value: "/hab/pkgs/python/setuptools/35.0.1/20170424072606/bin".to_string(),
                     separator: Some(':'),
                 },
                 EnvVar {
                     key: "PYTHONPATH".to_string(),
                     value: "/hab/pkgs/python/setuptools/35.0.1/20170424072606/lib/python3.6/site-packages"
                         .to_string(),
                     separator: Some(':'),
                 }];

        assert_eq!(ev.inner, r);
    }
}
<commit_msg>Fix test new_env_vars to guarantee order<commit_after>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// 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 std::collections::HashMap;
use std::fmt;
use std::iter::{FromIterator, IntoIterator};
use std::str::FromStr;
use std::vec::IntoIter;

use error::{Error, Result};

pub fn parse_key_value(s: &str) -> Result<HashMap<String, String>> {
    Ok(HashMap::from_iter(s.lines()
                              .map(|l| l.splitn(2, '=').collect::<Vec<_>>())
                              .map(|kv| (kv[0].to_string(), kv[1].to_string()))))
}

#[derive(Debug)]
pub struct Bind {
    pub service: String,
    pub exports: Vec<String>,
}

impl FromStr for Bind {
    type Err = Error;

    fn from_str(line: &str) -> Result<Self> {
        let mut parts = line.split('=');
        let service = match parts.next() {
            None => return Err(Error::MetaFileBadBind),
            Some(service) => service.to_string(),
        };
        let exports = match parts.next() {
            None => return Err(Error::MetaFileBadBind),
            Some(exports) => exports.split(' ').map(|t| t.to_string()).collect(),
        };
        Ok(Bind {
               service: service,
               exports: exports,
           })
    }
}

#[derive(Debug, PartialEq)]
pub struct EnvVar {
    pub key: String,
    pub value: String,
    pub separator: Option<char>,
}

#[derive(Debug)]
pub struct EnvVars {
    inner: Vec<EnvVar>,
}

impl EnvVars {
    pub fn new(values: HashMap<String, String>, separators: HashMap<String, String>) -> Self {
        Self {
            inner: values
                .into_iter()
                .map(|(key, value)| if let Some(sep) = separators.get(&key) {
                         EnvVar {
                             key,
                             value,
                             separator: sep.to_owned().pop(),
                         }
                     } else {
                         EnvVar {
                             key,
                             value,
                             separator: None,
                         }
                     })
                .collect(),
        }
    }
}

impl IntoIterator for EnvVars {
    type Item = EnvVar;
    type IntoIter = IntoIter<EnvVar>;

    fn into_iter(self) -> Self::IntoIter {
        self.inner.into_iter()
    }
}

#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub enum MetaFile {
    Binds,
    BindsOptional,
    CFlags,
    Config,
    Deps,
    TDeps,
    Environment,
    EnvironmentSep,
    Exports,
    Exposes,
    Ident,
    LdRunPath,
    LdFlags,
    Manifest,
    Path,
    SvcUser,
    SvcGroup,
    Target,
}

impl fmt::Display for MetaFile {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let id = match *self {
            MetaFile::Binds => "BINDS",
            MetaFile::BindsOptional => "BINDS_OPTIONAL",
            MetaFile::CFlags => "CFLAGS",
            MetaFile::Config => "default.toml",
            MetaFile::Deps => "DEPS",
            MetaFile::TDeps => "TDEPS",
            MetaFile::Environment => "ENVIRONMENT",
            MetaFile::EnvironmentSep => "ENVIRONMENT_SEP",
            MetaFile::Exports => "EXPORTS",
            MetaFile::Exposes => "EXPOSES",
            MetaFile::Ident => "IDENT",
            MetaFile::LdRunPath => "LD_RUN_PATH",
            MetaFile::LdFlags => "LDFLAGS",
            MetaFile::Manifest => "MANIFEST",
            MetaFile::Path => "PATH",
            MetaFile::SvcUser => "SVC_USER",
            MetaFile::SvcGroup => "SVC_GROUP",
            MetaFile::Target => "TARGET",
        };
        write!(f, "{}", id)
    }
}

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

    static ENVIRONMENT: &str = r#"PATH=/hab/pkgs/python/setuptools/35.0.1/20170424072606/bin
PYTHONPATH=/hab/pkgs/python/setuptools/35.0.1/20170424072606/lib/python3.6/site-packages
"#;
    static ENVIRONMENT_SEP: &str = r#"PATH=:
PYTHONPATH=:
"#;

    #[test]
    #[should_panic]
    fn malformed_file() {
        parse_key_value(&"PATH").unwrap();
    }

    #[test]
    fn can_parse_environment_file() {
        let mut m: HashMap<String, String> = HashMap::new();
        m.insert("PATH".to_string(),
                 "/hab/pkgs/python/setuptools/35.0.1/20170424072606/bin".to_string());
        m.insert("PYTHONPATH".to_string(),
                 "/hab/pkgs/python/setuptools/35.0.1/20170424072606/lib/python3.6/site-packages"
                     .to_string());

        assert_eq!(parse_key_value(&ENVIRONMENT).unwrap(), m);
    }

    #[test]
    fn can_parse_environment_sep_file() {
        let mut m: HashMap<String, String> = HashMap::new();
        m.insert("PATH".to_string(), ":".to_string());
        m.insert("PYTHONPATH".to_string(), ":".to_string());

        assert_eq!(parse_key_value(&ENVIRONMENT_SEP).unwrap(), m);
    }

    #[test]
    fn new_env_vars() {
        let mut result = EnvVars::new(parse_key_value(&ENVIRONMENT).unwrap(),
                                      parse_key_value(&ENVIRONMENT_SEP).unwrap())
                .into_iter()
                .collect::<Vec<_>>();
        // Sort the result by key, so we have a guarantee of order
        result.sort_by_key(|v| v.key.to_owned());

        let expected =
            vec![EnvVar {
                     key: "PATH".to_string(),
                     value: "/hab/pkgs/python/setuptools/35.0.1/20170424072606/bin".to_string(),
                     separator: Some(':'),
                 },
                 EnvVar {
                     key: "PYTHONPATH".to_string(),
                     value: "/hab/pkgs/python/setuptools/35.0.1/20170424072606/lib/python3.6/site-packages"
                         .to_string(),
                     separator: Some(':'),
                 }];

        assert_eq!(result, expected);
    }
}
<|endoftext|>


<commit_before>use parking_lot::RwLock;
use std::collections::HashSet;

use crate::class::*;
use crate::error::msg::SemError;
use crate::semck;
use crate::sym::Sym;
use crate::ty::{BuiltinType, TypeList};
use crate::vm::{Fct, FctId, FctKind, FctParent, FctSrc, NodeMap, VM};

use dora_parser::ast::visit::{self, Visitor};
use dora_parser::ast::{self, Ast};
use dora_parser::interner::Name;
use dora_parser::lexer::position::Position;

pub fn check<'ast>(vm: &mut VM<'ast>, ast: &'ast Ast, map_cls_defs: &NodeMap<ClassId>) {
    let mut clsck = ClsCheck {
        vm,
        ast,
        cls_id: None,
        map_cls_defs,
        file_id: 0,
    };

    clsck.check();
}

struct ClsCheck<'x, 'ast: 'x> {
    vm: &'x mut VM<'ast>,
    ast: &'ast ast::Ast,
    map_cls_defs: &'x NodeMap<ClassId>,
    file_id: u32,

    cls_id: Option<ClassId>,
}

impl<'x, 'ast> ClsCheck<'x, 'ast> {
    fn check(&mut self) {
        self.visit_ast(self.ast);
    }

    fn add_field(&mut self, pos: Position, name: Name, ty: BuiltinType, reassignable: bool) {
        let cls = self.vm.classes.idx(self.cls_id.unwrap());
        let mut cls = cls.write();

        for field in &cls.fields {
            if field.name == name {
                let name = self.vm.interner.str(name).to_string();
                self.vm
                    .diag
                    .lock()
                    .report(cls.file, pos, SemError::ShadowField(name));
            }
        }

        let field = Field {
            id: cls.fields.len().into(),
            name,
            ty,
            offset: 0,
            reassignable,
        };

        cls.fields.push(field);
    }
}

impl<'x, 'ast> Visitor<'ast> for ClsCheck<'x, 'ast> {
    fn visit_file(&mut self, f: &'ast ast::File) {
        visit::walk_file(self, f);
        self.file_id += 1;
    }

    fn visit_class(&mut self, c: &'ast ast::Class) {
        self.cls_id = Some(*self.map_cls_defs.get(c.id).unwrap());

        self.vm.sym.lock().push_level();

        if let Some(ref type_params) = c.type_params {
            let cls = self.vm.classes.idx(self.cls_id.unwrap());
            let mut cls = cls.write();

            if type_params.len() > 0 {
                let mut names = HashSet::new();
                let mut type_param_id = 0;
                let mut params = Vec::new();

                for type_param in type_params {
                    if !names.insert(type_param.name) {
                        let name = self.vm.interner.str(type_param.name).to_string();
                        let msg = SemError::TypeParamNameNotUnique(name);
                        self.vm.diag.lock().report(cls.file, type_param.pos, msg);
                    }

                    params.push(BuiltinType::ClassTypeParam(cls.id, type_param_id.into()));

                    for bound in &type_param.bounds {
                        let ty = semck::read_type(self.vm, cls.file, bound);

                        match ty {
                            Some(BuiltinType::Class(cls_id, _)) => {
                                if let None = cls.type_params[type_param_id].class_bound {
                                    cls.type_params[type_param_id].class_bound = Some(cls_id);
                                } else {
                                    let msg = SemError::MultipleClassBounds;
                                    self.vm.diag.lock().report(cls.file, type_param.pos, msg);
                                }
                            }

                            Some(BuiltinType::Trait(trait_id)) => {
                                if !cls.type_params[type_param_id].trait_bounds.insert(trait_id) {
                                    let msg = SemError::DuplicateTraitBound;
                                    self.vm.diag.lock().report(cls.file, type_param.pos, msg);
                                }
                            }

                            None => {
                                // unknown type, error is already thrown
                            }

                            _ => {
                                let msg = SemError::BoundExpected;
                                self.vm.diag.lock().report(cls.file, bound.pos(), msg);
                            }
                        }
                    }

                    let sym = Sym::SymClassTypeParam(cls.id, type_param_id.into());
                    self.vm.sym.lock().insert(type_param.name, sym);
                    type_param_id += 1;
                }

                let params = TypeList::with(params);
                let list_id = self.vm.lists.lock().insert(params);
                cls.ty = BuiltinType::Class(cls.id, list_id);
            } else {
                let msg = SemError::TypeParamsExpected;
                self.vm.diag.lock().report(cls.file, c.pos, msg);
            }
        }

        visit::walk_class(self, c);

        if let Some(ref parent_class) = c.parent_class {
            let name = self.vm.interner.str(parent_class.name).to_string();
            let sym = self.vm.sym.lock().get(parent_class.name);

            match sym {
                Some(Sym::SymClass(clsid)) => {
                    let super_cls = self.vm.classes.idx(clsid);
                    let super_cls = super_cls.read();

                    if super_cls.has_open {
                        let cls = self.vm.classes.idx(self.cls_id.unwrap());
                        let mut cls = cls.write();
                        cls.parent_class = Some(clsid);
                    } else {
                        let msg = SemError::UnderivableType(name);
                        self.vm
                            .diag
                            .lock()
                            .report(self.file_id.into(), parent_class.pos, msg);
                    }

                    let number_type_params = parent_class.type_params.len();

                    if number_type_params != super_cls.type_params.len() {
                        let msg = SemError::WrongNumberTypeParams(
                            super_cls.type_params.len(),
                            number_type_params,
                        );
                        self.vm
                            .diag
                            .lock()
                            .report(self.file_id.into(), parent_class.pos, msg);
                    }
                }

                _ => {
                    let msg = SemError::UnknownClass(name);
                    self.vm
                        .diag
                        .lock()
                        .report(self.file_id.into(), parent_class.pos, msg);
                }
            };
        } else {
            let object_cls = self.vm.vips.object_class;
            let cls_id = self.cls_id.unwrap();

            if cls_id != object_cls {
                let cls = self.vm.classes.idx(cls_id);
                let mut cls = cls.write();
                cls.parent_class = Some(object_cls);
            }
        }

        self.cls_id = None;
        self.vm.sym.lock().pop_level();
    }

    fn visit_field(&mut self, f: &'ast ast::Field) {
        let ty = semck::read_type(self.vm, self.file_id.into(), &f.data_type)
            .unwrap_or(BuiltinType::Unit);
        self.add_field(f.pos, f.name, ty, f.reassignable);

        if !f.reassignable && !f.primary_ctor && f.expr.is_none() {
            self.vm.diag.lock().report(
                self.file_id.into(),
                f.pos,
                SemError::LetMissingInitialization,
            );
        }
    }

    fn visit_ctor(&mut self, f: &'ast ast::Function) {
        let clsid = self.cls_id.unwrap();

        let kind = if f.block.is_some() {
            FctKind::Source(RwLock::new(FctSrc::new()))
        } else {
            FctKind::Definition
        };

        let fct = Fct {
            id: FctId(0),
            pos: f.pos,
            ast: f,
            name: f.name,
            param_types: Vec::new(),
            return_type: BuiltinType::Unit,
            parent: FctParent::Class(clsid),
            has_override: f.has_override,
            has_open: f.has_open,
            has_final: f.has_final,
            is_pub: true,
            is_static: false,
            is_abstract: f.is_test,
            is_test: f.is_test,
            internal: f.internal,
            internal_resolved: false,
            overrides: None,
            throws: f.throws,
            is_constructor: f.is_constructor,
            vtable_index: None,
            initialized: false,
            impl_for: None,
            file: self.file_id.into(),

            type_params: Vec::new(),
            kind,
        };

        let fctid = self.vm.add_fct(fct);

        let cls = self.vm.classes.idx(self.cls_id.unwrap());
        let mut cls = cls.write();
        cls.constructor = Some(fctid);
    }

    fn visit_method(&mut self, f: &'ast ast::Function) {
        if self.cls_id.is_none() {
            return;
        }

        let kind = if f.block.is_some() {
            FctKind::Source(RwLock::new(FctSrc::new()))
        } else {
            FctKind::Definition
        };

        let fct = Fct {
            id: FctId(0),
            ast: f,
            pos: f.pos,
            name: f.name,
            param_types: Vec::new(),
            return_type: BuiltinType::Unit,
            parent: FctParent::Class(self.cls_id.unwrap()),
            has_override: f.has_override,

            // abstract for methods also means that method is open to
            // override
            has_open: f.has_open || f.is_abstract,
            has_final: f.has_final,
            is_pub: f.is_pub,
            is_static: f.is_static,
            is_abstract: f.is_abstract,
            is_test: f.is_test,
            internal: f.internal,
            internal_resolved: false,
            overrides: None,
            throws: f.throws,
            is_constructor: false,
            vtable_index: None,
            initialized: false,
            impl_for: None,
            file: self.file_id.into(),

            type_params: Vec::new(),
            kind,
        };

        let fctid = self.vm.add_fct(fct);

        let cls = self.vm.classes.idx(self.cls_id.unwrap());
        let mut cls = cls.write();
        cls.methods.push(fctid);
    }
}

#[cfg(test)]
mod tests {
    use crate::error::msg::SemError;
    use crate::semck::tests::*;

    #[test]
    fn test_multiple_definition() {
        err(
            "class Foo class Foo",
            pos(1, 11),
            SemError::ShadowClass("Foo".into()),
        );
    }

    #[test]
    fn test_class_and_function() {
        err(
            "fun Foo() {} class Foo",
            pos(1, 14),
            SemError::ShadowFunction("Foo".into()),
        );
        err(
            "class Foo fun Foo() {}",
            pos(1, 11),
            SemError::ShadowClass("Foo".into()),
        );
    }

    #[test]
    fn test_class_definition() {
        ok("class Foo");
        ok("class Foo()");
        ok("class Foo(let a: Int)");
        ok("class Foo(let a: Int, let b:Int)");
        ok("class Foo(let a: Foo)");
        ok("class Foo(let a: Bar) class Bar");
        err(
            "class Foo(let a: Unknown)",
            pos(1, 18),
            SemError::UnknownType("Unknown".into()),
        );
        err(
            "class Foo(let a: Int, let a: Int)",
            pos(1, 27),
            SemError::ShadowField("a".to_string()),
        );
    }

    #[test]
    fn class_with_unknown_super_class() {
        err(
            "class B : A {}",
            pos(1, 11),
            SemError::UnknownClass("A".into()),
        );
        err(
            "@open class B : A {}",
            pos(1, 17),
            SemError::UnknownClass("A".into()),
        );
        err(
            "class B : Int {}",
            pos(1, 11),
            SemError::UnderivableType("Int".into()),
        );
    }

    #[test]
    fn class_with_open_modifier() {
        ok("@open class A {}");
        ok("@open class A {} class B : A {}");
        err(
            "class A {} class B : A {}",
            pos(1, 22),
            SemError::UnderivableType("A".into()),
        );
    }

    #[test]
    fn non_field_ctor_arguments() {
        ok("class Foo(a: Int, b: Int)");
        ok("class Foo(let a: Int, b: Int)");
        ok("class Foo(a: Int, var b: Int)");
        err(
            "class Foo(a: Int, a: Int)",
            pos(1, 1),
            SemError::ShadowParam("a".into()),
        );
        err(
            "class Foo(a: Int, let a: Int)",
            pos(1, 1),
            SemError::ShadowParam("a".into()),
        );
        err(
            "class Foo(let a: Int, a: Int)",
            pos(1, 1),
            SemError::ShadowParam("a".into()),
        );
        err(
            "class Foo(a: Int) fun f(x: Foo) { x.a = 1; }",
            pos(1, 36),
            SemError::UnknownField("a".into(), "Foo".into()),
        );

        ok("class Foo(a: Int) fun foo() -> Foo { return Foo(1); } ");
    }

    #[test]
    fn field_defined_twice() {
        err(
            "class Foo { var a: Int; var a: Int; }",
            pos(1, 25),
            SemError::ShadowField("a".into()),
        );
        err(
            "class Foo(let a: Int) { var a: Int; }",
            pos(1, 25),
            SemError::ShadowField("a".into()),
        );
    }

    #[test]
    fn let_field_without_initialization() {
        err(
            "class Foo { let a: Int; }",
            pos(1, 13),
            SemError::LetMissingInitialization,
        );
    }

    #[test]
    fn field_self_assignment() {
        err(
            "class Foo(a: Int) { var b: Int = b; }",
            pos(1, 34),
            SemError::UnknownIdentifier("b".into()),
        );
    }

    #[test]
    fn test_generic_class() {
        ok("class A[T]");
        ok("class A[X, Y]");
        err(
            "class A[T, T]",
            pos(1, 12),
            SemError::TypeParamNameNotUnique("T".into()),
        );
        err("class A[]", pos(1, 1), SemError::TypeParamsExpected);
    }

    #[test]
    fn test_generic_argument() {
        ok("class A[T](val: T)");
        ok("class A[T](var val: T)");
        ok("class A[T](let val: T)");
    }

    #[test]
    fn test_generic_bound() {
        err(
            "class A[T: Foo]",
            pos(1, 12),
            SemError::UnknownType("Foo".into()),
        );
        ok("class Foo class A[T: Foo]");
        ok("trait Foo {} class A[T: Foo]");
    }

    #[test]
    fn test_generic_multiple_class_bounds() {
        err(
            "class Foo class Bar
            class A[T: Foo + Bar]",
            pos(2, 21),
            SemError::MultipleClassBounds,
        );
    }

    #[test]
    fn test_duplicate_trait_bound() {
        err(
            "trait Foo {}
            class A[T: Foo + Foo]",
            pos(2, 21),
            SemError::DuplicateTraitBound,
        );
    }

    #[test]
    fn test_super_class_with_superfluous_type_params() {
        err(
            "
            @open class A
            class B: A[Int] {}",
            pos(3, 22),
            SemError::WrongNumberTypeParams(0, 1),
        );
    }
}
<commit_msg>semck: check parent class definition<commit_after>use parking_lot::RwLock;
use std::collections::HashSet;

use crate::class::*;
use crate::error::msg::SemError;
use crate::semck;
use crate::sym::Sym;
use crate::ty::{BuiltinType, TypeList};
use crate::vm::{Fct, FctId, FctKind, FctParent, FctSrc, NodeMap, VM};

use dora_parser::ast::visit::{self, Visitor};
use dora_parser::ast::{self, Ast};
use dora_parser::interner::Name;
use dora_parser::lexer::position::Position;

pub fn check<'ast>(vm: &mut VM<'ast>, ast: &'ast Ast, map_cls_defs: &NodeMap<ClassId>) {
    let mut clsck = ClsCheck {
        vm,
        ast,
        cls_id: None,
        map_cls_defs,
        file_id: 0,
    };

    clsck.check();
}

struct ClsCheck<'x, 'ast: 'x> {
    vm: &'x mut VM<'ast>,
    ast: &'ast ast::Ast,
    map_cls_defs: &'x NodeMap<ClassId>,
    file_id: u32,

    cls_id: Option<ClassId>,
}

impl<'x, 'ast> ClsCheck<'x, 'ast> {
    fn check(&mut self) {
        self.visit_ast(self.ast);
    }

    fn add_field(&mut self, pos: Position, name: Name, ty: BuiltinType, reassignable: bool) {
        let cls = self.vm.classes.idx(self.cls_id.unwrap());
        let mut cls = cls.write();

        for field in &cls.fields {
            if field.name == name {
                let name = self.vm.interner.str(name).to_string();
                self.vm
                    .diag
                    .lock()
                    .report(cls.file, pos, SemError::ShadowField(name));
            }
        }

        let field = Field {
            id: cls.fields.len().into(),
            name,
            ty,
            offset: 0,
            reassignable,
        };

        cls.fields.push(field);
    }
}

impl<'x, 'ast> Visitor<'ast> for ClsCheck<'x, 'ast> {
    fn visit_file(&mut self, f: &'ast ast::File) {
        visit::walk_file(self, f);
        self.file_id += 1;
    }

    fn visit_class(&mut self, c: &'ast ast::Class) {
        self.cls_id = Some(*self.map_cls_defs.get(c.id).unwrap());

        self.vm.sym.lock().push_level();

        if let Some(ref type_params) = c.type_params {
            let cls = self.vm.classes.idx(self.cls_id.unwrap());
            let mut cls = cls.write();

            if type_params.len() > 0 {
                let mut names = HashSet::new();
                let mut type_param_id = 0;
                let mut params = Vec::new();

                for type_param in type_params {
                    if !names.insert(type_param.name) {
                        let name = self.vm.interner.str(type_param.name).to_string();
                        let msg = SemError::TypeParamNameNotUnique(name);
                        self.vm.diag.lock().report(cls.file, type_param.pos, msg);
                    }

                    params.push(BuiltinType::ClassTypeParam(cls.id, type_param_id.into()));

                    for bound in &type_param.bounds {
                        let ty = semck::read_type(self.vm, cls.file, bound);

                        match ty {
                            Some(BuiltinType::Class(cls_id, _)) => {
                                if let None = cls.type_params[type_param_id].class_bound {
                                    cls.type_params[type_param_id].class_bound = Some(cls_id);
                                } else {
                                    let msg = SemError::MultipleClassBounds;
                                    self.vm.diag.lock().report(cls.file, type_param.pos, msg);
                                }
                            }

                            Some(BuiltinType::Trait(trait_id)) => {
                                if !cls.type_params[type_param_id].trait_bounds.insert(trait_id) {
                                    let msg = SemError::DuplicateTraitBound;
                                    self.vm.diag.lock().report(cls.file, type_param.pos, msg);
                                }
                            }

                            None => {
                                // unknown type, error is already thrown
                            }

                            _ => {
                                let msg = SemError::BoundExpected;
                                self.vm.diag.lock().report(cls.file, bound.pos(), msg);
                            }
                        }
                    }

                    let sym = Sym::SymClassTypeParam(cls.id, type_param_id.into());
                    self.vm.sym.lock().insert(type_param.name, sym);
                    type_param_id += 1;
                }

                let params = TypeList::with(params);
                let list_id = self.vm.lists.lock().insert(params);
                cls.ty = BuiltinType::Class(cls.id, list_id);
            } else {
                let msg = SemError::TypeParamsExpected;
                self.vm.diag.lock().report(cls.file, c.pos, msg);
            }
        }

        visit::walk_class(self, c);

        if let Some(ref parent_class) = c.parent_class {
            let name = self.vm.interner.str(parent_class.name).to_string();
            let sym = self.vm.sym.lock().get(parent_class.name);

            match sym {
                Some(Sym::SymClass(cls_id)) => {
                    let super_cls = self.vm.classes.idx(cls_id);
                    let super_cls = super_cls.read();

                    if !super_cls.has_open {
                        let msg = SemError::UnderivableType(name);
                        self.vm
                            .diag
                            .lock()
                            .report(self.file_id.into(), parent_class.pos, msg);
                    }

                    let number_type_params = parent_class.type_params.len();

                    if number_type_params != super_cls.type_params.len() {
                        let msg = SemError::WrongNumberTypeParams(
                            super_cls.type_params.len(),
                            number_type_params,
                        );
                        self.vm
                            .diag
                            .lock()
                            .report(self.file_id.into(), parent_class.pos, msg);
                    } else {
                        let mut types = Vec::new();

                        for tp in &parent_class.type_params {
                            let ty = semck::read_type(self.vm, self.file_id.into(), tp)
                                .unwrap_or(BuiltinType::Error);
                            types.push(ty);
                        }

                        let list = TypeList::with(types);
                        let list_id = self.vm.lists.lock().insert(list);
                        let _ty = BuiltinType::Class(cls_id, list_id);

                        let cls = self.vm.classes.idx(self.cls_id.unwrap());
                        let mut cls = cls.write();
                        cls.parent_class = Some(cls_id);
                    }
                }

                _ => {
                    let msg = SemError::UnknownClass(name);
                    self.vm
                        .diag
                        .lock()
                        .report(self.file_id.into(), parent_class.pos, msg);
                }
            };
        } else {
            let object_cls = self.vm.vips.object_class;
            let cls_id = self.cls_id.unwrap();

            if cls_id != object_cls {
                let cls = self.vm.classes.idx(cls_id);
                let mut cls = cls.write();
                cls.parent_class = Some(object_cls);
            }
        }

        self.cls_id = None;
        self.vm.sym.lock().pop_level();
    }

    fn visit_field(&mut self, f: &'ast ast::Field) {
        let ty = semck::read_type(self.vm, self.file_id.into(), &f.data_type)
            .unwrap_or(BuiltinType::Unit);
        self.add_field(f.pos, f.name, ty, f.reassignable);

        if !f.reassignable && !f.primary_ctor && f.expr.is_none() {
            self.vm.diag.lock().report(
                self.file_id.into(),
                f.pos,
                SemError::LetMissingInitialization,
            );
        }
    }

    fn visit_ctor(&mut self, f: &'ast ast::Function) {
        let clsid = self.cls_id.unwrap();

        let kind = if f.block.is_some() {
            FctKind::Source(RwLock::new(FctSrc::new()))
        } else {
            FctKind::Definition
        };

        let fct = Fct {
            id: FctId(0),
            pos: f.pos,
            ast: f,
            name: f.name,
            param_types: Vec::new(),
            return_type: BuiltinType::Unit,
            parent: FctParent::Class(clsid),
            has_override: f.has_override,
            has_open: f.has_open,
            has_final: f.has_final,
            is_pub: true,
            is_static: false,
            is_abstract: f.is_test,
            is_test: f.is_test,
            internal: f.internal,
            internal_resolved: false,
            overrides: None,
            throws: f.throws,
            is_constructor: f.is_constructor,
            vtable_index: None,
            initialized: false,
            impl_for: None,
            file: self.file_id.into(),

            type_params: Vec::new(),
            kind,
        };

        let fctid = self.vm.add_fct(fct);

        let cls = self.vm.classes.idx(self.cls_id.unwrap());
        let mut cls = cls.write();
        cls.constructor = Some(fctid);
    }

    fn visit_method(&mut self, f: &'ast ast::Function) {
        if self.cls_id.is_none() {
            return;
        }

        let kind = if f.block.is_some() {
            FctKind::Source(RwLock::new(FctSrc::new()))
        } else {
            FctKind::Definition
        };

        let fct = Fct {
            id: FctId(0),
            ast: f,
            pos: f.pos,
            name: f.name,
            param_types: Vec::new(),
            return_type: BuiltinType::Unit,
            parent: FctParent::Class(self.cls_id.unwrap()),
            has_override: f.has_override,

            // abstract for methods also means that method is open to
            // override
            has_open: f.has_open || f.is_abstract,
            has_final: f.has_final,
            is_pub: f.is_pub,
            is_static: f.is_static,
            is_abstract: f.is_abstract,
            is_test: f.is_test,
            internal: f.internal,
            internal_resolved: false,
            overrides: None,
            throws: f.throws,
            is_constructor: false,
            vtable_index: None,
            initialized: false,
            impl_for: None,
            file: self.file_id.into(),

            type_params: Vec::new(),
            kind,
        };

        let fctid = self.vm.add_fct(fct);

        let cls = self.vm.classes.idx(self.cls_id.unwrap());
        let mut cls = cls.write();
        cls.methods.push(fctid);
    }
}

#[cfg(test)]
mod tests {
    use crate::error::msg::SemError;
    use crate::semck::tests::*;

    #[test]
    fn test_multiple_definition() {
        err(
            "class Foo class Foo",
            pos(1, 11),
            SemError::ShadowClass("Foo".into()),
        );
    }

    #[test]
    fn test_class_and_function() {
        err(
            "fun Foo() {} class Foo",
            pos(1, 14),
            SemError::ShadowFunction("Foo".into()),
        );
        err(
            "class Foo fun Foo() {}",
            pos(1, 11),
            SemError::ShadowClass("Foo".into()),
        );
    }

    #[test]
    fn test_class_definition() {
        ok("class Foo");
        ok("class Foo()");
        ok("class Foo(let a: Int)");
        ok("class Foo(let a: Int, let b:Int)");
        ok("class Foo(let a: Foo)");
        ok("class Foo(let a: Bar) class Bar");
        err(
            "class Foo(let a: Unknown)",
            pos(1, 18),
            SemError::UnknownType("Unknown".into()),
        );
        err(
            "class Foo(let a: Int, let a: Int)",
            pos(1, 27),
            SemError::ShadowField("a".to_string()),
        );
    }

    #[test]
    fn class_with_unknown_super_class() {
        err(
            "class B : A {}",
            pos(1, 11),
            SemError::UnknownClass("A".into()),
        );
        err(
            "@open class B : A {}",
            pos(1, 17),
            SemError::UnknownClass("A".into()),
        );
        err(
            "class B : Int {}",
            pos(1, 11),
            SemError::UnderivableType("Int".into()),
        );
    }

    #[test]
    fn class_with_open_modifier() {
        ok("@open class A {}");
        ok("@open class A {} class B : A {}");
        err(
            "class A {} class B : A {}",
            pos(1, 22),
            SemError::UnderivableType("A".into()),
        );
    }

    #[test]
    fn non_field_ctor_arguments() {
        ok("class Foo(a: Int, b: Int)");
        ok("class Foo(let a: Int, b: Int)");
        ok("class Foo(a: Int, var b: Int)");
        err(
            "class Foo(a: Int, a: Int)",
            pos(1, 1),
            SemError::ShadowParam("a".into()),
        );
        err(
            "class Foo(a: Int, let a: Int)",
            pos(1, 1),
            SemError::ShadowParam("a".into()),
        );
        err(
            "class Foo(let a: Int, a: Int)",
            pos(1, 1),
            SemError::ShadowParam("a".into()),
        );
        err(
            "class Foo(a: Int) fun f(x: Foo) { x.a = 1; }",
            pos(1, 36),
            SemError::UnknownField("a".into(), "Foo".into()),
        );

        ok("class Foo(a: Int) fun foo() -> Foo { return Foo(1); } ");
    }

    #[test]
    fn field_defined_twice() {
        err(
            "class Foo { var a: Int; var a: Int; }",
            pos(1, 25),
            SemError::ShadowField("a".into()),
        );
        err(
            "class Foo(let a: Int) { var a: Int; }",
            pos(1, 25),
            SemError::ShadowField("a".into()),
        );
    }

    #[test]
    fn let_field_without_initialization() {
        err(
            "class Foo { let a: Int; }",
            pos(1, 13),
            SemError::LetMissingInitialization,
        );
    }

    #[test]
    fn field_self_assignment() {
        err(
            "class Foo(a: Int) { var b: Int = b; }",
            pos(1, 34),
            SemError::UnknownIdentifier("b".into()),
        );
    }

    #[test]
    fn test_generic_class() {
        ok("class A[T]");
        ok("class A[X, Y]");
        err(
            "class A[T, T]",
            pos(1, 12),
            SemError::TypeParamNameNotUnique("T".into()),
        );
        err("class A[]", pos(1, 1), SemError::TypeParamsExpected);
    }

    #[test]
    fn test_generic_argument() {
        ok("class A[T](val: T)");
        ok("class A[T](var val: T)");
        ok("class A[T](let val: T)");
    }

    #[test]
    fn test_generic_bound() {
        err(
            "class A[T: Foo]",
            pos(1, 12),
            SemError::UnknownType("Foo".into()),
        );
        ok("class Foo class A[T: Foo]");
        ok("trait Foo {} class A[T: Foo]");
    }

    #[test]
    fn test_generic_multiple_class_bounds() {
        err(
            "class Foo class Bar
            class A[T: Foo + Bar]",
            pos(2, 21),
            SemError::MultipleClassBounds,
        );
    }

    #[test]
    fn test_duplicate_trait_bound() {
        err(
            "trait Foo {}
            class A[T: Foo + Foo]",
            pos(2, 21),
            SemError::DuplicateTraitBound,
        );
    }

    #[test]
    fn test_super_class_with_superfluous_type_params() {
        err(
            "
            @open class A
            class B: A[Int] {}",
            pos(3, 22),
            SemError::WrongNumberTypeParams(0, 1),
        );
    }
}
<|endoftext|>


<commit_before>
//! Procedural macro attributes for defining intercom libraries.
//!
//! These macros implement various low level items that enable the associated
//! types to be instantiated and invoked over the COM interface protocol.
//!
//! **Instead of depending on this crate directly, the users should depend on
//! the main `intercom` crate isntead.**
//!
//! The split into two crates is an artificial limitation of Rust. The crates
//! defining procedural macros cannot export anything else than procedural
//! macros.
#![feature(proc_macro)]
#![allow(unused_imports)]
#![feature(catch_expr)]

extern crate intercom_common;
use intercom_common::attributes::*;

extern crate proc_macro;
use proc_macro::{TokenStream, LexError};

// Note the rustdoc comments on the [proc_macro_attribute] functions document
// "attributes", not "functions".
//
// While at "com_interface" function creates virtual tables, etc. when it is
// invoked, the attribute doesn't "creates" these. Instead the attribute just
// "defines" the trait/impl as a COM interface.
//
// The runtime documentation for developers is present in the expand_...
// methods below.

/// Defines an intercom interface.
///
/// ```rust,ignore
/// #[com_interface(IID, base?)]
/// trait Foo { /* ... */ }
/// ```
///
/// - `IID` - A unique ID of the interface used to query for it. Must be either
///           a valid GUID or `AUTO_GUID` specifier.
/// - `base` - Base interface. Defaults to `IUnknown` if not specified.
///
/// Associated types: `trait`, `impl Struct`
///
/// Intercom interfaces form the basis of the cross language API provided by
/// the user library. The interfaces define the available methods that can be
/// called through the interface pointers given to the clients.
///
/// Each interface automatically inherits from the base `IUnknown` interface,
/// which provides the clients a way to perform reference counting and the
/// ability to query for other interfaces the object might implement.
#[proc_macro_attribute]
pub fn com_interface(
    attr: TokenStream,
    tokens: TokenStream,
) -> TokenStream
{
    match expand_com_interface( &attr, tokens ) {
        Ok(t) => t,
        Err(e) => panic!( "{}", e ),
    }
}

/// Defines an implementation of an intercom interface.
///
/// ```rust,ignore
/// #[com_impl]
/// impl Foo for Struct { /* ... */ }
/// ```
///
/// Associated types: `impl Trait for Struct`, `impl Struct`
///
/// The attribute allows Intercom to implement raw FFI functions for the
/// interface's methods. Intercom allows the use of non-FFI compatible types
/// as arguments and return values for the interface methods. The automatic
/// FFI layer handles conversion between these types and FFI compatible types.
#[proc_macro_attribute]
pub fn com_impl(
    attr: TokenStream,
    tokens: TokenStream,
) -> TokenStream
{
    match expand_com_impl( &attr, tokens ) {
        Ok(t) => t,
        Err(e) => panic!( "{}", e ),
    }
}

/// Defines a COM class that implements one or more COM interfaces.
///
/// ```rust,ignore
/// #[com_class(CLSID, interfaces...)]
/// struct S { /* ... */ }
/// ```
///
/// - `CLSID` - A unique ID of the exposed class. The clients use the class ID
///             to specify the class when they want to construct an object.
///             The value must be a valid GUID, `AUTO_GUID` or `NO_GUID`.
/// - `interfaces` - Any number of interfaces that the class implements.
///
/// Associated types: `struct`, `enum`
///
/// If the `CLSID` is specified as `NO_GUID`, the class cannot be constructed
/// by the clients. It can still be returned as a return value from other
/// intercom methods.
#[proc_macro_attribute]
pub fn com_class(
    attr: TokenStream,
    tokens: TokenStream,
) -> TokenStream
{
    match expand_com_class( &attr, tokens ) {
        Ok(t) => t,
        Err(e) => panic!( "{}", e ),
    }
}

/// Defines the COM library.
///
/// ```rust,ignore
/// #[com_library(LIBID, classes...)]
/// # struct S;  // Unfortunately the attribute needs to be on SOMETHING.
/// ```
///
/// - `LIBID` - A unique ID that specifies the current intercom library. Must
///             be a valid GUID or `AUTO_GUID`.
/// - `classes` - List of intercom classes that can be constructed by the
///               clients.
///
/// Associated types: None
///
/// The attribute results in the implementation of the object creation
/// infrastructure that allows external clients to load the library and
/// instantiate the specified types.
#[proc_macro_attribute]
pub fn com_library(
    attr: TokenStream,
    tokens: TokenStream,
) -> TokenStream
{
    match expand_com_library( &attr, tokens ) {
        Ok(t) => t,
        Err(e) => panic!( "{}", e ),
    }
}

<commit_msg>Disable false positives from clippy<commit_after>
//! Procedural macro attributes for defining intercom libraries.
//!
//! These macros implement various low level items that enable the associated
//! types to be instantiated and invoked over the COM interface protocol.
//!
//! **Instead of depending on this crate directly, the users should depend on
//! the main `intercom` crate isntead.**
//!
//! The split into two crates is an artificial limitation of Rust. The crates
//! defining procedural macros cannot export anything else than procedural
//! macros.
#![feature(proc_macro)]
#![allow(unused_imports)]
#![feature(catch_expr)]

extern crate intercom_common;
use intercom_common::attributes::*;

extern crate proc_macro;
use proc_macro::{TokenStream, LexError};

// Note the rustdoc comments on the [proc_macro_attribute] functions document
// "attributes", not "functions".
//
// While at "com_interface" function creates virtual tables, etc. when it is
// invoked, the attribute doesn't "creates" these. Instead the attribute just
// "defines" the trait/impl as a COM interface.
//
// The runtime documentation for developers is present in the expand_...
// methods below.

/// Defines an intercom interface.
///
/// ```rust,ignore
/// #[com_interface(IID, base?)]
/// trait Foo { /* ... */ }
/// ```
///
/// - `IID` - A unique ID of the interface used to query for it. Must be either
///           a valid GUID or `AUTO_GUID` specifier.
/// - `base` - Base interface. Defaults to `IUnknown` if not specified.
///
/// Associated types: `trait`, `impl Struct`
///
/// Intercom interfaces form the basis of the cross language API provided by
/// the user library. The interfaces define the available methods that can be
/// called through the interface pointers given to the clients.
///
/// Each interface automatically inherits from the base `IUnknown` interface,
/// which provides the clients a way to perform reference counting and the
/// ability to query for other interfaces the object might implement.
#[proc_macro_attribute]
#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
pub fn com_interface(
    attr: TokenStream,
    tokens: TokenStream,
) -> TokenStream
{
    match expand_com_interface( &attr, tokens ) {
        Ok(t) => t,
        Err(e) => panic!( "{}", e ),
    }
}

/// Defines an implementation of an intercom interface.
///
/// ```rust,ignore
/// #[com_impl]
/// impl Foo for Struct { /* ... */ }
/// ```
///
/// Associated types: `impl Trait for Struct`, `impl Struct`
///
/// The attribute allows Intercom to implement raw FFI functions for the
/// interface's methods. Intercom allows the use of non-FFI compatible types
/// as arguments and return values for the interface methods. The automatic
/// FFI layer handles conversion between these types and FFI compatible types.
#[proc_macro_attribute]
#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
pub fn com_impl(
    attr: TokenStream,
    tokens: TokenStream,
) -> TokenStream
{
    match expand_com_impl( &attr, tokens ) {
        Ok(t) => t,
        Err(e) => panic!( "{}", e ),
    }
}

/// Defines a COM class that implements one or more COM interfaces.
///
/// ```rust,ignore
/// #[com_class(CLSID, interfaces...)]
/// struct S { /* ... */ }
/// ```
///
/// - `CLSID` - A unique ID of the exposed class. The clients use the class ID
///             to specify the class when they want to construct an object.
///             The value must be a valid GUID, `AUTO_GUID` or `NO_GUID`.
/// - `interfaces` - Any number of interfaces that the class implements.
///
/// Associated types: `struct`, `enum`
///
/// If the `CLSID` is specified as `NO_GUID`, the class cannot be constructed
/// by the clients. It can still be returned as a return value from other
/// intercom methods.
#[proc_macro_attribute]
#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
pub fn com_class(
    attr: TokenStream,
    tokens: TokenStream,
) -> TokenStream
{
    match expand_com_class( &attr, tokens ) {
        Ok(t) => t,
        Err(e) => panic!( "{}", e ),
    }
}

/// Defines the COM library.
///
/// ```rust,ignore
/// #[com_library(LIBID, classes...)]
/// # struct S;  // Unfortunately the attribute needs to be on SOMETHING.
/// ```
///
/// - `LIBID` - A unique ID that specifies the current intercom library. Must
///             be a valid GUID or `AUTO_GUID`.
/// - `classes` - List of intercom classes that can be constructed by the
///               clients.
///
/// Associated types: None
///
/// The attribute results in the implementation of the object creation
/// infrastructure that allows external clients to load the library and
/// instantiate the specified types.
#[proc_macro_attribute]
#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
pub fn com_library(
    attr: TokenStream,
    tokens: TokenStream,
) -> TokenStream
{
    match expand_com_library( &attr, tokens ) {
        Ok(t) => t,
        Err(e) => panic!( "{}", e ),
    }
}

<|endoftext|>


<commit_before><commit_msg>Fix build warnings in balance module.<commit_after><|endoftext|>


<commit_before><commit_msg>add twitch emotes to irc object<commit_after><|endoftext|>


<commit_before><commit_msg>Make cargo test work<commit_after><|endoftext|>


<commit_before><commit_msg>Rename a few things + compute memory usage<commit_after><|endoftext|>


<commit_before><commit_msg>Adding ServerSocket send<commit_after><|endoftext|>


<commit_before><commit_msg>Bug fix: borrowed value doesn't live long enough<commit_after><|endoftext|>


<commit_before><commit_msg>Fix missing Jump instruction<commit_after><|endoftext|>


<commit_before><commit_msg>Implemented comparisons with x register<commit_after><|endoftext|>


<commit_before><commit_msg>Cleanup add_er_8 instruction code<commit_after><|endoftext|>


<commit_before><commit_msg>Non-exhaustive `ColorTransform`<commit_after><|endoftext|>


<commit_before>mod statistics;
mod planner;

use kite::query::Query;
use kite::collectors::{Collector, DocumentMatch};
use byteorder::{ByteOrder, BigEndian};

use super::RocksDBIndexReader;
use doc_id_set::DocIdSet;
use segment::{Segment, SegmentReadError};
use search::statistics::{StatisticsReader, RocksDBStatisticsReader};
use search::planner::{SearchPlan, plan_query};
use search::planner::boolean_query::BooleanQueryOp;
use search::planner::score_function::{CombinatorScorer, ScoreFunctionOp};

impl<'a> RocksDBIndexReader<'a> {
    fn search_segment_boolean_phase<S: Segment>(&self, boolean_query: &Vec<BooleanQueryOp>, is_negated: bool, segment: &S) -> Result<DocIdSet, SegmentReadError> {
        // Execute boolean query
        let mut stack = Vec::new();
        for op in boolean_query.iter() {
            match *op {
                BooleanQueryOp::PushEmpty => {
                    stack.push(DocIdSet::new_filled(0));
                }
                BooleanQueryOp::PushFull => {
                    stack.push(DocIdSet::new_filled(65536));
                }
                BooleanQueryOp::PushTermDirectory(field_ref, term_ref) => {
                    match try!(segment.load_term_directory(field_ref, term_ref)) {
                        Some(doc_id_set) => stack.push(doc_id_set),
                        None => stack.push(DocIdSet::new_filled(0)),
                    }
                }
                BooleanQueryOp::PushDeletionList => {
                     match try!(segment.load_deletion_list()) {
                        Some(doc_id_set) => stack.push(doc_id_set),
                        None => stack.push(DocIdSet::new_filled(0)),
                    }
                }
                BooleanQueryOp::And => {
                    let b = stack.pop().expect("boolean query executor: stack underflow");
                    let a = stack.pop().expect("boolean query executor: stack underflow");
                    stack.push(a.intersection(&b));
                }
                BooleanQueryOp::Or => {
                    let b = stack.pop().expect("boolean query executor: stack underflow");
                    let a = stack.pop().expect("boolean query executor: stack underflow");
                    stack.push(a.union(&b));
                }
                BooleanQueryOp::AndNot => {
                    let b = stack.pop().expect("boolean query executor: stack underflow");
                    let a = stack.pop().expect("boolean query executor: stack underflow");
                    stack.push(a.exclusion(&b));
                }
            }
        }

        if !stack.len() == 1 {
            // This shouldn't be possible unless there's a bug in the planner
            panic!("boolean query executor: stack size too big ({})", stack.len());
        }
        let mut matches = stack.pop().unwrap();

        // Invert the list if the query is negated
        if is_negated {
            let total_docs = try!(segment.load_statistic(b"total_docs")).unwrap_or(0);
            let all_docs = DocIdSet::new_filled(total_docs as u32);
            matches = all_docs.exclusion(&matches);
        }

        Ok(matches)
    }

    fn score_doc<S: Segment>(&self, doc_id: u16, score_function: &Vec<ScoreFunctionOp>, segment: &S, mut stats: &mut RocksDBStatisticsReader) -> Result<f64, SegmentReadError> {
        // Execute score function
        let mut stack = Vec::new();
        for op in score_function.iter() {
            match *op {
                ScoreFunctionOp::Literal(val) => stack.push(val),
                ScoreFunctionOp::TermScorer(field_ref, term_ref, ref scorer) => {
                    // TODO: Check this isn't really slow
                    match try!(segment.load_term_directory(field_ref, term_ref)) {
                        Some(doc_id_set) => {
                            if doc_id_set.contains_doc(doc_id) {
                                // Read field length
                                // TODO: we only need this for BM25
                                let field_length_raw = try!(segment.load_stored_field_value_raw(doc_id, field_ref, b"len"));
                                let field_length = match field_length_raw {
                                    Some(value) => {
                                        let length_sqrt = (value[0] as f64) / 3.0 + 1.0;
                                        length_sqrt * length_sqrt
                                    }
                                    None => 1.0
                                };

                                // Read term frequency
                                let mut value_type = vec![b't', b'f'];
                                value_type.extend(term_ref.ord().to_string().as_bytes());
                                let term_frequency_raw = try!(segment.load_stored_field_value_raw(doc_id, field_ref, &value_type));
                                let term_frequency = match term_frequency_raw {
                                    Some(value) => BigEndian::read_i64(&value),
                                    None => 1,
                                };

                                let score = scorer.similarity_model.score(term_frequency as u32, field_length, try!(stats.total_tokens(field_ref)) as u64, try!(stats.total_docs(field_ref)) as u64, try!(stats.term_document_frequency(field_ref, term_ref)) as u64);
                                stack.push(score * scorer.boost);
                            } else {
                                stack.push(0.0f64);
                            }
                        }
                        None => stack.push(0.0f64),
                    }
                }
                ScoreFunctionOp::CombinatorScorer(num_vals, ref scorer) => {
                    let score = match *scorer {
                        CombinatorScorer::Avg => {
                            let mut total_score = 0.0f64;

                            for _ in 0..num_vals {
                                total_score += stack.pop().expect("document scorer: stack underflow");
                            }

                            total_score / num_vals as f64
                        }
                        CombinatorScorer::Max => {
                            let mut max_score = 0.0f64;

                            for _ in 0..num_vals {
                                let score = stack.pop().expect("document scorer: stack underflow");
                                if score > max_score {
                                    max_score = score
                                }
                            }

                            max_score
                        }
                    };

                    stack.push(score);
                }
            }
        }

        if !stack.len() == 1 {
            // This shouldn't be possible unless there's a bug in the planner
            panic!("document scorer: stack size too big ({})", stack.len());
        }

        Ok(stack.pop().expect("document scorer: stack underflow"))
    }

    fn search_segment<C: Collector, S: Segment>(&self, collector: &mut C, plan: &SearchPlan, segment: &S, mut stats: &mut RocksDBStatisticsReader) -> Result<(), SegmentReadError> {
        let matches = try!(self.search_segment_boolean_phase(&plan.boolean_query, plan.boolean_query_is_negated, segment));

        // Score documents and pass to collector
        for doc in matches.iter() {
            let score = try!(self.score_doc(doc, &plan.score_function, segment, &mut stats));

            let doc_ref = segment.doc_ref(doc);
            let doc_match = DocumentMatch::new_scored(doc_ref.as_u64(), score);
            collector.collect(doc_match);
        }

        Ok(())
    }

    pub fn search<C: Collector>(&self, collector: &mut C, query: &Query) -> Result<(), SegmentReadError> {
        // Plan query
        let plan = plan_query(&self, query, collector.needs_score());

        // Initialise statistics reader
        let mut stats = RocksDBStatisticsReader::new(&self);

        // Run query on each segment
        for segment in self.store.segments.iter_active(&self) {
            try!(self.search_segment(collector, &plan, &segment, &mut stats));
        }

        Ok(())
    }
}
<commit_msg>Make score_docs and search_segments statistics paramater generic<commit_after>mod statistics;
mod planner;

use kite::query::Query;
use kite::collectors::{Collector, DocumentMatch};
use byteorder::{ByteOrder, BigEndian};

use super::RocksDBIndexReader;
use doc_id_set::DocIdSet;
use segment::{Segment, SegmentReadError};
use search::statistics::{StatisticsReader, RocksDBStatisticsReader};
use search::planner::{SearchPlan, plan_query};
use search::planner::boolean_query::BooleanQueryOp;
use search::planner::score_function::{CombinatorScorer, ScoreFunctionOp};

impl<'a> RocksDBIndexReader<'a> {
    fn search_segment_boolean_phase<S: Segment>(&self, boolean_query: &Vec<BooleanQueryOp>, is_negated: bool, segment: &S) -> Result<DocIdSet, SegmentReadError> {
        // Execute boolean query
        let mut stack = Vec::new();
        for op in boolean_query.iter() {
            match *op {
                BooleanQueryOp::PushEmpty => {
                    stack.push(DocIdSet::new_filled(0));
                }
                BooleanQueryOp::PushFull => {
                    stack.push(DocIdSet::new_filled(65536));
                }
                BooleanQueryOp::PushTermDirectory(field_ref, term_ref) => {
                    match try!(segment.load_term_directory(field_ref, term_ref)) {
                        Some(doc_id_set) => stack.push(doc_id_set),
                        None => stack.push(DocIdSet::new_filled(0)),
                    }
                }
                BooleanQueryOp::PushDeletionList => {
                     match try!(segment.load_deletion_list()) {
                        Some(doc_id_set) => stack.push(doc_id_set),
                        None => stack.push(DocIdSet::new_filled(0)),
                    }
                }
                BooleanQueryOp::And => {
                    let b = stack.pop().expect("boolean query executor: stack underflow");
                    let a = stack.pop().expect("boolean query executor: stack underflow");
                    stack.push(a.intersection(&b));
                }
                BooleanQueryOp::Or => {
                    let b = stack.pop().expect("boolean query executor: stack underflow");
                    let a = stack.pop().expect("boolean query executor: stack underflow");
                    stack.push(a.union(&b));
                }
                BooleanQueryOp::AndNot => {
                    let b = stack.pop().expect("boolean query executor: stack underflow");
                    let a = stack.pop().expect("boolean query executor: stack underflow");
                    stack.push(a.exclusion(&b));
                }
            }
        }

        if !stack.len() == 1 {
            // This shouldn't be possible unless there's a bug in the planner
            panic!("boolean query executor: stack size too big ({})", stack.len());
        }
        let mut matches = stack.pop().unwrap();

        // Invert the list if the query is negated
        if is_negated {
            let total_docs = try!(segment.load_statistic(b"total_docs")).unwrap_or(0);
            let all_docs = DocIdSet::new_filled(total_docs as u32);
            matches = all_docs.exclusion(&matches);
        }

        Ok(matches)
    }

    fn score_doc<S: Segment, R: StatisticsReader>(&self, doc_id: u16, score_function: &Vec<ScoreFunctionOp>, segment: &S, mut stats: &mut R) -> Result<f64, SegmentReadError> {
        // Execute score function
        let mut stack = Vec::new();
        for op in score_function.iter() {
            match *op {
                ScoreFunctionOp::Literal(val) => stack.push(val),
                ScoreFunctionOp::TermScorer(field_ref, term_ref, ref scorer) => {
                    // TODO: Check this isn't really slow
                    match try!(segment.load_term_directory(field_ref, term_ref)) {
                        Some(doc_id_set) => {
                            if doc_id_set.contains_doc(doc_id) {
                                // Read field length
                                // TODO: we only need this for BM25
                                let field_length_raw = try!(segment.load_stored_field_value_raw(doc_id, field_ref, b"len"));
                                let field_length = match field_length_raw {
                                    Some(value) => {
                                        let length_sqrt = (value[0] as f64) / 3.0 + 1.0;
                                        length_sqrt * length_sqrt
                                    }
                                    None => 1.0
                                };

                                // Read term frequency
                                let mut value_type = vec![b't', b'f'];
                                value_type.extend(term_ref.ord().to_string().as_bytes());
                                let term_frequency_raw = try!(segment.load_stored_field_value_raw(doc_id, field_ref, &value_type));
                                let term_frequency = match term_frequency_raw {
                                    Some(value) => BigEndian::read_i64(&value),
                                    None => 1,
                                };

                                let score = scorer.similarity_model.score(term_frequency as u32, field_length, try!(stats.total_tokens(field_ref)) as u64, try!(stats.total_docs(field_ref)) as u64, try!(stats.term_document_frequency(field_ref, term_ref)) as u64);
                                stack.push(score * scorer.boost);
                            } else {
                                stack.push(0.0f64);
                            }
                        }
                        None => stack.push(0.0f64),
                    }
                }
                ScoreFunctionOp::CombinatorScorer(num_vals, ref scorer) => {
                    let score = match *scorer {
                        CombinatorScorer::Avg => {
                            let mut total_score = 0.0f64;

                            for _ in 0..num_vals {
                                total_score += stack.pop().expect("document scorer: stack underflow");
                            }

                            total_score / num_vals as f64
                        }
                        CombinatorScorer::Max => {
                            let mut max_score = 0.0f64;

                            for _ in 0..num_vals {
                                let score = stack.pop().expect("document scorer: stack underflow");
                                if score > max_score {
                                    max_score = score
                                }
                            }

                            max_score
                        }
                    };

                    stack.push(score);
                }
            }
        }

        if !stack.len() == 1 {
            // This shouldn't be possible unless there's a bug in the planner
            panic!("document scorer: stack size too big ({})", stack.len());
        }

        Ok(stack.pop().expect("document scorer: stack underflow"))
    }

    fn search_segment<C: Collector, S: Segment, R: StatisticsReader>(&self, collector: &mut C, plan: &SearchPlan, segment: &S, mut stats: &mut R) -> Result<(), SegmentReadError> {
        let matches = try!(self.search_segment_boolean_phase(&plan.boolean_query, plan.boolean_query_is_negated, segment));

        // Score documents and pass to collector
        for doc in matches.iter() {
            let score = try!(self.score_doc(doc, &plan.score_function, segment, stats));

            let doc_ref = segment.doc_ref(doc);
            let doc_match = DocumentMatch::new_scored(doc_ref.as_u64(), score);
            collector.collect(doc_match);
        }

        Ok(())
    }

    pub fn search<C: Collector>(&self, collector: &mut C, query: &Query) -> Result<(), SegmentReadError> {
        // Plan query
        let plan = plan_query(&self, query, collector.needs_score());

        // Initialise statistics reader
        let mut stats = RocksDBStatisticsReader::new(&self);

        // Run query on each segment
        for segment in self.store.segments.iter_active(&self) {
            try!(self.search_segment(collector, &plan, &segment, &mut stats));
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::io::{Reader, Writer, IoResult, IoError, ConnectionFailed};
use std::io::net::tcp::TcpStream;
use std::cell::RefCell;
use std::collections::HashSet;

use cmd::cmd;
use types::{RedisResult, Okay, Error, Value, InternalIoError,
            ToRedisArgs, FromRedisValue};
use parser::Parser;

struct ActualConnection {
    sock: TcpStream,
}

/// Represents a stateful redis TCP connection.
pub struct Connection {
    con: RefCell<ActualConnection>,
    db: i64,
}

/// Represents a pubsub connection.
pub struct PubSub {
    con: Connection,
    channels: HashSet<Vec<u8>>,
}

impl ActualConnection {

    pub fn new(host: &str, port: u16) -> IoResult<ActualConnection> {
        let sock = try!(TcpStream::connect(host, port));
        Ok(ActualConnection { sock: sock })
    }

    pub fn send_bytes(&mut self, bytes: &[u8]) -> RedisResult<Value> {
        let w = &mut self.sock as &mut Writer;
        match w.write(bytes) {
            Err(err) => {
                Err(Error::simple(
                    InternalIoError(err),
                    "Could not send command because of an IO error"))
            },
            Ok(_) => Ok(Okay)
        }
    }

    pub fn read_response(&mut self) -> RedisResult<Value> {
        let mut parser = Parser::new(&mut self.sock as &mut Reader);
        parser.parse_value()
    }
}

pub fn connect(host: &str, port: u16, db: i64) -> IoResult<Connection> {
    let con = try!(ActualConnection::new(host, port));
    let rv = Connection { con: RefCell::new(con), db: db };
    if db != 0 {
        match cmd("SELECT").arg(db).query::<Value>(&rv) {
            Ok(Okay) => {}
            _ => { return Err(IoError {
                kind: ConnectionFailed,
                desc: "Redis server refused to switch database",
                detail: None,
            }); }
        }
    }
    Ok(rv)
}

pub fn connect_pubsub(host: &str, port: u16) -> IoResult<PubSub> {
    Ok(PubSub {
        con: try!(connect(host, port, 0)),
        channels: HashSet::new(),
    })
}

/// A connection is an object that represents a single redis connection.  It
/// provides basic support for sending encoded commands into a redis connection
/// and to read a response from it.  It's bound to a single database and can
/// only be created from the client.
///
/// You generally do not much with this object other than passing it to
/// `Cmd` objects.
impl Connection {

    /// Sends an already encoded (packed) command into the TCP socket and
    /// reads the single response from it.
    pub fn req_packed_command(&self, cmd: &[u8]) -> RedisResult<Value> {
        let mut con = self.con.borrow_mut();
        try!(con.send_bytes(cmd));
        con.read_response()
    }

    /// Sends an already encoded (packed) command into the TCP socket and
    /// does not read a response.  This is useful for commands like
    /// `MONITOR` which yield multiple items.  This needs to be used with
    /// care because it changes the state of the connection.
    pub unsafe fn send_packed_command(&self, cmd: &[u8]) -> RedisResult<()> {
        try!(self.con.borrow_mut().send_bytes(cmd));
        Ok(())
    }

    /// Fetches a single response from the connection.  This is useful
    /// if used in combination with `send_packed_command`.
    pub unsafe fn recv_response(&self) -> RedisResult<Value> {
        self.con.borrow_mut().read_response()
    }

    /// Sends multiple already encoded (packed) command into the TCP socket
    /// and reads `count` responses from it.  This is used to implement
    /// pipelining.
    pub fn req_packed_commands(&self, cmd: &[u8],
            offset: uint, count: uint) -> RedisResult<Vec<Value>> {
        let mut con = self.con.borrow_mut();
        try!(con.send_bytes(cmd));
        let mut rv = vec![];
        for idx in range(0, offset + count) {
            let item = try!(con.read_response());
            if idx >= offset {
                rv.push(item);
            }
        }
        Ok(rv)
    }

    /// Returns the database this connection is bound to.
    pub fn get_db(&self) -> i64 {
        self.db
    }
}

/// The pubsub object provides convenient access to the redis pubsub
/// system.  Once created you can subscribe and unsubscribe from channels
/// and listen in on messages.
///
/// Example:
///
/// ```rust,no_run
/// let client = redis::Client::open("redis://127.0.0.1/").unwrap();
/// let mut pubsub = client.pubsub();
/// pubsub.subscribe("channel_1").unwrap();
/// pubsub.subscribe("channel_2").unwrap();
///
/// loop {
///     let (channel, message) : (String, String) = pubsub.get_message();
///     println!("channel '{}': {}", channel, message);
/// }
/// ```
impl PubSub {

    fn get_channel<T: ToRedisArgs>(&mut self, channel: &T) -> Vec<u8> {
        let mut chan = vec![];
        for item in channel.to_redis_args().iter() {
            chan.push_all(item[]);
        }
        chan
    }

    /// Subscribes to a new channel.
    pub fn subscribe<T: ToRedisArgs>(&mut self, channel: T) -> RedisResult<()> {
        let chan = self.get_channel(&channel);
        let _ : () = try!(cmd("SUBSCRIBE").arg(chan[]).query(&self.con));
        self.channels.insert(chan);
        Ok(())
    }

    /// Unsubscribes from a channel.
    pub fn unsubscribe<T: ToRedisArgs>(&mut self, channel: T) -> RedisResult<()> {
        let chan = self.get_channel(&channel);
        let _ : () = try!(cmd("UNSUBSCRIBE").arg(chan[]).query(&self.con));
        self.channels.remove(&chan);
        Ok(())
    }

    /// Fetches the next message from the pubsub connection.  Blocks until
    /// a message becomes available.  This currently does not provide a
    /// wait not to block :(
    pub fn get_message<C: FromRedisValue, M: FromRedisValue>(&self) -> RedisResult<(C, M)> {
        loop {
            unsafe {
                let (msg_type, channel, msg) : (String, C, M) =
                    try!(FromRedisValue::from_redis_value(&try!(self.con.recv_response())));
                if msg_type.as_slice() == "message" {
                    return Ok((channel, msg))
                }
            }
        }
    }
}
<commit_msg>Improved docs for get_message<commit_after>use std::io::{Reader, Writer, IoResult, IoError, ConnectionFailed};
use std::io::net::tcp::TcpStream;
use std::cell::RefCell;
use std::collections::HashSet;

use cmd::cmd;
use types::{RedisResult, Okay, Error, Value, InternalIoError,
            ToRedisArgs, FromRedisValue};
use parser::Parser;

struct ActualConnection {
    sock: TcpStream,
}

/// Represents a stateful redis TCP connection.
pub struct Connection {
    con: RefCell<ActualConnection>,
    db: i64,
}

/// Represents a pubsub connection.
pub struct PubSub {
    con: Connection,
    channels: HashSet<Vec<u8>>,
}

impl ActualConnection {

    pub fn new(host: &str, port: u16) -> IoResult<ActualConnection> {
        let sock = try!(TcpStream::connect(host, port));
        Ok(ActualConnection { sock: sock })
    }

    pub fn send_bytes(&mut self, bytes: &[u8]) -> RedisResult<Value> {
        let w = &mut self.sock as &mut Writer;
        match w.write(bytes) {
            Err(err) => {
                Err(Error::simple(
                    InternalIoError(err),
                    "Could not send command because of an IO error"))
            },
            Ok(_) => Ok(Okay)
        }
    }

    pub fn read_response(&mut self) -> RedisResult<Value> {
        let mut parser = Parser::new(&mut self.sock as &mut Reader);
        parser.parse_value()
    }
}

pub fn connect(host: &str, port: u16, db: i64) -> IoResult<Connection> {
    let con = try!(ActualConnection::new(host, port));
    let rv = Connection { con: RefCell::new(con), db: db };
    if db != 0 {
        match cmd("SELECT").arg(db).query::<Value>(&rv) {
            Ok(Okay) => {}
            _ => { return Err(IoError {
                kind: ConnectionFailed,
                desc: "Redis server refused to switch database",
                detail: None,
            }); }
        }
    }
    Ok(rv)
}

pub fn connect_pubsub(host: &str, port: u16) -> IoResult<PubSub> {
    Ok(PubSub {
        con: try!(connect(host, port, 0)),
        channels: HashSet::new(),
    })
}

/// A connection is an object that represents a single redis connection.  It
/// provides basic support for sending encoded commands into a redis connection
/// and to read a response from it.  It's bound to a single database and can
/// only be created from the client.
///
/// You generally do not much with this object other than passing it to
/// `Cmd` objects.
impl Connection {

    /// Sends an already encoded (packed) command into the TCP socket and
    /// reads the single response from it.
    pub fn req_packed_command(&self, cmd: &[u8]) -> RedisResult<Value> {
        let mut con = self.con.borrow_mut();
        try!(con.send_bytes(cmd));
        con.read_response()
    }

    /// Sends an already encoded (packed) command into the TCP socket and
    /// does not read a response.  This is useful for commands like
    /// `MONITOR` which yield multiple items.  This needs to be used with
    /// care because it changes the state of the connection.
    pub unsafe fn send_packed_command(&self, cmd: &[u8]) -> RedisResult<()> {
        try!(self.con.borrow_mut().send_bytes(cmd));
        Ok(())
    }

    /// Fetches a single response from the connection.  This is useful
    /// if used in combination with `send_packed_command`.
    pub unsafe fn recv_response(&self) -> RedisResult<Value> {
        self.con.borrow_mut().read_response()
    }

    /// Sends multiple already encoded (packed) command into the TCP socket
    /// and reads `count` responses from it.  This is used to implement
    /// pipelining.
    pub fn req_packed_commands(&self, cmd: &[u8],
            offset: uint, count: uint) -> RedisResult<Vec<Value>> {
        let mut con = self.con.borrow_mut();
        try!(con.send_bytes(cmd));
        let mut rv = vec![];
        for idx in range(0, offset + count) {
            let item = try!(con.read_response());
            if idx >= offset {
                rv.push(item);
            }
        }
        Ok(rv)
    }

    /// Returns the database this connection is bound to.
    pub fn get_db(&self) -> i64 {
        self.db
    }
}

/// The pubsub object provides convenient access to the redis pubsub
/// system.  Once created you can subscribe and unsubscribe from channels
/// and listen in on messages.
///
/// Example:
///
/// ```rust,no_run
/// let client = redis::Client::open("redis://127.0.0.1/").unwrap();
/// let mut pubsub = client.pubsub();
/// pubsub.subscribe("channel_1").unwrap();
/// pubsub.subscribe("channel_2").unwrap();
///
/// loop {
///     let (channel, message) : (String, String) = pubsub.get_message();
///     println!("channel '{}': {}", channel, message);
/// }
/// ```
impl PubSub {

    fn get_channel<T: ToRedisArgs>(&mut self, channel: &T) -> Vec<u8> {
        let mut chan = vec![];
        for item in channel.to_redis_args().iter() {
            chan.push_all(item[]);
        }
        chan
    }

    /// Subscribes to a new channel.
    pub fn subscribe<T: ToRedisArgs>(&mut self, channel: T) -> RedisResult<()> {
        let chan = self.get_channel(&channel);
        let _ : () = try!(cmd("SUBSCRIBE").arg(chan[]).query(&self.con));
        self.channels.insert(chan);
        Ok(())
    }

    /// Unsubscribes from a channel.
    pub fn unsubscribe<T: ToRedisArgs>(&mut self, channel: T) -> RedisResult<()> {
        let chan = self.get_channel(&channel);
        let _ : () = try!(cmd("UNSUBSCRIBE").arg(chan[]).query(&self.con));
        self.channels.remove(&chan);
        Ok(())
    }

    /// Fetches the next message from the pubsub connection.  Blocks until
    /// a message becomes available.  This currently does not provide a
    /// wait not to block :(
    ///
    /// The return value is in the format ``(channel, message)``.
    pub fn get_message<C: FromRedisValue, M: FromRedisValue>(&self) -> RedisResult<(C, M)> {
        loop {
            unsafe {
                let (msg_type, channel, msg) : (String, C, M) =
                    try!(FromRedisValue::from_redis_value(&try!(self.con.recv_response())));
                if msg_type.as_slice() == "message" {
                    return Ok((channel, msg))
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>// 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.

#[feature(asm)];
#[feature(macro_rules)];

extern mod extra;

pub mod aes;
pub mod aesni;
pub mod aessafe;
pub mod blockmodes;
pub mod buffer;
mod cryptoutil;
pub mod digest;
pub mod hmac;
pub mod mac;
pub mod md5;
pub mod pbkdf2;
pub mod rc4;
pub mod scrypt;
pub mod sha1;
pub mod sha2;
pub mod symmetriccipher;
pub mod util;
<commit_msg>Forgot to update test.rs<commit_after>// 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.

#[feature(asm)];
#[feature(macro_rules)];
#[feature(simd)];

extern mod extra;

pub mod aes;
pub mod aesni;
pub mod aessafe;
pub mod blockmodes;
pub mod buffer;
mod cryptoutil;
pub mod digest;
pub mod hmac;
pub mod mac;
pub mod md5;
pub mod pbkdf2;
pub mod rc4;
pub mod scrypt;
pub mod sha1;
pub mod sha2;
pub mod symmetriccipher;
pub mod util;
<|endoftext|>


<commit_before>extern crate regex;

use std::io::prelude::*;
use std::io::BufReader;
use std::fs::File;

use regex::Regex;

fn main() {
    let file = File::open("test-input.txt").unwrap();
    let reader = BufReader::new(&file);

    let regex = Regex::new(r".*").unwrap();
    for wrapped_line in reader.lines() {
        let line = wrapped_line.unwrap();
        let maybe_match = regex.captures(line.as_str());
        match maybe_match {
            Some(capture) => {}
            None => panic!("No match for '{}'", line),
        }

    }
}
<commit_msg>Part 1 solved.<commit_after>extern crate regex;

use std::io::prelude::*;
use std::io::BufReader;
use std::fs::File;

use regex::Regex;

#[derive(Debug)]
struct Reindeer {
    name: String,
    speed: isize,
    speed_time: isize,
    rest_time: isize,
}

impl Reindeer {
    fn distance_traveled(&self, seconds: isize) -> isize {
        let mut distance = 0;
        let mut seconds = seconds;
        while seconds > 0 {
            if seconds > self.speed_time {
                distance += self.speed * self.speed_time;
                seconds -= self.speed_time;
            } else {
                distance += self.speed * seconds;
                break;
            }
            seconds -= self.rest_time;
        }
        return distance;
    }
}

fn main() {
    let file = File::open("input.txt").unwrap();
    let reader = BufReader::new(&file);

    let regex = Regex::new(r"(?P<name>[[:alpha:]]+) can fly (?P<speed>[[:digit:]]+) km/s for (?P<speed_time>[[:digit:]]+) seconds, but then must rest for (?P<rest_time>[[:digit:]]+) seconds.").unwrap();
    let mut reindeers: Vec<Reindeer> = Vec::with_capacity(10);
    for wrapped_line in reader.lines() {
        let line = wrapped_line.unwrap();
        let maybe_match = regex.captures(line.as_str());
        match maybe_match {
            Some(capture) => {
                let name = capture.name("name").unwrap().as_str();
                let speed = capture.name("speed").unwrap().as_str().parse::<isize>().unwrap();
                let speed_time =
                    capture.name("speed_time").unwrap().as_str().parse::<isize>().unwrap();
                let rest_time =
                    capture.name("rest_time").unwrap().as_str().parse::<isize>().unwrap();
                reindeers.push(Reindeer {
                    name: String::from(name),
                    speed: speed,
                    speed_time: speed_time,
                    rest_time: rest_time,
                });

            }
            None => panic!("No match for '{}'", line),
        }
    }
    let time_in_secs = 2503;
    for r in reindeers {
        println!("{:10} traveled {:4} km in {:5} seconds",
                 r.name,
                 r.distance_traveled(time_in_secs),
                 time_in_secs);
    }
}
<|endoftext|>