<commit_before>use rustc::traits::{self, Reveal, SelectionContext};

use eval_context::EvalContext;
use memory::Pointer;

use rustc::hir::def_id::DefId;
use rustc::ty::fold::TypeFoldable;
use rustc::ty::subst::Substs;
use rustc::ty::{self, Ty, TyCtxt};
use syntax::codemap::DUMMY_SP;
use syntax::ast;

use error::EvalResult;

impl<'a, 'tcx> EvalContext<'a, 'tcx> {

    pub(crate) fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> {
        // Do the initial selection for the obligation. This yields the shallow result we are
        // looking for -- that is, what specific impl.
        self.tcx.infer_ctxt((), Reveal::All).enter(|infcx| {
            let mut selcx = traits::SelectionContext::new(&infcx);

            let obligation = traits::Obligation::new(
                traits::ObligationCause::misc(DUMMY_SP, ast::DUMMY_NODE_ID),
                trait_ref.to_poly_trait_predicate(),
            );
            let selection = selcx.select(&obligation).unwrap().unwrap();

            // Currently, we use a fulfillment context to completely resolve all nested obligations.
            // This is because they can inform the inference of the impl's type parameters.
            let mut fulfill_cx = traits::FulfillmentContext::new();
            let vtable = selection.map(|predicate| {
                fulfill_cx.register_predicate_obligation(&infcx, predicate);
            });
            infcx.drain_fulfillment_cx_or_panic(DUMMY_SP, &mut fulfill_cx, &vtable)
        })
    }

    /// Creates a dynamic vtable for the given type and vtable origin. This is used only for
    /// objects.
    ///
    /// The `trait_ref` encodes the erased self type. Hence if we are
    /// making an object `Foo<Trait>` from a value of type `Foo<T>`, then
    /// `trait_ref` would map `T:Trait`.
    pub fn get_vtable(&mut self, ty: Ty<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> EvalResult<'tcx, Pointer> {
        let tcx = self.tcx;

        debug!("get_vtable(trait_ref={:?})", trait_ref);

        let methods: Vec<_> = traits::supertraits(tcx, trait_ref).flat_map(|trait_ref| {
            match self.fulfill_obligation(trait_ref) {
                // Should default trait error here?
                traits::VtableDefaultImpl(_) |
                traits::VtableBuiltin(_) => {
                    Vec::new().into_iter()
                }

                traits::VtableImpl(traits::VtableImplData { impl_def_id: id, substs, .. }) => {
                    self.get_vtable_methods(id, substs)
                        .into_iter()
                        .map(|opt_mth| opt_mth.map(|mth| {
                            self.memory.create_fn_ptr(mth.method.def_id, mth.substs)
                        }))
                        .collect::<Vec<_>>()
                        .into_iter()
                }

                traits::VtableClosure(
                    traits::VtableClosureData {
                        closure_def_id,
                        substs,
                        ..
                    }
                ) => {
                    let instance = ::eval_context::resolve_closure(self.tcx, closure_def_id, substs, ty::ClosureKind::FnOnce);
                    vec![Some(self.memory.create_fn_alloc(instance))].into_iter()
                }

                // turn a function definition into a Fn trait object
                traits::VtableFnPointer(traits::VtableFnPointerData { fn_ty, .. }) => {
                    match fn_ty.sty {
                        ty::TyFnDef(did, substs, _) => {
                            let instance = ty::Instance {
                                def: ty::InstanceDef::FnPtrShim(did, fn_ty),
                                substs,
                            };
                            vec![Some(self.memory.create_fn_alloc(instance))].into_iter()
                        },
                        ty::TyFnPtr(_) => {
                            unimplemented!();
                        },
                        _ => bug!("bad VtableFnPointer fn_ty: {:#?}", fn_ty.sty),
                    }
                }

                traits::VtableObject(ref data) => {
                    // this would imply that the Self type being erased is
                    // an object type; this cannot happen because we
                    // cannot cast an unsized type into a trait object
                    bug!("cannot get vtable for an object type: {:?}",
                         data);
                }

                vtable @ traits::VtableParam(..) => {
                    bug!("resolved vtable for {:?} to bad vtable {:?} in trans",
                         trait_ref,
                         vtable);
                }
            }
        }).collect();

        let size = self.type_size(trait_ref.self_ty())?.expect("can't create a vtable for an unsized type");
        let align = self.type_align(trait_ref.self_ty())?;

        let ptr_size = self.memory.pointer_size();
        let vtable = self.memory.allocate(ptr_size * (3 + methods.len() as u64), ptr_size)?;

        // in case there is no drop function to be called, this still needs to be initialized
        self.memory.write_usize(vtable, 0)?;
        let drop_in_place = self.tcx.lang_items.drop_in_place_fn().expect("drop_in_place lang item not available");
        if let ty::TyAdt(adt_def, substs) = trait_ref.self_ty().sty {
            if adt_def.has_dtor(self.tcx) {
                let env = self.tcx.empty_parameter_environment();
                let def = if self.tcx.type_needs_drop_given_env(ty, &env) {
                    ty::InstanceDef::DropGlue(drop_in_place, Some(ty))
                } else {
                    ty::InstanceDef::DropGlue(drop_in_place, None)
                };
                let instance = ty::Instance { substs, def };
                let fn_ptr = self.memory.create_fn_alloc(instance);
                self.memory.write_ptr(vtable, fn_ptr)?;
            }
        }

        self.memory.write_usize(vtable.offset(ptr_size), size)?;
        self.memory.write_usize(vtable.offset(ptr_size * 2), align)?;

        for (i, method) in methods.into_iter().enumerate() {
            if let Some(method) = method {
                self.memory.write_ptr(vtable.offset(ptr_size * (3 + i as u64)), method)?;
            }
        }

        self.memory.mark_static_initalized(vtable.alloc_id, false)?;

        Ok(vtable)
    }

    pub fn read_drop_type_from_vtable(&self, vtable: Pointer) -> EvalResult<'tcx, Option<ty::Instance<'tcx>>> {
        let drop_fn = self.memory.read_ptr(vtable)?;

        // just a sanity check
        assert_eq!(drop_fn.offset, 0);

        // some values don't need to call a drop impl, so the value is null
        if drop_fn == Pointer::from_int(0) {
            Ok(None)
        } else {
            self.memory.get_fn(drop_fn.alloc_id).map(Some)
        }
    }

    pub fn read_size_and_align_from_vtable(&self, vtable: Pointer) -> EvalResult<'tcx, (u64, u64)> {
        let pointer_size = self.memory.pointer_size();
        let size = self.memory.read_usize(vtable.offset(pointer_size))?;
        let align = self.memory.read_usize(vtable.offset(pointer_size * 2))?;
        Ok((size, align))
    }

    fn get_vtable_methods(&mut self, impl_id: DefId, substs: &'tcx Substs<'tcx>) -> Vec<Option<ImplMethod<'tcx>>> {
        debug!("get_vtable_methods(impl_id={:?}, substs={:?}", impl_id, substs);

        let trait_id = match self.tcx.impl_trait_ref(impl_id) {
            Some(t_id) => t_id.def_id,
            None       => bug!("make_impl_vtable: don't know how to \
                                make a vtable for a type impl!")
        };

        self.tcx.populate_implementations_for_trait_if_necessary(trait_id);

        self.tcx
            .associated_items(trait_id)
            // Filter out non-method items.
            .filter_map(|trait_method_type| {
                if trait_method_type.kind != ty::AssociatedKind::Method {
                    return None;
                }
                debug!("get_vtable_methods: trait_method_type={:?}",
                       trait_method_type);

                let name = trait_method_type.name;

                // Some methods cannot be called on an object; skip those.
                if !self.tcx.is_vtable_safe_method(trait_id, &trait_method_type) {
                    debug!("get_vtable_methods: not vtable safe");
                    return Some(None);
                }

                debug!("get_vtable_methods: trait_method_type={:?}",
                       trait_method_type);

                // the method may have some early-bound lifetimes, add
                // regions for those
                let method_substs = Substs::for_item(self.tcx, trait_method_type.def_id,
                                                     |_, _| self.tcx.mk_region(ty::ReErased),
                                                     |_, _| self.tcx.types.err);

                // The substitutions we have are on the impl, so we grab
                // the method type from the impl to substitute into.
                let mth = get_impl_method(self.tcx, method_substs, impl_id, substs, name);

                debug!("get_vtable_methods: mth={:?}", mth);

                // If this is a default method, it's possible that it
                // relies on where clauses that do not hold for this
                // particular set of type parameters. Note that this
                // method could then never be called, so we do not want to
                // try and trans it, in that case. Issue #23435.
                if mth.is_provided {
                    let predicates = self.tcx.item_predicates(trait_method_type.def_id).instantiate_own(self.tcx, mth.substs);
                    if !self.normalize_and_test_predicates(predicates.predicates) {
                        debug!("get_vtable_methods: predicates do not hold");
                        return Some(None);
                    }
                }

                Some(Some(mth))
            })
            .collect()
    }

    /// Normalizes the predicates and checks whether they hold.  If this
    /// returns false, then either normalize encountered an error or one
    /// of the predicates did not hold. Used when creating vtables to
    /// check for unsatisfiable methods.
    fn normalize_and_test_predicates(&mut self, predicates: Vec<ty::Predicate<'tcx>>) -> bool {
        debug!("normalize_and_test_predicates(predicates={:?})",
               predicates);

        self.tcx.infer_ctxt((), Reveal::All).enter(|infcx| {
            let mut selcx = SelectionContext::new(&infcx);
            let mut fulfill_cx = traits::FulfillmentContext::new();
            let cause = traits::ObligationCause::dummy();
            let traits::Normalized { value: predicates, obligations } =
                traits::normalize(&mut selcx, cause.clone(), &predicates);
            for obligation in obligations {
                fulfill_cx.register_predicate_obligation(&infcx, obligation);
            }
            for predicate in predicates {
                let obligation = traits::Obligation::new(cause.clone(), predicate);
                fulfill_cx.register_predicate_obligation(&infcx, obligation);
            }

            fulfill_cx.select_all_or_error(&infcx).is_ok()
        })
    }

    pub(crate) fn resolve_associated_const(
        &self,
        def_id: DefId,
        substs: &'tcx Substs<'tcx>,
    ) -> ty::Instance<'tcx> {
        if let Some(trait_id) = self.tcx.trait_of_item(def_id) {
            let trait_ref = ty::Binder(ty::TraitRef::new(trait_id, substs));
            let vtable = self.fulfill_obligation(trait_ref);
            if let traits::VtableImpl(vtable_impl) = vtable {
                let name = self.tcx.item_name(def_id);
                let assoc_const_opt = self.tcx.associated_items(vtable_impl.impl_def_id)
                    .find(|item| item.kind == ty::AssociatedKind::Const && item.name == name);
                if let Some(assoc_const) = assoc_const_opt {
                    return ty::Instance::new(assoc_const.def_id, vtable_impl.substs);
                }
            }
        }
        ty::Instance::new(def_id, substs)
    }
}

#[derive(Debug)]
pub(super) struct ImplMethod<'tcx> {
    pub(super) method: ty::AssociatedItem,
    pub(super) substs: &'tcx Substs<'tcx>,
    pub(super) is_provided: bool,
}

/// Locates the applicable definition of a method, given its name.
pub(super) fn get_impl_method<'a, 'tcx>(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    substs: &'tcx Substs<'tcx>,
    impl_def_id: DefId,
    impl_substs: &'tcx Substs<'tcx>,
    name: ast::Name,
) -> ImplMethod<'tcx> {
    assert!(!substs.needs_infer());

    let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap();
    let trait_def = tcx.lookup_trait_def(trait_def_id);

    match trait_def.ancestors(impl_def_id).defs(tcx, name, ty::AssociatedKind::Method).next() {
        Some(node_item) => {
            let substs = tcx.infer_ctxt((), Reveal::All).enter(|infcx| {
                let substs = substs.rebase_onto(tcx, trait_def_id, impl_substs);
                let substs = traits::translate_substs(&infcx, impl_def_id,
                                                      substs, node_item.node);
                tcx.lift(&substs).unwrap_or_else(|| {
                    bug!("trans::meth::get_impl_method: translate_substs \
                          returned {:?} which contains inference types/regions",
                         substs);
                })
            });
            ImplMethod {
                method: node_item.item,
                substs,
                is_provided: node_item.node.is_from_trait(),
            }
        }
        None => {
            bug!("method {:?} not found in {:?}", name, impl_def_id)
        }
    }
}
<commit_msg>Reuse more rustc code instead of copying it into miri<commit_after>use rustc::traits::{self, Reveal};

use eval_context::EvalContext;
use memory::Pointer;

use rustc::hir::def_id::DefId;
use rustc::ty::subst::Substs;
use rustc::ty::{self, Ty};
use syntax::codemap::DUMMY_SP;
use syntax::ast;

use error::EvalResult;

impl<'a, 'tcx> EvalContext<'a, 'tcx> {

    pub(crate) fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> {
        // Do the initial selection for the obligation. This yields the shallow result we are
        // looking for -- that is, what specific impl.
        self.tcx.infer_ctxt((), Reveal::All).enter(|infcx| {
            let mut selcx = traits::SelectionContext::new(&infcx);

            let obligation = traits::Obligation::new(
                traits::ObligationCause::misc(DUMMY_SP, ast::DUMMY_NODE_ID),
                trait_ref.to_poly_trait_predicate(),
            );
            let selection = selcx.select(&obligation).unwrap().unwrap();

            // Currently, we use a fulfillment context to completely resolve all nested obligations.
            // This is because they can inform the inference of the impl's type parameters.
            let mut fulfill_cx = traits::FulfillmentContext::new();
            let vtable = selection.map(|predicate| {
                fulfill_cx.register_predicate_obligation(&infcx, predicate);
            });
            infcx.drain_fulfillment_cx_or_panic(DUMMY_SP, &mut fulfill_cx, &vtable)
        })
    }

    /// Creates a dynamic vtable for the given type and vtable origin. This is used only for
    /// objects.
    ///
    /// The `trait_ref` encodes the erased self type. Hence if we are
    /// making an object `Foo<Trait>` from a value of type `Foo<T>`, then
    /// `trait_ref` would map `T:Trait`.
    pub fn get_vtable(&mut self, ty: Ty<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> EvalResult<'tcx, Pointer> {
        debug!("get_vtable(trait_ref={:?})", trait_ref);

        let size = self.type_size(trait_ref.self_ty())?.expect("can't create a vtable for an unsized type");
        let align = self.type_align(trait_ref.self_ty())?;

        let ptr_size = self.memory.pointer_size();
        let methods = ::rustc::traits::get_vtable_methods(self.tcx, trait_ref);
        let vtable = self.memory.allocate(ptr_size * (3 + methods.count() as u64), ptr_size)?;

        let drop = ::eval_context::resolve_drop_in_place(self.tcx, ty);
        let drop = self.memory.create_fn_alloc(drop);
        self.memory.write_ptr(vtable, drop)?;

        self.memory.write_usize(vtable.offset(ptr_size), size)?;
        self.memory.write_usize(vtable.offset(ptr_size * 2), align)?;

        for (i, method) in ::rustc::traits::get_vtable_methods(self.tcx, trait_ref).enumerate() {
            if let Some((def_id, substs)) = method {
                let instance = ::eval_context::resolve(self.tcx, def_id, substs);
                let fn_ptr = self.memory.create_fn_alloc(instance);
                self.memory.write_ptr(vtable.offset(ptr_size * (3 + i as u64)), fn_ptr)?;
            }
        }

        self.memory.mark_static_initalized(vtable.alloc_id, false)?;

        Ok(vtable)
    }

    pub fn read_drop_type_from_vtable(&self, vtable: Pointer) -> EvalResult<'tcx, Option<ty::Instance<'tcx>>> {
        let drop_fn = self.memory.read_ptr(vtable)?;

        // just a sanity check
        assert_eq!(drop_fn.offset, 0);

        // some values don't need to call a drop impl, so the value is null
        if drop_fn == Pointer::from_int(0) {
            Ok(None)
        } else {
            self.memory.get_fn(drop_fn.alloc_id).map(Some)
        }
    }

    pub fn read_size_and_align_from_vtable(&self, vtable: Pointer) -> EvalResult<'tcx, (u64, u64)> {
        let pointer_size = self.memory.pointer_size();
        let size = self.memory.read_usize(vtable.offset(pointer_size))?;
        let align = self.memory.read_usize(vtable.offset(pointer_size * 2))?;
        Ok((size, align))
    }

    pub(crate) fn resolve_associated_const(
        &self,
        def_id: DefId,
        substs: &'tcx Substs<'tcx>,
    ) -> ty::Instance<'tcx> {
        if let Some(trait_id) = self.tcx.trait_of_item(def_id) {
            let trait_ref = ty::Binder(ty::TraitRef::new(trait_id, substs));
            let vtable = self.fulfill_obligation(trait_ref);
            if let traits::VtableImpl(vtable_impl) = vtable {
                let name = self.tcx.item_name(def_id);
                let assoc_const_opt = self.tcx.associated_items(vtable_impl.impl_def_id)
                    .find(|item| item.kind == ty::AssociatedKind::Const && item.name == name);
                if let Some(assoc_const) = assoc_const_opt {
                    return ty::Instance::new(assoc_const.def_id, vtable_impl.substs);
                }
            }
        }
        ty::Instance::new(def_id, substs)
    }
}
<|endoftext|>


<commit_before>pub use lang::Lang;

#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum Script {
    Latin,
    Cyrillic,
    Arabic,
    Devanagari,
    Ethiopic,
    Hebrew,
    Cmn,
    Kat,
}

static FUNCS : &'static [(Script, fn(char) -> bool)] = &[
    (Script::Cyrillic  , is_cyrillic),
    (Script::Latin     , is_latin),
    (Script::Arabic    , is_arabic),
    (Script::Devanagari, is_devanagari),
    (Script::Hebrew    , is_hebrew),
    (Script::Ethiopic  , is_ethiopic),
    (Script::Kat       , is_kat),
    (Script::Cmn       , is_cmn)
];

pub fn detect_script(text : String) -> Option<Script> {
    let mut max_score = 0;
    let mut result = None;

    for &(script, func) in FUNCS {
        let mut current_score = 0;
        for ch in text.chars() {
            if func(ch) { current_score += 1; }
        }
        if current_score > max_score {
            max_score = current_score;
            result = Some(script);
        }
    }

    result
}

// TODO: Use http://jrgraphix.net/research/unicode_blocks.php
fn is_cyrillic(ch: char) -> bool {
   match ch {
       '\u{0400}'...'\u{0484}' |
       '\u{0487}'...'\u{052F}' |
       '\u{2DE0}'...'\u{2DFF}' |
       '\u{A640}'...'\u{A69D}' |
       '\u{1D2B}' |
       '\u{1D78}' |
       '\u{A69F}' => true,
       _ => false
   }
}

// https://en.wikipedia.org/wiki/Latin_script_in_Unicode
fn is_latin(ch : char) -> bool {
    match ch {
        'a'...'z' |
        'A'...'Z' |
        '\u{0080}'...'\u{00FF}' |
        '\u{0100}'...'\u{017F}' |
        '\u{0180}'...'\u{024F}' |
        '\u{0250}'...'\u{02AF}' |
        '\u{1D00}'...'\u{1D7F}' |
        '\u{1D80}'...'\u{1DBF}' |
        '\u{1E00}'...'\u{1EFF}' |
        '\u{2100}'...'\u{214F}' |
        '\u{2C60}'...'\u{2C7F}' |
        '\u{A720}'...'\u{A7FF}' |
        '\u{AB30}'...'\u{AB6F}' => true,
        _ => false
    }
}

// Based on https://en.wikipedia.org/wiki/Arabic_script_in_Unicode
fn is_arabic(ch : char) -> bool {
    match ch {
        '\u{0600}'...'\u{06FF}' |
        '\u{0750}'...'\u{07FF}' |
        '\u{08A0}'...'\u{08FF}' |
        '\u{FB50}'...'\u{FDFF}' |
        '\u{FE70}'...'\u{FEFF}' |
        '\u{10E60}'...'\u{10E7F}' |
        '\u{1EE00}'...'\u{1EEFF}' => true,
        _ => false
    }
}

// Based on https://en.wikipedia.org/wiki/Devanagari#Unicode
fn is_devanagari(ch : char) -> bool {
    match ch {
        '\u{0900}'...'\u{097F}' |
        '\u{A8E0}'...'\u{A8FF}' |
        '\u{1CD0}'...'\u{1CFF}' => true,
        _ => false
    }
}

// Based on https://www.key-shortcut.com/en/writing-systems/ethiopian-script/
fn is_ethiopic(ch : char) -> bool {
    match ch {
        '\u{1200}'...'\u{139F}' |
        '\u{2D80}'...'\u{2DDF}' |
        '\u{AB00}'...'\u{AB2F}' => true,
        _ => false
    }
}

// Based on https://en.wikipedia.org/wiki/Hebrew_(Unicode_block)
fn is_hebrew(ch : char) -> bool {
    match ch {
        '\u{0590}'...'\u{05FF}' => true,
        _ => false
    }
}

// Is Georgian char?
fn is_kat(ch : char) -> bool {
   match ch {
       '\u{10A0}'...'\u{10FF}' => true,
       _ => false
   }
}

// TODO: likely not the full set of possible chars..
fn is_cmn(ch : char) -> bool {
    match ch {
        '\u{2E80}'...'\u{2E99}' |
        '\u{2E9B}'...'\u{2EF3}' |
        '\u{2F00}'...'\u{2FD5}' |
        '\u{3005}' |
        '\u{3007}' |
        '\u{3021}'...'\u{3029}' |
        '\u{3038}'...'\u{303B}' |
        '\u{3400}'...'\u{4DB5}' |
        '\u{4E00}'...'\u{9FCC}' |
        '\u{F900}'...'\u{FA6D}' |
        '\u{FA70}'...'\u{FAD9}' => true,
        _ => false
    }
}

#[cfg(test)]
mod tests {
   use super::Script;
   use super::is_cyrillic;
   use super::is_latin;
   use super::is_kat;
   use super::is_ethiopic;
   use super::detect_script;

   #[test]
   fn test_detect_script() {
       assert_eq!(detect_script("1234567890-,;!".to_string()), None);

       // One script
       assert_eq!(detect_script("Hello!".to_string()), Some(Script::Latin));
       assert_eq!(detect_script("Привет всем!".to_string()), Some(Script::Cyrillic));
       assert_eq!(detect_script("ქართული ენა მსოფლიო ".to_string()), Some(Script::Kat));
       assert_eq!(detect_script("県見夜上温国阪題富販".to_string()), Some(Script::Cmn));
       assert_eq!(detect_script(" ككل حوالي 1.6، ومعظم الناس ".to_string()), Some(Script::Arabic));
       assert_eq!(detect_script("हिमालयी वन चिड़िया (जूथेरा सालिमअली) चिड़िया की एक प्रजाति है".to_string()), Some(Script::Devanagari));
       assert_eq!(detect_script("היסטוריה והתפתחות של האלפבית העברי".to_string()), Some(Script::Hebrew));
       assert_eq!(detect_script("የኢትዮጵያ ፌዴራላዊ ዴሞክራሲያዊሪፐብሊክ".to_string()), Some(Script::Ethiopic));

       // Mixed scripts
       assert_eq!(detect_script("Привет всем! Этот текст на русском with some English.".to_string()), Some(Script::Cyrillic));
       assert_eq!(detect_script("Russian word любовь means love.".to_string()), Some(Script::Latin));
   }

   #[test]
   fn test_is_latin() {
       assert_eq!(is_latin('z'), true);
       assert_eq!(is_latin('A'), true);
       assert_eq!(is_latin('č'), true);
       assert_eq!(is_latin('š'), true);
       assert_eq!(is_latin('Ĵ'), true);

       assert_eq!(is_latin('ж'), false);
   }

   #[test]
   fn test_is_cyrillic() {
       assert_eq!(is_cyrillic('а'), true);
       assert_eq!(is_cyrillic('Я'), true);
       assert_eq!(is_cyrillic('Ґ'), true);
       assert_eq!(is_cyrillic('ї'), true);
       assert_eq!(is_cyrillic('Ꙕ'), true);

       assert_eq!(is_cyrillic('L'), false);
   }

   #[test]
   fn test_is_ethiopic() {
       assert_eq!(is_ethiopic('ፚ'), true);
       assert_eq!(is_ethiopic('ᎀ'), true);

       assert_eq!(is_ethiopic('а'), false);
       assert_eq!(is_cyrillic('L'), false);
   }

   #[test]
   fn test_is_kat() {
       assert_eq!(is_kat('რ'), true);
       assert_eq!(is_kat('ж'), false);
   }
}
<commit_msg>Finalize detect_script<commit_after>#![allow(dead_code)]
#![allow(unused_assignments)]

#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum Script {
    Latin,
    Cyrillic,
    Arabic,
    Devanagari,
    Ethiopic,
    Hebrew,
    Cmn,
    Kat
}

macro_rules! check_scripts {
    ( $text:ident, $( $script:expr => $is_script:ident ),* ) => {
        {
            let mut max_score = 0;
            let mut result = None;
            let mut current_score = 0;
            let half = $text.len() / 2;

            $(
                current_score = 0;
                for ch in $text.chars() {
                    if $is_script(ch) { current_score += 1; }
                }
                if current_score > half {
                    return Some($script);
                }
                if current_score > max_score {
                    max_score = current_score;
                    result = Some($script);
                }
            )*
            result
        }
    };
}

pub fn detect_script(text : String) -> Option<Script> {
    check_scripts![
        text,
        Script::Cyrillic => is_cyrillic,
        Script::Latin => is_latin,
        Script::Arabic => is_arabic,
        Script::Devanagari => is_devanagari,
        Script::Hebrew => is_hebrew,
        Script::Ethiopic => is_ethiopic,
        Script::Kat => is_kat,
        Script::Cmn => is_cmn
    ]
}

#[inline(always)]
fn is_cyrillic(ch: char) -> bool {
   match ch {
       '\u{0400}'...'\u{0484}' |
       '\u{0487}'...'\u{052F}' |
       '\u{2DE0}'...'\u{2DFF}' |
       '\u{A640}'...'\u{A69D}' |
       '\u{1D2B}' |
       '\u{1D78}' |
       '\u{A69F}' => true,
       _ => false
   }
}

// https://en.wikipedia.org/wiki/Latin_script_in_Unicode
#[inline(always)]
fn is_latin(ch : char) -> bool {
    match ch {
        'a'...'z' |
        'A'...'Z' |
        '\u{0080}'...'\u{00FF}' |
        '\u{0100}'...'\u{017F}' |
        '\u{0180}'...'\u{024F}' |
        '\u{0250}'...'\u{02AF}' |
        '\u{1D00}'...'\u{1D7F}' |
        '\u{1D80}'...'\u{1DBF}' |
        '\u{1E00}'...'\u{1EFF}' |
        '\u{2100}'...'\u{214F}' |
        '\u{2C60}'...'\u{2C7F}' |
        '\u{A720}'...'\u{A7FF}' |
        '\u{AB30}'...'\u{AB6F}' => true,
        _ => false
    }
}

// Based on https://en.wikipedia.org/wiki/Arabic_script_in_Unicode
#[inline(always)]
fn is_arabic(ch : char) -> bool {
    match ch {
        '\u{0600}'...'\u{06FF}' |
        '\u{0750}'...'\u{07FF}' |
        '\u{08A0}'...'\u{08FF}' |
        '\u{FB50}'...'\u{FDFF}' |
        '\u{FE70}'...'\u{FEFF}' |
        '\u{10E60}'...'\u{10E7F}' |
        '\u{1EE00}'...'\u{1EEFF}' => true,
        _ => false
    }
}

// Based on https://en.wikipedia.org/wiki/Devanagari#Unicode
#[inline(always)]
fn is_devanagari(ch : char) -> bool {
    match ch {
        '\u{0900}'...'\u{097F}' |
        '\u{A8E0}'...'\u{A8FF}' |
        '\u{1CD0}'...'\u{1CFF}' => true,
        _ => false
    }
}

// Based on https://www.key-shortcut.com/en/writing-systems/ethiopian-script/
#[inline(always)]
fn is_ethiopic(ch : char) -> bool {
    match ch {
        '\u{1200}'...'\u{139F}' |
        '\u{2D80}'...'\u{2DDF}' |
        '\u{AB00}'...'\u{AB2F}' => true,
        _ => false
    }
}

// Based on https://en.wikipedia.org/wiki/Hebrew_(Unicode_block)
#[inline(always)]
fn is_hebrew(ch : char) -> bool {
    match ch {
        '\u{0590}'...'\u{05FF}' => true,
        _ => false
    }
}

// Is Georgian char?
#[inline(always)]
fn is_kat(ch : char) -> bool {
   match ch {
       '\u{10A0}'...'\u{10FF}' => true,
       _ => false
   }
}

// TODO: likely not the full set of possible chars..
#[inline(always)]
fn is_cmn(ch : char) -> bool {
    match ch {
        '\u{2E80}'...'\u{2E99}' |
        '\u{2E9B}'...'\u{2EF3}' |
        '\u{2F00}'...'\u{2FD5}' |
        '\u{3005}' |
        '\u{3007}' |
        '\u{3021}'...'\u{3029}' |
        '\u{3038}'...'\u{303B}' |
        '\u{3400}'...'\u{4DB5}' |
        '\u{4E00}'...'\u{9FCC}' |
        '\u{F900}'...'\u{FA6D}' |
        '\u{FA70}'...'\u{FAD9}' => true,
        _ => false
    }
}

#[cfg(test)]
mod tests {
    use super::Script;
    use super::is_cyrillic;
    use super::is_latin;
    use super::is_kat;
    use super::is_ethiopic;
    use super::detect_script;

    #[test]
    fn test_detect_script() {
        assert_eq!(detect_script("1234567890-,;!".to_string()), None);

        // One script
        assert_eq!(detect_script("Hello!".to_string()), Some(Script::Latin));
        assert_eq!(detect_script("Привет всем!".to_string()), Some(Script::Cyrillic));
        assert_eq!(detect_script("ქართული ენა მსოფლიო ".to_string()), Some(Script::Kat));
        assert_eq!(detect_script("県見夜上温国阪題富販".to_string()), Some(Script::Cmn));
        assert_eq!(detect_script(" ككل حوالي 1.6، ومعظم الناس ".to_string()), Some(Script::Arabic));
        assert_eq!(detect_script("हिमालयी वन चिड़िया (जूथेरा सालिमअली) चिड़िया की एक प्रजाति है".to_string()), Some(Script::Devanagari));
        assert_eq!(detect_script("היסטוריה והתפתחות של האלפבית העברי".to_string()), Some(Script::Hebrew));
        assert_eq!(detect_script("የኢትዮጵያ ፌዴራላዊ ዴሞክራሲያዊሪፐብሊክ".to_string()), Some(Script::Ethiopic));

        // Mixed scripts
        assert_eq!(detect_script("Привет! Текст на русском with some English.".to_string()), Some(Script::Cyrillic));
        assert_eq!(detect_script("Russian word любовь means love.".to_string()), Some(Script::Latin));
    }

    #[test]
    fn test_is_latin() {
        assert_eq!(is_latin('z'), true);
        assert_eq!(is_latin('A'), true);
        assert_eq!(is_latin('č'), true);
        assert_eq!(is_latin('š'), true);
        assert_eq!(is_latin('Ĵ'), true);

        assert_eq!(is_latin('ж'), false);
    }

    #[test]
    fn test_is_cyrillic() {
        assert_eq!(is_cyrillic('а'), true);
        assert_eq!(is_cyrillic('Я'), true);
        assert_eq!(is_cyrillic('Ґ'), true);
        assert_eq!(is_cyrillic('ї'), true);
        assert_eq!(is_cyrillic('Ꙕ'), true);

        assert_eq!(is_cyrillic('L'), false);
    }

    #[test]
    fn test_is_ethiopic() {
        assert_eq!(is_ethiopic('ፚ'), true);
        assert_eq!(is_ethiopic('ᎀ'), true);

        assert_eq!(is_ethiopic('а'), false);
        assert_eq!(is_cyrillic('L'), false);
    }

    #[test]
    fn test_is_kat() {
        assert_eq!(is_kat('რ'), true);
        assert_eq!(is_kat('ж'), false);
    }
}
<|endoftext|>


<commit_before>use std::iter::Iterator;
use std::collections::HashSet;
use std::hash::{Hash, Hasher, SipHasher};

pub trait SearchSpace {
    type State: Hash;
    type Action;
    type Iterator: Iterator<Item=(Self::Action, Self::State)>;

    fn expand(&self, state: &Self::State) -> Self::Iterator;
    fn is_goal(&self, state: &Self::State) -> bool;
}

pub fn depth_first_search<S>(space: &S, start: S::State) -> Option<Vec<S::Action>>
where S: SearchSpace {
    if space.is_goal(&start) {
        return Some(vec![]);
    }

    let mut path = Vec::new();
    let mut visited = HashSet::new();
    let mut frontier = vec![space.expand(&start)];

    loop {
        let result = match frontier.last_mut() {
            None => return None,
            Some(&mut ref mut iter) => {
                match iter.next() {
                    None => {
                        path.pop();
                        None
                    },
                    Some((action, state)) => {
                        let mut hasher = SipHasher::new();
                        state.hash(&mut hasher);
                        let hash = hasher.finish();
                        if visited.contains(&hash) {
                            continue;
                        }
                        visited.insert(hash);

                        path.push(action);
                        if space.is_goal(&state) {
                            return Some(path);
                        }
                        Some(space.expand(&state))
                    }
                }
            }
        };
        match result {
            None => {
                frontier.pop();
            }
            Some(iter) => {
                frontier.push(iter);
            }
        };
    }
}

#[cfg(test)]
pub mod tests {
    use std::vec::IntoIter;
    use super::*;

    #[test]
    pub fn test_depth_first_search() {
        struct S {
            goal: i32,
        }

        #[derive(Debug, PartialEq)]
        enum Direction {
            Left,
            Right
        }

        impl SearchSpace for S {
            type State = i32;
            type Action = Direction;
            type Iterator = IntoIter<(Self::Action, Self::State)>;

            fn expand(&self, state: &Self::State) -> Self::Iterator {
                if *state == 0 {
                    vec![(Direction::Left, 1), (Direction::Right, 2)].into_iter()
                } else if *state == 1 {
                    vec![(Direction::Left, 3), (Direction::Right, 4)].into_iter()
                } else if *state == 2 {
                    vec![(Direction::Left, 2)].into_iter()
                } else {
                    vec![].into_iter()
                }
            }

            fn is_goal(&self, state: &i32) -> bool {
                return self.goal == *state
            }
        }

        impl S {
            fn solve(start: i32, goal: i32) -> Option<Vec<Direction>> {
                let space = S { goal: goal };
                depth_first_search(&space, start)
            }
        }

        assert_eq!(S::solve(0, 0).unwrap(), vec![]);
        assert_eq!(S::solve(0, 1).unwrap(), vec![Direction::Left]);
        assert_eq!(S::solve(0, 2).unwrap(), vec![Direction::Right]);
        assert_eq!(S::solve(0, 3).unwrap(), vec![Direction::Left, Direction::Left]);
        assert_eq!(S::solve(0, 4).unwrap(), vec![Direction::Left, Direction::Right]);
        assert_eq!(S::solve(2, 2).unwrap(), vec![]);
        assert!(S::solve(2, 0).is_none());
        assert!(S::solve(5, 0).is_none());
    }
}
<commit_msg>Move hashing logic out to HashTracker<commit_after>use std::iter::Iterator;
use std::collections::HashSet;
use std::marker::PhantomData;
use std::hash::{Hash, Hasher, SipHasher};

pub trait SearchSpace {
    type State: Hash;
    type Action;
    type Iterator: Iterator<Item=(Self::Action, Self::State)>;

    fn expand(&self, state: &Self::State) -> Self::Iterator;
    fn is_goal(&self, state: &Self::State) -> bool;
}

struct HashTracker<T> {
    hash_set: HashSet<u64>,
    phantom: PhantomData<T>,
}

impl<T> HashTracker<T> where T: Hash {
    fn new() -> HashTracker<T> {
        HashTracker {
            hash_set: HashSet::new(),
            phantom: PhantomData
        }
    }

    fn contains(&mut self, item: &T) -> bool {
        let mut hasher = SipHasher::new();
        item.hash(&mut hasher);
        let hash = hasher.finish();
        let contains = self.hash_set.contains(&hash);
        if !contains {
            self.hash_set.insert(hash);
        }
        contains
    }
}

pub fn depth_first_search<S>(space: &S, start: S::State) -> Option<Vec<S::Action>>
where S: SearchSpace {
    if space.is_goal(&start) {
        return Some(vec![]);
    }

    let mut path = Vec::new();
    let mut visited = HashTracker::new();
    let mut frontier = vec![space.expand(&start)];

    loop {
        let result = match frontier.last_mut() {
            None => {
                return None
            },
            Some(&mut ref mut iter) => {
                match iter.next() {
                    None => {
                        path.pop();
                        None
                    },
                    Some((action, state)) => {
                        if visited.contains(&state) {
                            continue;
                        }
                        path.push(action);
                        if space.is_goal(&state) {
                            return Some(path);
                        }
                        Some(space.expand(&state))
                    }
                }
            }
        };
        match result {
            None => {
                frontier.pop();
            }
            Some(iter) => {
                frontier.push(iter);
            }
        };
    }
}

#[cfg(test)]
pub mod tests {
    use std::vec::IntoIter;
    use super::*;

    #[test]
    pub fn test_depth_first_search() {
        struct S {
            goal: i32,
        }

        #[derive(Debug, PartialEq)]
        enum Direction {
            Left,
            Right
        }

        impl SearchSpace for S {
            type State = i32;
            type Action = Direction;
            type Iterator = IntoIter<(Self::Action, Self::State)>;

            fn expand(&self, state: &Self::State) -> Self::Iterator {
                if *state == 0 {
                    vec![(Direction::Left, 1), (Direction::Right, 2)].into_iter()
                } else if *state == 1 {
                    vec![(Direction::Left, 3), (Direction::Right, 4)].into_iter()
                } else if *state == 2 {
                    vec![(Direction::Left, 2)].into_iter()
                } else {
                    vec![].into_iter()
                }
            }

            fn is_goal(&self, state: &i32) -> bool {
                return self.goal == *state
            }
        }

        impl S {
            fn solve(start: i32, goal: i32) -> Option<Vec<Direction>> {
                let space = S { goal: goal };
                depth_first_search(&space, start)
            }
        }

        assert_eq!(S::solve(0, 0).unwrap(), vec![]);
        assert_eq!(S::solve(0, 1).unwrap(), vec![Direction::Left]);
        assert_eq!(S::solve(0, 2).unwrap(), vec![Direction::Right]);
        assert_eq!(S::solve(0, 3).unwrap(), vec![Direction::Left, Direction::Left]);
        assert_eq!(S::solve(0, 4).unwrap(), vec![Direction::Left, Direction::Right]);
        assert_eq!(S::solve(2, 2).unwrap(), vec![]);
        assert!(S::solve(2, 0).is_none());
        assert!(S::solve(5, 0).is_none());
    }
}
<|endoftext|>


<commit_before>use crate::dictionary::CONF_LINE_SECRET_MODIFIER__VISIBLE;
use std::fmt;

/// Defines the line-break position when displaying the secret string.
const LINE_WIDTH: usize = 20;

/// One character of the secret string.
#[derive(Clone, Debug, PartialEq)]
struct HangmanChar {
    character: char,
    visible: bool,
}

/// Format HangmanChar.
impl fmt::Display for HangmanChar {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        if self.visible {
            write!(f, "{}", self.character)
        } else {
            write!(f, "_")
        }
    }
}

/// The secret
#[derive(Debug, PartialEq)]
pub struct Secret {
    raw: String,
    hangman_chars: Vec<HangmanChar>,
    pub chars_to_guess: usize,
}

impl Secret {
    /// Constructor.
    pub fn new(secretstr: &str) -> Self {
        // parse `secretsstr`, flip 'visible' every CONF_LINE_SECRET_MODIFIER__VISIBLE
        let w: Vec<HangmanChar> = secretstr
            .chars()
            // for every * found flip v_acc
            .scan(false, |v_acc, c| {
                *v_acc ^= c == CONF_LINE_SECRET_MODIFIER__VISIBLE;
                if c == CONF_LINE_SECRET_MODIFIER__VISIBLE {
                    Some(None)
                } else {
                    Some(Some(HangmanChar {
                        character: c,
                        visible: *v_acc,
                    }))
                }
            })
            // omit None and unwrap
            .filter_map(|s| s)
            //.inspect(|ref x| println!("after scan:\t{:?}", x))
            .collect();

        let chars_to_guess = w.iter().filter(|hc| !hc.visible).count();

        Self {
            raw: secretstr.to_string(),
            hangman_chars: w,
            chars_to_guess,
        }
    }

    /// Process a guess and modify the game state.
    pub fn guess(&mut self, character: char) -> bool {
        let mut found = false;
        for h_char in &mut self.hangman_chars {
            if h_char.visible == false && h_char.character.eq_ignore_ascii_case(&character) {
                h_char.visible = true;
                found = true;
            }
        }

        found
    }

    /// We disclose all characters when all lives are used and the
    /// game is over.
    pub fn disclose_all(&mut self) {
        // Disclose the secret
        for hc in &mut self.hangman_chars {
            hc.visible = true;
        }
    }

    /// Method used to find out if the user has won.
    pub fn is_fully_disclosed(&self) -> bool {
        self.hangman_chars.iter().all(|c| c.visible)
    }

    /// Information used to calculate how much of the image should
    /// be disclosed.
    pub fn hidden_chars(&self) -> usize {
        self.hangman_chars.iter().filter(|hc| !hc.visible).count()
    }

    /// Used in case the secret was not guessed and we want to inject
    /// it to the dictionary again.
    pub fn to_raw_string(&self) -> String {
        self.raw.clone()
    }
}

impl fmt::Display for Secret {
    /// Graphical representation of the game state.
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let mut linebreak = false;
        for (n, c) in self.hangman_chars.iter().enumerate() {
            if n % LINE_WIDTH == 0 {
                linebreak = true
            };
            if n == 0 {
                linebreak = false
            };
            if linebreak && (c.character == ' ') {
                linebreak = false;
                writeln!(f, " {}", c)?
            } else {
                write!(f, " {}", c)?
            }
        }
        writeln!(f)
    }
}

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

    /// Game simulation
    #[test]
    fn test_secret() {
        let mut secret = Secret::new("_ab _cd");

        assert_eq!(secret.to_raw_string(), "_ab _cd");
        assert_eq!(format!("{}", secret), " a b   _ _\n");
        assert_eq!(secret.hidden_chars(), 2);
        assert!(!secret.is_fully_disclosed());

        secret.guess('x');

        assert_eq!(secret.to_raw_string(), "_ab _cd");
        assert_eq!(format!("{}", secret), " a b   _ _\n");
        assert_eq!(secret.hidden_chars(), 2);
        assert!(!secret.is_fully_disclosed());

        secret.guess('d');

        assert_eq!(secret.to_raw_string(), "_ab _cd");
        assert_eq!(format!("{}", secret), " a b   _ d\n");
        assert_eq!(secret.hidden_chars(), 1);
        assert!(!secret.is_fully_disclosed());

        secret.disclose_all();
        assert_eq!(secret.to_raw_string(), "_ab _cd");
        assert_eq!(format!("{}", secret), " a b   c d\n");
        assert_eq!(secret.hidden_chars(), 0);
        assert!(secret.is_fully_disclosed());
    }
}
<commit_msg>simplify if statement<commit_after>use crate::dictionary::CONF_LINE_SECRET_MODIFIER__VISIBLE;
use std::fmt;

/// Defines the line-break position when displaying the secret string.
const LINE_WIDTH: usize = 20;

/// One character of the secret string.
#[derive(Clone, Debug, PartialEq)]
struct HangmanChar {
    character: char,
    visible: bool,
}

/// Format HangmanChar.
impl fmt::Display for HangmanChar {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        if self.visible {
            write!(f, "{}", self.character)
        } else {
            write!(f, "_")
        }
    }
}

/// The secret
#[derive(Debug, PartialEq)]
pub struct Secret {
    raw: String,
    hangman_chars: Vec<HangmanChar>,
    pub chars_to_guess: usize,
}

impl Secret {
    /// Constructor.
    pub fn new(secretstr: &str) -> Self {
        // parse `secretsstr`, flip 'visible' every CONF_LINE_SECRET_MODIFIER__VISIBLE
        let w: Vec<HangmanChar> = secretstr
            .chars()
            // for every * found flip v_acc
            .scan(false, |v_acc, c| {
                *v_acc ^= c == CONF_LINE_SECRET_MODIFIER__VISIBLE;
                if c == CONF_LINE_SECRET_MODIFIER__VISIBLE {
                    Some(None)
                } else {
                    Some(Some(HangmanChar {
                        character: c,
                        visible: *v_acc,
                    }))
                }
            })
            // omit None and unwrap
            .filter_map(|s| s)
            //.inspect(|ref x| println!("after scan:\t{:?}", x))
            .collect();

        let chars_to_guess = w.iter().filter(|hc| !hc.visible).count();

        Self {
            raw: secretstr.to_string(),
            hangman_chars: w,
            chars_to_guess,
        }
    }

    /// Process a guess and modify the game state.
    pub fn guess(&mut self, character: char) -> bool {
        let mut found = false;
        for h_char in &mut self.hangman_chars {
            if !h_char.visible && h_char.character.eq_ignore_ascii_case(&character) {
                h_char.visible = true;
                found = true;
            }
        }

        found
    }

    /// We disclose all characters when all lives are used and the
    /// game is over.
    pub fn disclose_all(&mut self) {
        // Disclose the secret
        for hc in &mut self.hangman_chars {
            hc.visible = true;
        }
    }

    /// Method used to find out if the user has won.
    pub fn is_fully_disclosed(&self) -> bool {
        self.hangman_chars.iter().all(|c| c.visible)
    }

    /// Information used to calculate how much of the image should
    /// be disclosed.
    pub fn hidden_chars(&self) -> usize {
        self.hangman_chars.iter().filter(|hc| !hc.visible).count()
    }

    /// Used in case the secret was not guessed and we want to inject
    /// it to the dictionary again.
    pub fn to_raw_string(&self) -> String {
        self.raw.clone()
    }
}

impl fmt::Display for Secret {
    /// Graphical representation of the game state.
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let mut linebreak = false;
        for (n, c) in self.hangman_chars.iter().enumerate() {
            if n % LINE_WIDTH == 0 {
                linebreak = true
            };
            if n == 0 {
                linebreak = false
            };
            if linebreak && (c.character == ' ') {
                linebreak = false;
                writeln!(f, " {}", c)?
            } else {
                write!(f, " {}", c)?
            }
        }
        writeln!(f)
    }
}

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

    /// Game simulation
    #[test]
    fn test_secret() {
        let mut secret = Secret::new("_ab _cd");

        assert_eq!(secret.to_raw_string(), "_ab _cd");
        assert_eq!(format!("{}", secret), " a b   _ _\n");
        assert_eq!(secret.hidden_chars(), 2);
        assert!(!secret.is_fully_disclosed());

        secret.guess('x');

        assert_eq!(secret.to_raw_string(), "_ab _cd");
        assert_eq!(format!("{}", secret), " a b   _ _\n");
        assert_eq!(secret.hidden_chars(), 2);
        assert!(!secret.is_fully_disclosed());

        secret.guess('d');

        assert_eq!(secret.to_raw_string(), "_ab _cd");
        assert_eq!(format!("{}", secret), " a b   _ d\n");
        assert_eq!(secret.hidden_chars(), 1);
        assert!(!secret.is_fully_disclosed());

        secret.disclose_all();
        assert_eq!(secret.to_raw_string(), "_ab _cd");
        assert_eq!(format!("{}", secret), " a b   c d\n");
        assert_eq!(secret.hidden_chars(), 0);
        assert!(secret.is_fully_disclosed());
    }
}
<|endoftext|>


<commit_before>use rocket_contrib::Json;
use rocket::request::FromParam;
use rocket::http::RawStr;
use ldap_hook::user::LdapUser;
use ldap_hook::config::LdapConfig;
use ldap_hook::ldap_search;

#[derive(PartialEq, Eq)]
pub enum LdapQueryType {
	Nick,
	UID,
}

impl<'a> FromParam<'a> for LdapQueryType {
	type Error = &'a RawStr;
	fn from_param(param: &'a RawStr) -> Result<Self, Self::Error> {
		if param == "nick" {
			Ok(LdapQueryType::Nick)
		} else if param == "uid" {
			Ok(LdapQueryType::UID)
		} else {
			Err(param)
		}
	}
}

// Dummy endpoints so that the front-end won't complain
#[get("/search/<query_type>")]
pub fn empty_search(query_type: LdapQueryType) -> Json<Vec<String>> { Json(vec![]) }

#[get("/search/<query_type>/<query>")]
pub fn search(query_type: LdapQueryType, query: String) -> Json<Vec<LdapUser>> {
	let id_list: Vec<String> = query.split(",").map(|s| s.to_string()).collect();

	let mut result_list: Vec<LdapUser> = vec![];

	// TODO: Load the config somewhere more sensible
	let ldap_config = match LdapConfig::load("config.toml") {
		Ok(ldap_config) => ldap_config,
		Err(msg) => {
			println!("Could not load config.toml: {}", msg);
			panic!();
		}
	};

	for id in &id_list{
		let filter = if query_type == LdapQueryType::UID {
			format!("(uid={})", id)
		} else {
			format!("(nickname=*{}*)", id)
		};
		let res = ldap_search(&ldap_config, filter.as_str());

		match res.ok() {
			None => { break; }
			Some(list) => {
				for entry in list {
					if !result_list.contains(&entry) {
						result_list.push(entry)
					}
				}
			}
		}
	}

	Json(result_list)
}
<commit_msg>Prettify panic attack<commit_after>use rocket_contrib::Json;
use rocket::request::FromParam;
use rocket::http::RawStr;
use ldap_hook::user::LdapUser;
use ldap_hook::config::LdapConfig;
use ldap_hook::ldap_search;

#[derive(PartialEq, Eq)]
pub enum LdapQueryType {
	Nick,
	UID,
}

impl<'a> FromParam<'a> for LdapQueryType {
	type Error = &'a RawStr;
	fn from_param(param: &'a RawStr) -> Result<Self, Self::Error> {
		if param == "nick" {
			Ok(LdapQueryType::Nick)
		} else if param == "uid" {
			Ok(LdapQueryType::UID)
		} else {
			Err(param)
		}
	}
}

// Dummy endpoints so that the front-end won't complain
#[get("/search/<query_type>")]
pub fn empty_search(query_type: LdapQueryType) -> Json<Vec<String>> { Json(vec![]) }

#[get("/search/<query_type>/<query>")]
pub fn search(query_type: LdapQueryType, query: String) -> Json<Vec<LdapUser>> {
	let id_list: Vec<String> = query.split(",").map(|s| s.to_string()).collect();

	let mut result_list: Vec<LdapUser> = vec![];

	// TODO: Load the config somewhere more sensible
	let ldap_config = match LdapConfig::load("config.toml") {
		Ok(ldap_config) => ldap_config,
		Err(msg) => {
			panic!("Could not load config.toml: {}", msg);
		}
	};

	for id in &id_list{
		let filter = if query_type == LdapQueryType::UID {
			format!("(uid={})", id)
		} else {
			format!("(nickname=*{}*)", id)
		};
		let res = ldap_search(&ldap_config, filter.as_str());

		match res.ok() {
			None => { break; }
			Some(list) => {
				for entry in list {
					if !result_list.contains(&entry) {
						result_list.push(entry)
					}
				}
			}
		}
	}

	Json(result_list)
}
<|endoftext|>


<commit_before>use std::ffi::CString;
use std::fmt;
use std::io::{IoResult, IoError, File, Open, ReadWrite};
use std::os::unix::prelude::AsRawFd;
use libc::c_int;
use libc::consts::os::bsd44::{AF_INET6, SOCK_DGRAM};
use libc::funcs::bsd43::socket;
use libc::funcs::bsd44::ioctl;
use libc::funcs::posix88::unistd::close;
use libc::types::os::common::bsd44::in6_addr;
use c_interop::*;

const DEVICE_PATH: &'static str = "/dev/net/tun";

// TODO Make not a constant
const MTU_SIZE: usize = 1500;

#[derive(Show, Copy, Clone, Eq, PartialEq, Hash)]
pub enum TunTapType {
	Tun,
	Tap
}

pub struct TunTap {
	pub file: File,
	sock: c_int,
	if_name: [u8; IFNAMSIZ],
	if_index: c_int
}

impl Drop for TunTap {
	fn drop(&mut self) {
		unsafe { close(self.sock) };
	}
}

impl fmt::Show for TunTap {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		write!(f, "Tun({:?})", self.get_name())
	}
}

impl TunTap {
	pub fn create(typ: TunTapType) -> TunTap {
		TunTap::create_named(typ, &CString::from_slice(&[]))
	}

	pub fn create_named(typ: TunTapType, name: &CString) -> TunTap {
		let (file, if_name) = TunTap::create_if(typ, name);
		let (sock, if_index) = TunTap::create_socket(if_name);

		TunTap {
			file: file,
			sock: sock,
			if_name: if_name,
			if_index: if_index
		}
	}

	fn create_if(typ: TunTapType, name: &CString) -> (File, [u8; IFNAMSIZ]) {
		let name_slice = name.as_bytes_with_nul();
		if name_slice.len() > IFNAMSIZ {
			panic!("Interface name too long, max length is {}", IFNAMSIZ - 1);
		}

		let path = Path::new(DEVICE_PATH);
		let file = match File::open_mode(&path, Open, ReadWrite) {
			Err(why) => panic!("Couldn't open tun device '{}': {}", path.display(), why.desc),
			Ok(file) => file,
		};

		let mut req = ioctl_flags_data {
			ifr_name: {
				let mut buffer = [0u8; IFNAMSIZ];
				buffer.clone_from_slice(name_slice);
				buffer
			},
			ifr_flags: match typ {
				TunTapType::Tun => IFF_TUN,
				TunTapType::Tap => IFF_TAP
			}
		};

		let res = unsafe { ioctl(file.as_raw_fd(), TUNSETIFF, &mut req) };
		if res < 0 {
			panic!("{}", IoError::last_error());
		}

		(file, req.ifr_name)
	}

	fn create_socket(if_name: [u8; IFNAMSIZ]) -> (c_int, c_int) {
		let sock = unsafe { socket(AF_INET6, SOCK_DGRAM, 0) };
		if sock < 0 {
			panic!("{}", IoError::last_error());
		}

		let mut req = ioctl_ifindex_data {
			ifr_name: if_name,
			ifr_ifindex: -1
		};

		let res = unsafe { ioctl(sock, SIOCGIFINDEX, &mut req) };
		if res < 0 {
			let err = IoError::last_error();
			unsafe { close(sock) };
			panic!("{}", err);
		}

		(sock, req.ifr_ifindex)
	}

	pub fn get_name(&self) -> CString {
		let nul_pos = match self.if_name.as_slice().position_elem(&0) {
			Some(p) => p,
			None => panic!("Device name should be null-terminated")
		};

		CString::from_slice(self.if_name.slice_to(nul_pos))
	}

	pub fn up(&self) {
		let mut req = ioctl_flags_data {
			ifr_name: self.if_name,
			ifr_flags: 0
		};

		let res = unsafe { ioctl(self.sock, SIOCGIFFLAGS, &mut req) };
		if res < 0 {
			panic!("{}", IoError::last_error());
		}

		if req.ifr_flags & IFF_UP & IFF_RUNNING != 0 {
			// Already up
			return;
		}

		req.ifr_flags |= IFF_UP | IFF_RUNNING;

		let res = unsafe { ioctl(self.sock, SIOCSIFFLAGS, &mut req) };
		if res < 0 {
			panic!("{}", IoError::last_error());
		}
	}

	pub fn add_address(&self, ip: &[u8]) {
		self.up();

		if ip.len() == 4 {
			panic!("IPv4 not implemented");
		}
		else if ip.len() == 16 {
			let mut req = in6_ifreq {
				ifr6_addr: in6_addr {s6_addr: [
					(ip[ 1] as u16) << 8 | ip[ 0] as u16,
					(ip[ 3] as u16) << 8 | ip[ 2] as u16,
					(ip[ 5] as u16) << 8 | ip[ 4] as u16,
					(ip[ 7] as u16) << 8 | ip[ 6] as u16,
					(ip[ 9] as u16) << 8 | ip[ 8] as u16,
					(ip[11] as u16) << 8 | ip[10] as u16,
					(ip[13] as u16) << 8 | ip[12] as u16,
					(ip[15] as u16) << 8 | ip[14] as u16
				]},
				ifr6_prefixlen: 8,
				ifr6_ifindex: self.if_index
			};

			let res = unsafe { ioctl(self.sock, SIOCSIFADDR, &mut req) };
			if res < 0 {
				panic!("{}", IoError::last_error());
			}
		}
		else {
			panic!("IP length must be either 4 or 16 bytes, got {}", ip.len());
		}
	}

	pub fn read<'a>(&mut self, buffer: &'a mut [u8]) -> IoResult<&'a [u8]> {
		assert!(buffer.len() >= MTU_SIZE);

		let len = try!(self.file.read(buffer));
		Ok(buffer.slice_to(len))
	}

	pub fn write(&mut self, data: &[u8]) -> IoResult<()> {
		self.file.write(data)
	}
}
<commit_msg>Show -> Debug fallout<commit_after>use std::ffi::CString;
use std::fmt;
use std::io::{IoResult, IoError, File, Open, ReadWrite};
use std::os::unix::prelude::AsRawFd;
use libc::c_int;
use libc::consts::os::bsd44::{AF_INET6, SOCK_DGRAM};
use libc::funcs::bsd43::socket;
use libc::funcs::bsd44::ioctl;
use libc::funcs::posix88::unistd::close;
use libc::types::os::common::bsd44::in6_addr;
use c_interop::*;

const DEVICE_PATH: &'static str = "/dev/net/tun";

// TODO Make not a constant
const MTU_SIZE: usize = 1500;

#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum TunTapType {
	Tun,
	Tap
}

pub struct TunTap {
	pub file: File,
	sock: c_int,
	if_name: [u8; IFNAMSIZ],
	if_index: c_int
}

impl Drop for TunTap {
	fn drop(&mut self) {
		unsafe { close(self.sock) };
	}
}

impl fmt::Debug for TunTap {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		write!(f, "Tun({:?})", self.get_name())
	}
}

impl TunTap {
	pub fn create(typ: TunTapType) -> TunTap {
		TunTap::create_named(typ, &CString::from_slice(&[]))
	}

	pub fn create_named(typ: TunTapType, name: &CString) -> TunTap {
		let (file, if_name) = TunTap::create_if(typ, name);
		let (sock, if_index) = TunTap::create_socket(if_name);

		TunTap {
			file: file,
			sock: sock,
			if_name: if_name,
			if_index: if_index
		}
	}

	fn create_if(typ: TunTapType, name: &CString) -> (File, [u8; IFNAMSIZ]) {
		let name_slice = name.as_bytes_with_nul();
		if name_slice.len() > IFNAMSIZ {
			panic!("Interface name too long, max length is {}", IFNAMSIZ - 1);
		}

		let path = Path::new(DEVICE_PATH);
		let file = match File::open_mode(&path, Open, ReadWrite) {
			Err(why) => panic!("Couldn't open tun device '{}': {}", path.display(), why.desc),
			Ok(file) => file,
		};

		let mut req = ioctl_flags_data {
			ifr_name: {
				let mut buffer = [0u8; IFNAMSIZ];
				buffer.clone_from_slice(name_slice);
				buffer
			},
			ifr_flags: match typ {
				TunTapType::Tun => IFF_TUN,
				TunTapType::Tap => IFF_TAP
			}
		};

		let res = unsafe { ioctl(file.as_raw_fd(), TUNSETIFF, &mut req) };
		if res < 0 {
			panic!("{}", IoError::last_error());
		}

		(file, req.ifr_name)
	}

	fn create_socket(if_name: [u8; IFNAMSIZ]) -> (c_int, c_int) {
		let sock = unsafe { socket(AF_INET6, SOCK_DGRAM, 0) };
		if sock < 0 {
			panic!("{}", IoError::last_error());
		}

		let mut req = ioctl_ifindex_data {
			ifr_name: if_name,
			ifr_ifindex: -1
		};

		let res = unsafe { ioctl(sock, SIOCGIFINDEX, &mut req) };
		if res < 0 {
			let err = IoError::last_error();
			unsafe { close(sock) };
			panic!("{}", err);
		}

		(sock, req.ifr_ifindex)
	}

	pub fn get_name(&self) -> CString {
		let nul_pos = match self.if_name.as_slice().position_elem(&0) {
			Some(p) => p,
			None => panic!("Device name should be null-terminated")
		};

		CString::from_slice(self.if_name.slice_to(nul_pos))
	}

	pub fn up(&self) {
		let mut req = ioctl_flags_data {
			ifr_name: self.if_name,
			ifr_flags: 0
		};

		let res = unsafe { ioctl(self.sock, SIOCGIFFLAGS, &mut req) };
		if res < 0 {
			panic!("{}", IoError::last_error());
		}

		if req.ifr_flags & IFF_UP & IFF_RUNNING != 0 {
			// Already up
			return;
		}

		req.ifr_flags |= IFF_UP | IFF_RUNNING;

		let res = unsafe { ioctl(self.sock, SIOCSIFFLAGS, &mut req) };
		if res < 0 {
			panic!("{}", IoError::last_error());
		}
	}

	pub fn add_address(&self, ip: &[u8]) {
		self.up();

		if ip.len() == 4 {
			panic!("IPv4 not implemented");
		}
		else if ip.len() == 16 {
			let mut req = in6_ifreq {
				ifr6_addr: in6_addr {s6_addr: [
					(ip[ 1] as u16) << 8 | ip[ 0] as u16,
					(ip[ 3] as u16) << 8 | ip[ 2] as u16,
					(ip[ 5] as u16) << 8 | ip[ 4] as u16,
					(ip[ 7] as u16) << 8 | ip[ 6] as u16,
					(ip[ 9] as u16) << 8 | ip[ 8] as u16,
					(ip[11] as u16) << 8 | ip[10] as u16,
					(ip[13] as u16) << 8 | ip[12] as u16,
					(ip[15] as u16) << 8 | ip[14] as u16
				]},
				ifr6_prefixlen: 8,
				ifr6_ifindex: self.if_index
			};

			let res = unsafe { ioctl(self.sock, SIOCSIFADDR, &mut req) };
			if res < 0 {
				panic!("{}", IoError::last_error());
			}
		}
		else {
			panic!("IP length must be either 4 or 16 bytes, got {}", ip.len());
		}
	}

	pub fn read<'a>(&mut self, buffer: &'a mut [u8]) -> IoResult<&'a [u8]> {
		assert!(buffer.len() >= MTU_SIZE);

		let len = try!(self.file.read(buffer));
		Ok(buffer.slice_to(len))
	}

	pub fn write(&mut self, data: &[u8]) -> IoResult<()> {
		self.file.write(data)
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright 2020 Google LLC All Rights Reserved.
 *
 * 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::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use std::str::from_utf8;
use std::sync::Arc;

use slog::{debug, error, info, o, Logger};
use tokio::io::Result;
use tokio::net::udp::{RecvHalf, SendHalf};
use tokio::net::UdpSocket;
use tokio::sync::mpsc::{channel, Receiver, Sender};
use tokio::sync::{Mutex, RwLock};

use crate::config::{Config, ConnectionConfig};

type SessionMap = Arc<RwLock<HashMap<(SocketAddr, SocketAddr), Mutex<Session>>>>;

/// Server is the UDP server main implementation
pub struct Server {
    log: Logger,
}

impl Server {
    pub fn new(base: Logger) -> Self {
        let log = base.new(o!("source" => "server::Server"));
        return Server { log };
    }

    // TODO: write tests for this
    /// start the async processing of incoming UDP packets
    pub async fn run(self, config: Arc<Config>) -> Result<()> {
        self.log_config(&config);

        let (mut receive_socket, send_socket) = Server::bind(&config).await?.split();
        // HashMap key is from,destination addresses as a tuple.
        let sessions: SessionMap = Arc::new(RwLock::new(HashMap::new()));
        let (send_packets, receive_packets) = channel::<Packet>(1024);

        let log = self.log.clone();
        tokio::spawn(async move {
            Server::process_receive_packet_channel(&log, send_socket, receive_packets).await;
            debug!(log, "Receiver closed");
        });

        loop {
            if let Err(err) = Server::process_receive_socket(
                self.log.clone(),
                config.clone(),
                &mut receive_socket,
                sessions.clone(),
                send_packets.clone(),
            )
            .await
            {
                error!(self.log, "Error processing reeive socket: {}", err);
            }
        }
    }

    // TODO: write tests
    /// process_receive_socket takes packets from the local socket and asynchronously
    /// processes them to send them out to endpoints.
    async fn process_receive_socket(
        log: Logger,
        config: Arc<Config>,
        receive_socket: &mut RecvHalf,
        sessions: SessionMap,
        send_packets: Sender<Packet>,
    ) -> Result<()> {
        let mut buf: Vec<u8> = vec![0; 65535];
        let (size, recv_addr) = receive_socket.recv_from(&mut buf).await?;
        tokio::spawn(async move {
            let endpoints = config.get_endpoints();
            let packet = &buf[..size];

            debug!(
                log,
                "Packet Received from: {}, {}",
                recv_addr,
                from_utf8(packet).unwrap()
            );

            for (_, dest) in endpoints.iter() {
                if let Err(err) = Server::ensure_session(
                    &log,
                    sessions.clone(),
                    recv_addr,
                    *dest,
                    send_packets.clone(),
                )
                .await
                {
                    error!(log, "Error ensuring session exists: {}", err);
                }

                let map = sessions.read().await;
                let mut session = map.get(&(recv_addr, *dest)).unwrap().lock().await;
                if let Err(err) = session.send_to(packet).await {
                    error!(log, "Error ensuring sending packet: {}", err)
                }
            }
        });
        return Ok(());
    }

    // TODO: write tests for this
    /// process_receive_packet_channel blocks on receive_packets.recv() channel
    /// and sends each packet on to the Packet.dest
    async fn process_receive_packet_channel(
        log: &Logger,
        mut send_socket: SendHalf,
        mut receive_packets: Receiver<Packet>,
    ) {
        while let Some(packet) = receive_packets.recv().await {
            // TODO: wrap this in tokio::spawn?
            debug!(
                log,
                "Sending packet back to origin {}, {}",
                packet.dest,
                String::from_utf8(packet.contents.clone()).unwrap()
            );

            if let Err(err) = send_socket
                .send_to(packet.contents.as_slice(), &packet.dest)
                .await
            {
                error!(log, "Error sending packet to {}, {}", packet.dest, err);
            }
        }
    }

    /// log_config outputs a log of what is configured
    fn log_config(&self, config: &Arc<Config>) {
        info!(self.log, "Starting on port {}", config.local.port);
        match &config.connections {
            ConnectionConfig::Sender { address, .. } => {
                info!(self.log, "Sender configuration"; "address" => address)
            }
            ConnectionConfig::Receiver { endpoints } => {
                info!(self.log, "Receiver configuration"; "endpoints" => endpoints.len())
            }
        };
    }

    /// bind binds the local configured port
    async fn bind(config: &Config) -> Result<UdpSocket> {
        let addr = SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), config.local.port);
        return UdpSocket::bind(addr).await;
    }

    // TODO: write tests for this
    /// ensure_session makes sure there is a value session for the name in the sessions map
    async fn ensure_session(
        log: &Logger,
        sessions: SessionMap,
        from: SocketAddr,
        dest: SocketAddr,
        sender: Sender<Packet>,
    ) -> Result<()> {
        {
            let map = sessions.read().await;
            if map.contains_key(&(from, dest)) {
                return Ok(());
            }
        }
        let s = Session::new(log, from, dest, sender).await?;
        {
            let mut map = sessions.write().await;
            map.insert((from, dest), Mutex::new(s));
        }
        return Ok(());
    }
}

/// Packet represents a packet that needs to go somewhere
struct Packet {
    dest: SocketAddr,
    contents: Vec<u8>,
}

/// Session encapsulates a UDP stream session
struct Session {
    log: Logger,
    send: SendHalf,
    /// dest is where to send data to
    dest: SocketAddr,
    /// from is the original sender
    from: SocketAddr,
    // TODO: store a session expiry, and update when you send data
}

impl Session {
    // TODO: write some tests
    async fn new(
        base: &Logger,
        from: SocketAddr,
        dest: SocketAddr,
        sender: Sender<Packet>,
    ) -> Result<Self> {
        let addr = SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 0);
        let (recv, send) = UdpSocket::bind(addr).await?.split();
        let mut s = Session {
            log: base.new(o!("source" => "server::Session", "from" => from, "dest" => dest)),
            send,
            from,
            dest,
        };
        debug!(s.log, "Session created");

        s.run(recv, sender);

        return Ok(s);
    }

    // TODO: write some tests
    /// run starts processing receiving udp packets on its UdpSocket
    fn run(&mut self, mut recv: RecvHalf, mut sender: Sender<Packet>) {
        let log = self.log.clone();
        let from = self.from;
        tokio::spawn(async move {
            let mut buf: Vec<u8> = vec![0; 65535];
            // TODO: work out how to shut this down once this session expires
            loop {
                debug!(log, "awaiting incoming packet");
                match recv.recv_from(&mut buf).await {
                    Err(err) => error!(log, "Error receiving packet: {}", err),
                    Ok((size, recv_addr)) => {
                        let packet = &buf[..size];
                        debug!(
                            log,
                            "Received packet from {}, {}",
                            recv_addr,
                            from_utf8(packet).unwrap()
                        );
                        if let Err(err) = sender
                            .send(Packet {
                                contents: packet.to_vec(),
                                dest: from,
                            })
                            .await
                        {
                            println!("Error sending packet to channel: {}", err);
                        }
                    }
                }
            }
        });
    }

    // TODO: write some tests
    /// Both sends a packet to the Session's dest.
    async fn send_to(&mut self, buf: &[u8]) -> Result<usize> {
        debug!(
            self.log,
            "Sending packet to: {}, {}",
            self.dest,
            from_utf8(buf).unwrap()
        );
        return self.send.send_to(buf, &self.dest).await;
    }
}

#[cfg(test)]
mod tests {
    use std::net::{IpAddr, Ipv4Addr, SocketAddr};

    use crate::config::{Config, ConnectionConfig, Local};
    use crate::server::Server;

    #[tokio::test]
    async fn bind() {
        let config = Config {
            local: Local { port: 12345 },
            connections: ConnectionConfig::Receiver {
                endpoints: Vec::new(),
            },
        };
        let socket = Server::bind(&config).await.unwrap();
        let addr = socket.local_addr().unwrap();

        let expected = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 12345);
        assert_eq!(expected, addr)
    }
}
<commit_msg>Test for Session::send_to (#16)<commit_after>/*
 * Copyright 2020 Google LLC All Rights Reserved.
 *
 * 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::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use std::str::from_utf8;
use std::sync::Arc;

use slog::{debug, error, info, o, Logger};
use tokio::io::Result;
use tokio::net::udp::{RecvHalf, SendHalf};
use tokio::net::UdpSocket;
use tokio::sync::mpsc::{channel, Receiver, Sender};
use tokio::sync::{Mutex, RwLock};

use crate::config::{Config, ConnectionConfig};

type SessionMap = Arc<RwLock<HashMap<(SocketAddr, SocketAddr), Mutex<Session>>>>;

/// Server is the UDP server main implementation
pub struct Server {
    log: Logger,
}

impl Server {
    pub fn new(base: Logger) -> Self {
        let log = base.new(o!("source" => "server::Server"));
        return Server { log };
    }

    // TODO: write tests for this
    /// start the async processing of incoming UDP packets
    pub async fn run(self, config: Arc<Config>) -> Result<()> {
        self.log_config(&config);

        let (mut receive_socket, send_socket) = Server::bind(&config).await?.split();
        // HashMap key is from,destination addresses as a tuple.
        let sessions: SessionMap = Arc::new(RwLock::new(HashMap::new()));
        let (send_packets, receive_packets) = channel::<Packet>(1024);

        let log = self.log.clone();
        tokio::spawn(async move {
            Server::process_receive_packet_channel(&log, send_socket, receive_packets).await;
            debug!(log, "Receiver closed");
        });

        loop {
            if let Err(err) = Server::process_receive_socket(
                self.log.clone(),
                config.clone(),
                &mut receive_socket,
                sessions.clone(),
                send_packets.clone(),
            )
            .await
            {
                error!(self.log, "Error processing reeive socket: {}", err);
            }
        }
    }

    // TODO: write tests
    /// process_receive_socket takes packets from the local socket and asynchronously
    /// processes them to send them out to endpoints.
    async fn process_receive_socket(
        log: Logger,
        config: Arc<Config>,
        receive_socket: &mut RecvHalf,
        sessions: SessionMap,
        send_packets: Sender<Packet>,
    ) -> Result<()> {
        let mut buf: Vec<u8> = vec![0; 65535];
        let (size, recv_addr) = receive_socket.recv_from(&mut buf).await?;
        tokio::spawn(async move {
            let endpoints = config.get_endpoints();
            let packet = &buf[..size];

            debug!(
                log,
                "Packet Received from: {}, {}",
                recv_addr,
                from_utf8(packet).unwrap()
            );

            for (_, dest) in endpoints.iter() {
                if let Err(err) = Server::ensure_session(
                    &log,
                    sessions.clone(),
                    recv_addr,
                    *dest,
                    send_packets.clone(),
                )
                .await
                {
                    error!(log, "Error ensuring session exists: {}", err);
                }

                let map = sessions.read().await;
                let mut session = map.get(&(recv_addr, *dest)).unwrap().lock().await;
                if let Err(err) = session.send_to(packet).await {
                    error!(log, "Error ensuring sending packet: {}", err)
                }
            }
        });
        return Ok(());
    }

    // TODO: write tests for this
    /// process_receive_packet_channel blocks on receive_packets.recv() channel
    /// and sends each packet on to the Packet.dest
    async fn process_receive_packet_channel(
        log: &Logger,
        mut send_socket: SendHalf,
        mut receive_packets: Receiver<Packet>,
    ) {
        while let Some(packet) = receive_packets.recv().await {
            // TODO: wrap this in tokio::spawn?
            debug!(
                log,
                "Sending packet back to origin {}, {}",
                packet.dest,
                String::from_utf8(packet.contents.clone()).unwrap()
            );

            if let Err(err) = send_socket
                .send_to(packet.contents.as_slice(), &packet.dest)
                .await
            {
                error!(log, "Error sending packet to {}, {}", packet.dest, err);
            }
        }
    }

    /// log_config outputs a log of what is configured
    fn log_config(&self, config: &Arc<Config>) {
        info!(self.log, "Starting on port {}", config.local.port);
        match &config.connections {
            ConnectionConfig::Sender { address, .. } => {
                info!(self.log, "Sender configuration"; "address" => address)
            }
            ConnectionConfig::Receiver { endpoints } => {
                info!(self.log, "Receiver configuration"; "endpoints" => endpoints.len())
            }
        };
    }

    /// bind binds the local configured port
    async fn bind(config: &Config) -> Result<UdpSocket> {
        let addr = SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), config.local.port);
        return UdpSocket::bind(addr).await;
    }

    // TODO: write tests for this
    /// ensure_session makes sure there is a value session for the name in the sessions map
    async fn ensure_session(
        log: &Logger,
        sessions: SessionMap,
        from: SocketAddr,
        dest: SocketAddr,
        sender: Sender<Packet>,
    ) -> Result<()> {
        {
            let map = sessions.read().await;
            if map.contains_key(&(from, dest)) {
                return Ok(());
            }
        }
        let s = Session::new(log, from, dest, sender).await?;
        {
            let mut map = sessions.write().await;
            map.insert((from, dest), Mutex::new(s));
        }
        return Ok(());
    }
}

/// Packet represents a packet that needs to go somewhere
struct Packet {
    dest: SocketAddr,
    contents: Vec<u8>,
}

/// Session encapsulates a UDP stream session
struct Session {
    log: Logger,
    send: SendHalf,
    /// dest is where to send data to
    dest: SocketAddr,
    /// from is the original sender
    from: SocketAddr,
    // TODO: store a session expiry, and update when you send data
}

impl Session {
    // TODO: write some tests
    async fn new(
        base: &Logger,
        from: SocketAddr,
        dest: SocketAddr,
        sender: Sender<Packet>,
    ) -> Result<Self> {
        let addr = SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 0);
        let (recv, send) = UdpSocket::bind(addr).await?.split();
        let mut s = Session {
            log: base.new(o!("source" => "server::Session", "from" => from, "dest" => dest)),
            send,
            from,
            dest,
        };
        debug!(s.log, "Session created");

        s.run(recv, sender);

        return Ok(s);
    }

    // TODO: write some tests
    /// run starts processing receiving udp packets on its UdpSocket
    fn run(&mut self, mut recv: RecvHalf, mut sender: Sender<Packet>) {
        let log = self.log.clone();
        let from = self.from;
        tokio::spawn(async move {
            let mut buf: Vec<u8> = vec![0; 65535];
            // TODO: work out how to shut this down once this session expires
            loop {
                debug!(log, "awaiting incoming packet");
                match recv.recv_from(&mut buf).await {
                    Err(err) => error!(log, "Error receiving packet: {}", err),
                    Ok((size, recv_addr)) => {
                        let packet = &buf[..size];
                        debug!(
                            log,
                            "Received packet from {}, {}",
                            recv_addr,
                            from_utf8(packet).unwrap()
                        );
                        if let Err(err) = sender
                            .send(Packet {
                                contents: packet.to_vec(),
                                dest: from,
                            })
                            .await
                        {
                            println!("Error sending packet to channel: {}", err);
                        }
                    }
                }
            }
        });
    }

    /// Both sends a packet to the Session's dest.
    async fn send_to(&mut self, buf: &[u8]) -> Result<usize> {
        debug!(
            self.log,
            "Sending packet to: {}, {}",
            self.dest,
            from_utf8(buf).unwrap()
        );
        return self.send.send_to(buf, &self.dest).await;
    }
}

#[cfg(test)]
mod tests {
    use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4};

    use crate::config::{Config, ConnectionConfig, Local};
    use crate::logger;
    use crate::server::{Packet, Server, Session};
    use std::str::from_utf8;
    use tokio::net::UdpSocket;
    use tokio::sync::mpsc::channel;
    use tokio::sync::oneshot;

    #[tokio::test]
    async fn server_bind() {
        let config = Config {
            local: Local { port: 12345 },
            connections: ConnectionConfig::Receiver {
                endpoints: Vec::new(),
            },
        };
        let socket = Server::bind(&config).await.unwrap();
        let addr = socket.local_addr().unwrap();

        let expected = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 12345);
        assert_eq!(expected, addr)
    }

    #[tokio::test]
    async fn session_send_to() {
        let log = logger();

        let addr = SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 0);
        let socket = UdpSocket::bind(addr).await.unwrap();
        let local_addr = socket.local_addr().unwrap();
        let (mut recv, _) = socket.split();
        let (sender, _) = channel::<Packet>(1);

        let (done, wait) = oneshot::channel::<()>();

        tokio::spawn(async move {
            let mut buf = vec![0; 1024];
            let size = recv.recv(&mut buf).await.unwrap();
            assert_eq!("hello", from_utf8(&buf[..size]).unwrap());
            done.send(()).unwrap();
        });

        let mut session = Session::new(&log, local_addr, local_addr, sender)
            .await
            .unwrap();
        session.send_to("hello".as_bytes()).await.unwrap();
        wait.await.unwrap();
    }
}
<|endoftext|>


<commit_before>// Fixed XOR
// Write a function that takes two equal-length buffers and produces their XOR combination.
//
// If your function works properly, then when you feed it the string:
//
// 1c0111001f010100061a024b53535009181c
// ... after hex decoding, and when XOR'd against:
//
// 686974207468652062756c6c277320657965
// ... should produce:
//
// 746865206b696420646f6e277420706c6179

#![feature(core)]

#[macro_use]
extern crate log;

use std::ascii::AsciiExt;
use std::str;

#[cfg(not(test))]
fn main() {
	println!("Set 1, Challenge 2");
	let string1 = "1c0111001f010100061a024b53535009181c";
	let string2 = "686974207468652062756c6c277320657965";
	println!("string 1: {:?}, string 2: {:?}", string1, string2);
	let output = fixed_hex_xor(string1, string2);
	println!("output string: {:?}", output);
}

fn fixed_hex_xor(first_string: &str, second_string: &str) -> String {
	if first_string.len() != second_string.len() {
		panic!("String lengths should be even.");
	}

	let mut output_vec = vec!();

	let first_lower = first_string.to_ascii_lowercase();
	let second_lower = second_string.to_ascii_lowercase();

	let first_iter = first_lower.as_bytes().iter();
	let second_iter = second_lower.as_bytes().iter();

	for (&x, &y) in first_iter.zip(second_iter) {
		let first_num = convert_hex_char_to_decimal(x);
		let second_num = convert_hex_char_to_decimal(y);
        output_vec.push(convert_decimal_to_hex_char(first_num ^ second_num));
	}

	// turn the byte vector into a string
	match str::from_utf8(output_vec.as_slice()) {
	    Ok(v) => {
	        debug!("output string: {:?}", v);
	        return v.to_string();
	    }
	    Err(e) => {
	        panic!("error parsing string: {:?}", e);
    	}
	}
}

fn convert_hex_char_to_decimal(character: u8) -> u8 {
	// if x is in the ascii range for numbers, subtract 48,
	// which is '0' in ascii. Otherwise, it should be a lowercase letter.
	// 'a' is 97, so subtract 87.
	match character < 58 {
		true => character - 48,
		false => character - 87
	}
}

fn convert_decimal_to_hex_char(decimal: u8) -> u8 {
	match decimal {
		0...9 => decimal + 48,
		10...15 => decimal + 87,
		_ => panic!("Not a valid hex char")
	}
}

#[cfg(test)]
mod set1challenge2 {

	#[test]
	fn challenge() {
		let output = super::fixed_hex_xor("1c0111001f010100061a024b53535009181c", "686974207468652062756c6c277320657965");
		assert_eq!(output, "746865206b696420646f6e277420706c6179");
	}

}
<commit_msg>Update challenge 2 to latest Rust<commit_after>// Fixed XOR
// Write a function that takes two equal-length buffers and produces their XOR combination.
//
// If your function works properly, then when you feed it the string:
//
// 1c0111001f010100061a024b53535009181c
// ... after hex decoding, and when XOR'd against:
//
// 686974207468652062756c6c277320657965
// ... should produce:
//
// 746865206b696420646f6e277420706c6179

#[macro_use]
extern crate log;

use std::ascii::AsciiExt;
use std::str;

#[cfg(not(test))]
fn main() {
	println!("Set 1, Challenge 2");
	let string1 = "1c0111001f010100061a024b53535009181c";
	let string2 = "686974207468652062756c6c277320657965";
	println!("string 1: {:?}, string 2: {:?}", string1, string2);
	let output = fixed_hex_xor(string1, string2);
	println!("output string: {:?}", output);
}

fn fixed_hex_xor(first_string: &str, second_string: &str) -> String {
	if first_string.len() != second_string.len() {
		panic!("String lengths should be even.");
	}

	let mut output_vec = vec!();

	let first_lower = first_string.to_ascii_lowercase();
	let second_lower = second_string.to_ascii_lowercase();

	let first_iter = first_lower.as_bytes().iter();
	let second_iter = second_lower.as_bytes().iter();

	for (&x, &y) in first_iter.zip(second_iter) {
		let first_num = convert_hex_char_to_decimal(x);
		let second_num = convert_hex_char_to_decimal(y);
        output_vec.push(convert_decimal_to_hex_char(first_num ^ second_num));
	}

	// turn the byte vector into a string
	match str::from_utf8(&output_vec) {
	    Ok(v) => {
	        debug!("output string: {:?}", v);
	        return v.to_string();
	    }
	    Err(e) => {
	        panic!("error parsing string: {:?}", e);
    	}
	}
}

fn convert_hex_char_to_decimal(character: u8) -> u8 {
	// if x is in the ascii range for numbers, subtract 48,
	// which is '0' in ascii. Otherwise, it should be a lowercase letter.
	// 'a' is 97, so subtract 87.
	match character < 58 {
		true => character - 48,
		false => character - 87
	}
}

fn convert_decimal_to_hex_char(decimal: u8) -> u8 {
	match decimal {
		0...9 => decimal + 48,
		10...15 => decimal + 87,
		_ => panic!("Not a valid hex char")
	}
}

#[cfg(test)]
mod set1challenge2 {

	#[test]
	fn challenge() {
		let output = super::fixed_hex_xor("1c0111001f010100061a024b53535009181c", "686974207468652062756c6c277320657965");
		assert_eq!(output, "746865206b696420646f6e277420706c6179");
	}

}
<|endoftext|>