<commit_before>
use syntax::codemap::{Span, respan, dummy_spanned, DUMMY_SP};

use syntax::ptr::P;

use syntax::ast::{TokenTree, Name, Delimited, SyntaxContext, Ident,
                  StrStyle, LitKind, Item, Visibility, Mod,
                  ItemKind, Generics, EnumDef, VariantData, Variant_,
                  Path, PathSegment, PathParameters, DUMMY_NODE_ID};

use syntax::ext::base::{ExtCtxt, MacResult, MacEager};
use syntax::ext::build::AstBuilder;

use syntax::parse::parser::{Parser, PathParsingMode};
use syntax::parse::token::{Token, DelimToken, Lit,
                           intern, intern_and_get_ident};
use syntax::parse::token::keywords::Keyword;

use syntax::util::small_vector::SmallVector;

use nametable::name_hash;

struct MyLiteralArray<T>(Vec<T>);
struct MyLiteralString(String);
struct MyLiteralTuple2<T1,T2>(T1,T2);

use quasi::ToTokens;
use std::rc::Rc;
impl<T: ToTokens> ToTokens for MyLiteralArray<T> {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        let mut r = vec![];
        let mut r_inner = vec![];
        for item in self.0.iter() {
            r_inner.append(&mut item.to_tokens(_cx));
            r_inner.push(TokenTree::Token(DUMMY_SP, Token::Comma));
        }
        r.push(TokenTree::Delimited(DUMMY_SP, Rc::new(Delimited {
            delim: DelimToken::Bracket,
            open_span: DUMMY_SP,
            tts: r_inner,
            close_span: DUMMY_SP,
        })));
        r
    }
}

impl ToTokens for MyLiteralString {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
		let lit = LitKind::Str(
			intern_and_get_ident(&self.0), StrStyle::Cooked);
        dummy_spanned(lit).to_tokens(_cx)
	}
}

impl<T1: ToTokens, T2: ToTokens> ToTokens for MyLiteralTuple2<T1, T2> {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        let mut r = vec![];
        let mut r_inner = vec![];
        r_inner.append(&mut self.0.to_tokens(_cx));
        r_inner.push(TokenTree::Token(DUMMY_SP, Token::Comma));
        r_inner.append(&mut self.1.to_tokens(_cx));
        r.push(TokenTree::Delimited(DUMMY_SP, Rc::new(Delimited {
            delim: DelimToken::Paren,
            open_span: DUMMY_SP,
            tts: r_inner,
            close_span: DUMMY_SP,
        })));
        r
    }
}

fn to_pub_item(mut item: Item) -> Item {
    item.vis = Visibility::Public;
    item
}

fn to_pub_item_ptr(item: P<Item>) -> P<Item> {
    item.map(to_pub_item)
}

fn add_suffix_to_path(prefix: &Path, suffix: Ident) -> Path {
    let mut result = prefix.clone();
    result.segments.push(
        PathSegment {
            identifier: suffix,
            parameters: PathParameters::none(),
        }
    );
    result
}

fn add_prefix_to_path(prefix: Ident, suffix: &Path) -> Path {
    let mut result = suffix.clone();
    result.segments.insert(0,
        PathSegment {
            identifier: prefix,
            parameters: PathParameters::none(),
        }
    );
    result
}

fn generate_nametable_item<'cx>(
    cx: &'cx mut ExtCtxt,
    sp: Span,
    sc: SyntaxContext,
    artifact_name: Name,
    base_artifact_path: Option<Path>,
    artifact_items: Vec<(Name, Name)>) -> P<Item> {

    let mut mod_attributes = Vec::new();
    let mut mod_items = Vec::new();

    let base_artifact_path = base_artifact_path.map(
        |path: Path|
        if path.segments.len() > 0 {
            let initial_segment_name =
                path.segments[0].identifier.name.as_str();
            if initial_segment_name == "self" ||
                initial_segment_name == "super" {
                    add_prefix_to_path(Ident::new(intern("super"), sc), &path)
                } else {
                    path
                }
        } else {
            path
        }
    );

    {
        // use
        mod_items.push(quote_item!(
            cx,
            use ::nametable::{
                NameTable, StaticNameTable, DynamicNameTable,
                StaticHashedNameTable, NameTableIdx};).unwrap());
    }

    {
        // const INITIAL
        if let Some(ref path) = base_artifact_path {
            mod_items.push(cx.item_use_simple(DUMMY_SP, Visibility::Inherited, path.clone()));
            let base_artifact_initial = add_suffix_to_path(path, Ident::new(intern("INITIAL"), sc));
            let base_artifact_count = add_suffix_to_path(path, Ident::new(intern("COUNT"), sc));
            mod_items.push(quote_item!(cx, pub const INITIAL : usize = $base_artifact_initial + $base_artifact_count; ).unwrap());
        } else {
            mod_items.push(quote_item!(cx, pub const INITIAL : usize = 0usize; ).unwrap());
    }

        let count = artifact_items.len();
        mod_items.push(quote_item!(cx, pub const COUNT : usize = $count; ).unwrap());
    }

    {
        // enum Names
        let ident_names = Ident::new(intern("Names"), sc);

        let mut enumdef = EnumDef { variants: Vec::new() };
        for (idx,&(key, _)) in artifact_items.iter().enumerate() {
            let ident_key = Ident::new(key, sc);
            enumdef.variants.push(respan(sp, Variant_ {
                name: ident_key,
                attrs: Vec::new(),
                data: VariantData::Unit(DUMMY_NODE_ID),
                disr_expr: Some(quote_expr!(cx, INITIAL + $idx)),
            }));
        }

        let repr_attribute = cx.attribute(
            sp, cx.meta_list(sp, intern_and_get_ident("repr"),
                             vec!(cx.meta_word(sp, intern_and_get_ident("usize")))));

        let derive_attribute = cx.attribute(
            sp, cx.meta_list(sp, intern_and_get_ident("derive"),
                             vec!(cx.meta_word(sp, intern_and_get_ident("Copy")),
                                  cx.meta_word(sp, intern_and_get_ident("Clone")))));
        mod_items.push(to_pub_item_ptr(
            cx.item(sp, ident_names,
                    vec!(repr_attribute, derive_attribute),
                    ItemKind::Enum(enumdef, Generics::default()))));
    }

    {
        // impl
        mod_items.push(quote_item!(
            cx,
            impl NameTableIdx for Names {
                fn to_index(&self) -> usize() { *self as usize }
            }
        ).unwrap());
    }

    {
        //data
        let mut name_data : MyLiteralString = MyLiteralString(String::new());
        let mut index_data : MyLiteralArray<usize> = MyLiteralArray(vec!(0));
        let mut hash_data : MyLiteralArray<MyLiteralTuple2<u64,usize>> = MyLiteralArray(vec!());
        for (idx,&(_, value)) in artifact_items.iter().enumerate() {
            name_data.0.push_str(&*value.as_str());
            index_data.0.push(name_data.0.len());
            hash_data.0.push(MyLiteralTuple2(name_hash(&*value.as_str()),idx));
        }
        hash_data.0.sort_by(|&MyLiteralTuple2(a, _), &MyLiteralTuple2(b, _)| a.cmp(&b));

        fn detect_collision<T: PartialOrd + Copy>(arr: &[MyLiteralTuple2<T, usize>]) -> Option<(usize, usize)> {
            if arr.len() == 0 {
                return None;
            }
            let mut old_val = arr.get(0).unwrap().0;
            for i in 1..arr.len() {
                if arr.get(i).unwrap().0 == old_val {
                    return Some((arr.get(i-1).unwrap().1, arr.get(i).unwrap().1));
                }
                old_val = arr.get(i).unwrap().0;
            }
            return None
        }

        match detect_collision(&hash_data.0) {
            Some((a, b)) => {
                println!(
                    "nametable_macros: Hash collision happened between item index {:} and {:} for table `{:}'",
                    a, b, &*artifact_name.as_str());
                hash_data.0 = vec!();
            },
            None => ()
        }

        mod_items.push(quote_item!(
            cx,
            const NAME_DATA : &'static str = $name_data;
        ).unwrap());
        mod_items.push(quote_item!(
            cx,
            const INDEX_DATA : &'static [usize] = &$index_data;
        ).unwrap());

        mod_items.push(quote_item!(
            cx,
            const HASH_DATA : &'static [(u64,usize)] = &$hash_data;
        ).unwrap());

    }

    {
        //functions
        match base_artifact_path {
            Some(ref path) => {
                let base_artifact_new = add_suffix_to_path(&path, Ident::new(intern("new"), sc));
                mod_items.push(
                    quote_item!(
                        cx,
                        pub fn new<'x>() -> StaticHashedNameTable<'x> {
                            StaticHashedNameTable::new_upon(NAME_DATA, INDEX_DATA, HASH_DATA, $base_artifact_new())
                        }).unwrap());

                mod_items.push(quote_item!(
                    cx,
                    pub fn new_dynamic<'x>() -> DynamicNameTable<'x> {
                        DynamicNameTable::new_upon(
                            StaticHashedNameTable::new_upon(NAME_DATA, INDEX_DATA, HASH_DATA, $base_artifact_new()))
                    }
                ).unwrap());

                mod_items.push(quote_item!(
                    cx,
                    pub fn new_plain_<'x>() -> StaticNameTable<'x> {
                        StaticNameTable::new_upon(NAME_DATA, INDEX_DATA, $base_artifact_new())
                    }
                ).unwrap());

                mod_items.push(quote_item!(
                    cx,
                    pub fn new_dynamic_plain<'x>() -> DynamicNameTable<'x> {
                        DynamicNameTable::new_upon(
                            StaticNameTable::new_upon(NAME_DATA, INDEX_DATA, $base_artifact_new()))
                    }
                ).unwrap());

            }
            None => {
                mod_items.push(quote_item!(
                        cx,
                        pub fn new<'x>() -> StaticHashedNameTable<'x> {
                            StaticHashedNameTable::new(NAME_DATA, INDEX_DATA, HASH_DATA)
                        }
                ).unwrap());

                mod_items.push(quote_item!(
                    cx,
                    pub fn new_dynamic<'x>() -> DynamicNameTable<'x> {
                        DynamicNameTable::new_upon(
                            StaticHashedNameTable::new(NAME_DATA, INDEX_DATA, HASH_DATA))
                    }
                ).unwrap());

                mod_items.push(quote_item!(
                    cx,
                    pub fn new_plain_<'x>() -> StaticNameTable<'x> {
                        StaticNameTable::new(NAME_DATA, INDEX_DATA)
                    }
                ).unwrap());

                mod_items.push(quote_item!(
                    cx,
                    pub fn new_dynamic_plain<'x>() -> DynamicNameTable<'x> {
                        DynamicNameTable::new_upon(
                            StaticNameTable::new(NAME_DATA, INDEX_DATA))
                    }
                ).unwrap());
            }
        }
    }

      {
          let allow_unused_attribute = cx.attribute(
            sp, cx.meta_list(sp, intern_and_get_ident("allow"),
                             vec!(cx.meta_word(sp, intern_and_get_ident("dead_code")))));

        let allow_unused_imports_attribute = cx.attribute(
            sp, cx.meta_list(sp, intern_and_get_ident("allow"),
                             vec!(cx.meta_word(sp, intern_and_get_ident("unused_imports")))));

            mod_attributes.push(allow_unused_attribute);
            mod_attributes.push(allow_unused_imports_attribute);
      }

    P(Item {
        ident: Ident::new(artifact_name, sc),
        attrs: mod_attributes,
        id: DUMMY_NODE_ID,
        node: ItemKind::Mod(Mod {
            inner: sp,
            items: mod_items,
        }),
        vis: Visibility::Public,
        span: sp,
    })
}

fn process_nametables<'cx>(cx: &'cx mut ExtCtxt, sp: Span, mut parser: Parser) -> SmallVector<P<Item>> {
    let mut result : Vec<P<Item>> = Vec::new();

    while &parser.token != &Token::Eof {
        let syntax_ctx = if let Token::Ident(nt_keyword, _) = parser.token {
            if nt_keyword.name.as_str() != "nametable" {
                cx.span_fatal(parser.span, "expected keyword `nametable' here.");
            }
            let _ = parser.bump();
            nt_keyword.ctxt
        }
        else {
            cx.span_fatal(parser.span, "expected keyword `nametable' here.");
        };

        let artifact_name = if let Token::Ident(artifact_name, _) = parser.token {
            let _ = parser.bump();
            artifact_name.name
        }
        else {
            cx.span_fatal(parser.span, "expected nametable name here.");
        };

        let base_artifact_path = if parser.eat(&Token::Colon) {
            let result = parser.parse_path(PathParsingMode::NoTypesAllowed);
            match result{
                Ok(path) => Some(path),
                _ => cx.span_fatal(parser.span, "expected base nametable name here."),
            }
            } else {
            None
        };

        if !parser.eat(&Token::OpenDelim(DelimToken::Brace)) {
            cx.span_fatal(parser.span, "expected open brace here.");
        }

        let mut artifact_items : Vec<(Name, Name)> = Vec::new();

        while let Token::Ident(item_name, _) = parser.token {
            let _ = parser.bump();
            let item_string = if parser.token.is_keyword(Keyword::For) {
                let _ = parser.bump();
                if let Token::Literal(Lit::Str_(item_string_name), _) = parser.token {
                    let _ = parser.bump();
                    item_string_name
                } else {
                    cx.span_fatal(parser.span, "expected string literal here.");
                }
            } else {
                item_name.name
            };

            artifact_items.push((item_name.name,item_string));

            if !parser.eat(&Token::Comma) {
                break;
            }
        }

        if !parser.eat(&Token::CloseDelim(DelimToken::Brace)) {
            cx.span_fatal(parser.span, "expected close brace here.");
        }

        result.push(generate_nametable_item(cx, sp, syntax_ctx, artifact_name, base_artifact_path, artifact_items));
    }

    if &parser.token != &Token::Eof {
        cx.span_fatal(parser.span, "expected end of `nametable!` macro invocation");
    }

    match result.len() {
        0 => SmallVector::zero(),
        1 => SmallVector::one(result.pop().unwrap()),
        _ => SmallVector::many(result),
    }
}

pub fn expand<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult + 'cx> {
    let parser = cx.new_parser_from_tts(tts);

    MacEager::items(process_nametables(cx, sp, parser))
}

<commit_msg>Change codegen code according to nametable change.<commit_after>
use syntax::codemap::{Span, respan, dummy_spanned, DUMMY_SP};

use syntax::ptr::P;

use syntax::ast::{TokenTree, Name, Delimited, SyntaxContext, Ident,
                  StrStyle, LitKind, Item, Visibility, Mod,
                  ItemKind, Generics, EnumDef, VariantData, Variant_,
                  Path, PathSegment, PathParameters, DUMMY_NODE_ID};

use syntax::ext::base::{ExtCtxt, MacResult, MacEager};
use syntax::ext::build::AstBuilder;

use syntax::parse::parser::{Parser, PathParsingMode};
use syntax::parse::token::{Token, DelimToken, Lit,
                           intern, intern_and_get_ident};
use syntax::parse::token::keywords::Keyword;

use syntax::util::small_vector::SmallVector;

use nametable::name_hash;

struct MyLiteralArray<T>(Vec<T>);
struct MyLiteralString(String);
struct MyLiteralTuple2<T1,T2>(T1,T2);

use quasi::ToTokens;
use std::rc::Rc;
impl<T: ToTokens> ToTokens for MyLiteralArray<T> {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        let mut r = vec![];
        let mut r_inner = vec![];
        for item in self.0.iter() {
            r_inner.append(&mut item.to_tokens(_cx));
            r_inner.push(TokenTree::Token(DUMMY_SP, Token::Comma));
        }
        r.push(TokenTree::Delimited(DUMMY_SP, Rc::new(Delimited {
            delim: DelimToken::Bracket,
            open_span: DUMMY_SP,
            tts: r_inner,
            close_span: DUMMY_SP,
        })));
        r
    }
}

impl ToTokens for MyLiteralString {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
		let lit = LitKind::Str(
			intern_and_get_ident(&self.0), StrStyle::Cooked);
        dummy_spanned(lit).to_tokens(_cx)
	}
}

impl<T1: ToTokens, T2: ToTokens> ToTokens for MyLiteralTuple2<T1, T2> {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        let mut r = vec![];
        let mut r_inner = vec![];
        r_inner.append(&mut self.0.to_tokens(_cx));
        r_inner.push(TokenTree::Token(DUMMY_SP, Token::Comma));
        r_inner.append(&mut self.1.to_tokens(_cx));
        r.push(TokenTree::Delimited(DUMMY_SP, Rc::new(Delimited {
            delim: DelimToken::Paren,
            open_span: DUMMY_SP,
            tts: r_inner,
            close_span: DUMMY_SP,
        })));
        r
    }
}

fn to_pub_item(mut item: Item) -> Item {
    item.vis = Visibility::Public;
    item
}

fn to_pub_item_ptr(item: P<Item>) -> P<Item> {
    item.map(to_pub_item)
}

fn add_suffix_to_path(prefix: &Path, suffix: Ident) -> Path {
    let mut result = prefix.clone();
    result.segments.push(
        PathSegment {
            identifier: suffix,
            parameters: PathParameters::none(),
        }
    );
    result
}

fn add_prefix_to_path(prefix: Ident, suffix: &Path) -> Path {
    let mut result = suffix.clone();
    result.segments.insert(0,
        PathSegment {
            identifier: prefix,
            parameters: PathParameters::none(),
        }
    );
    result
}

fn generate_nametable_item<'cx>(
    cx: &'cx mut ExtCtxt,
    sp: Span,
    sc: SyntaxContext,
    artifact_name: Name,
    base_artifact_path: Option<Path>,
    artifact_items: Vec<(Name, Name)>) -> P<Item> {

    let mut mod_attributes = Vec::new();
    let mut mod_items = Vec::new();

    let base_artifact_path = base_artifact_path.map(
        |path: Path|
        if path.segments.len() > 0 {
            let initial_segment_name =
                path.segments[0].identifier.name.as_str();
            if initial_segment_name == "self" ||
                initial_segment_name == "super" {
                    add_prefix_to_path(Ident::new(intern("super"), sc), &path)
                } else {
                    path
                }
        } else {
            path
        }
    );

    {
        // use
        mod_items.push(quote_item!(
            cx,
            use ::nametable::{
                NameTable, StaticNameTable, DynamicNameTable,
                StaticHashedNameTable, NameTableIdx};).unwrap());
    }

    {
        // const INITIAL
        if let Some(ref path) = base_artifact_path {
            mod_items.push(cx.item_use_simple(DUMMY_SP, Visibility::Inherited, path.clone()));
            let base_artifact_initial = add_suffix_to_path(path, Ident::new(intern("INITIAL"), sc));
            let base_artifact_count = add_suffix_to_path(path, Ident::new(intern("COUNT"), sc));
            mod_items.push(quote_item!(cx, pub const INITIAL : usize = $base_artifact_initial + $base_artifact_count; ).unwrap());
        } else {
            mod_items.push(quote_item!(cx, pub const INITIAL : usize = 0usize; ).unwrap());
    }

        let count = artifact_items.len();
        mod_items.push(quote_item!(cx, pub const COUNT : usize = $count; ).unwrap());
    }

    {
        // enum Names
        let ident_names = Ident::new(intern("Names"), sc);

        let mut enumdef = EnumDef { variants: Vec::new() };
        for (idx,&(key, _)) in artifact_items.iter().enumerate() {
            let ident_key = Ident::new(key, sc);
            enumdef.variants.push(respan(sp, Variant_ {
                name: ident_key,
                attrs: Vec::new(),
                data: VariantData::Unit(DUMMY_NODE_ID),
                disr_expr: Some(quote_expr!(cx, INITIAL + $idx)),
            }));
        }

        let repr_attribute = cx.attribute(
            sp, cx.meta_list(sp, intern_and_get_ident("repr"),
                             vec!(cx.meta_word(sp, intern_and_get_ident("usize")))));

        let derive_attribute = cx.attribute(
            sp, cx.meta_list(sp, intern_and_get_ident("derive"),
                             vec!(cx.meta_word(sp, intern_and_get_ident("Copy")),
                                  cx.meta_word(sp, intern_and_get_ident("Clone")))));
        mod_items.push(to_pub_item_ptr(
            cx.item(sp, ident_names,
                    vec!(repr_attribute, derive_attribute),
                    ItemKind::Enum(enumdef, Generics::default()))));
    }

    {
        // impl
        mod_items.push(quote_item!(
            cx,
            impl NameTableIdx for Names {
                fn to_index(&self) -> usize() { *self as usize }
            }
        ).unwrap());
    }

    {
        //data
        let mut name_data : MyLiteralString = MyLiteralString(String::new());
        let mut index_data : MyLiteralArray<usize> = MyLiteralArray(vec!(0));
        let mut hash_data : MyLiteralArray<MyLiteralTuple2<u64,usize>> = MyLiteralArray(vec!());
        for (idx,&(_, value)) in artifact_items.iter().enumerate() {
            name_data.0.push_str(&*value.as_str());
            index_data.0.push(name_data.0.len());
            hash_data.0.push(MyLiteralTuple2(name_hash(&*value.as_str()),idx));
        }
        hash_data.0.sort_by(|&MyLiteralTuple2(a, _), &MyLiteralTuple2(b, _)| a.cmp(&b));

        fn detect_collision<T: PartialOrd + Copy>(arr: &[MyLiteralTuple2<T, usize>]) -> Option<(usize, usize)> {
            if arr.len() == 0 {
                return None;
            }
            let mut old_val = arr.get(0).unwrap().0;
            for i in 1..arr.len() {
                if arr.get(i).unwrap().0 == old_val {
                    return Some((arr.get(i-1).unwrap().1, arr.get(i).unwrap().1));
                }
                old_val = arr.get(i).unwrap().0;
            }
            return None
        }

        match detect_collision(&hash_data.0) {
            Some((a, b)) => {
                println!(
                    "nametable_macros: Hash collision happened between item index {:} and {:} for table `{:}'",
                    a, b, &*artifact_name.as_str());
                hash_data.0 = vec!();
            },
            None => ()
        }

        mod_items.push(quote_item!(
            cx,
            const NAME_DATA : &'static str = $name_data;
        ).unwrap());
        mod_items.push(quote_item!(
            cx,
            const INDEX_DATA : &'static [usize] = &$index_data;
        ).unwrap());

        mod_items.push(quote_item!(
            cx,
            const HASH_DATA : &'static [(u64,usize)] = &$hash_data;
        ).unwrap());

    }

    {
        //functions
        match base_artifact_path {
            Some(ref path) => {
                let base_artifact_new = add_suffix_to_path(&path, Ident::new(intern("new"), sc));
                mod_items.push(
                    quote_item!(
                        cx,
                        pub fn new() -> StaticHashedNameTable {
                            StaticHashedNameTable::new_upon(NAME_DATA, INDEX_DATA, HASH_DATA, $base_artifact_new())
                        }).unwrap());

                mod_items.push(quote_item!(
                    cx,
                    pub fn new_dynamic() -> DynamicNameTable {
                        DynamicNameTable::new_upon(
                            StaticHashedNameTable::new_upon(NAME_DATA, INDEX_DATA, HASH_DATA, $base_artifact_new()))
                    }
                ).unwrap());

                mod_items.push(quote_item!(
                    cx,
                    pub fn new_plain() -> StaticNameTable {
                        StaticNameTable::new_upon(NAME_DATA, INDEX_DATA, $base_artifact_new())
                    }
                ).unwrap());

                mod_items.push(quote_item!(
                    cx,
                    pub fn new_dynamic_plain() -> DynamicNameTable {
                        DynamicNameTable::new_upon(
                            StaticNameTable::new_upon(NAME_DATA, INDEX_DATA, $base_artifact_new()))
                    }
                ).unwrap());

            }
            None => {
                mod_items.push(quote_item!(
                        cx,
                        pub fn new() -> StaticHashedNameTable {
                            StaticHashedNameTable::new(NAME_DATA, INDEX_DATA, HASH_DATA)
                        }
                ).unwrap());

                mod_items.push(quote_item!(
                    cx,
                    pub fn new_dynamic() -> DynamicNameTable {
                        DynamicNameTable::new_upon(
                            StaticHashedNameTable::new(NAME_DATA, INDEX_DATA, HASH_DATA))
                    }
                ).unwrap());

                mod_items.push(quote_item!(
                    cx,
                    pub fn new_plain() -> StaticNameTable {
                        StaticNameTable::new(NAME_DATA, INDEX_DATA)
                    }
                ).unwrap());

                mod_items.push(quote_item!(
                    cx,
                    pub fn new_dynamic_plain() -> DynamicNameTable {
                        DynamicNameTable::new_upon(
                            StaticNameTable::new(NAME_DATA, INDEX_DATA))
                    }
                ).unwrap());
            }
        }
    }

      {
          let allow_unused_attribute = cx.attribute(
            sp, cx.meta_list(sp, intern_and_get_ident("allow"),
                             vec!(cx.meta_word(sp, intern_and_get_ident("dead_code")))));

        let allow_unused_imports_attribute = cx.attribute(
            sp, cx.meta_list(sp, intern_and_get_ident("allow"),
                             vec!(cx.meta_word(sp, intern_and_get_ident("unused_imports")))));

            mod_attributes.push(allow_unused_attribute);
            mod_attributes.push(allow_unused_imports_attribute);
      }

    P(Item {
        ident: Ident::new(artifact_name, sc),
        attrs: mod_attributes,
        id: DUMMY_NODE_ID,
        node: ItemKind::Mod(Mod {
            inner: sp,
            items: mod_items,
        }),
        vis: Visibility::Public,
        span: sp,
    })
}

fn process_nametables<'cx>(cx: &'cx mut ExtCtxt, sp: Span, mut parser: Parser) -> SmallVector<P<Item>> {
    let mut result : Vec<P<Item>> = Vec::new();

    while &parser.token != &Token::Eof {
        let syntax_ctx = if let Token::Ident(nt_keyword, _) = parser.token {
            if nt_keyword.name.as_str() != "nametable" {
                cx.span_fatal(parser.span, "expected keyword `nametable' here.");
            }
            let _ = parser.bump();
            nt_keyword.ctxt
        }
        else {
            cx.span_fatal(parser.span, "expected keyword `nametable' here.");
        };

        let artifact_name = if let Token::Ident(artifact_name, _) = parser.token {
            let _ = parser.bump();
            artifact_name.name
        }
        else {
            cx.span_fatal(parser.span, "expected nametable name here.");
        };

        let base_artifact_path = if parser.eat(&Token::Colon) {
            let result = parser.parse_path(PathParsingMode::NoTypesAllowed);
            match result{
                Ok(path) => Some(path),
                _ => cx.span_fatal(parser.span, "expected base nametable name here."),
            }
            } else {
            None
        };

        if !parser.eat(&Token::OpenDelim(DelimToken::Brace)) {
            cx.span_fatal(parser.span, "expected open brace here.");
        }

        let mut artifact_items : Vec<(Name, Name)> = Vec::new();

        while let Token::Ident(item_name, _) = parser.token {
            let _ = parser.bump();
            let item_string = if parser.token.is_keyword(Keyword::For) {
                let _ = parser.bump();
                if let Token::Literal(Lit::Str_(item_string_name), _) = parser.token {
                    let _ = parser.bump();
                    item_string_name
                } else {
                    cx.span_fatal(parser.span, "expected string literal here.");
                }
            } else {
                item_name.name
            };

            artifact_items.push((item_name.name,item_string));

            if !parser.eat(&Token::Comma) {
                break;
            }
        }

        if !parser.eat(&Token::CloseDelim(DelimToken::Brace)) {
            cx.span_fatal(parser.span, "expected close brace here.");
        }

        result.push(generate_nametable_item(cx, sp, syntax_ctx, artifact_name, base_artifact_path, artifact_items));
    }

    if &parser.token != &Token::Eof {
        cx.span_fatal(parser.span, "expected end of `nametable!` macro invocation");
    }

    match result.len() {
        0 => SmallVector::zero(),
        1 => SmallVector::one(result.pop().unwrap()),
        _ => SmallVector::many(result),
    }
}

pub fn expand<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult + 'cx> {
    let parser = cx.new_parser_from_tts(tts);

    MacEager::items(process_nametables(cx, sp, parser))
}

<|endoftext|>


<commit_before>/// Asserts that the traits are [object-safe][object].
///
/// This is useful for when changes are made to a trait that accidentally
/// prevent it from being used as an [object]. Such a case would be adding a
/// generic method and forgetting to add `where Self: Sized` after it. If left
/// unnoticed, that mistake will affect crate users and break both forward and
/// backward compatibility.
///
/// # Examples
///
/// When exposing a public API, it's important that types don't lose trait
/// `impl`s across compatible crate versions.
///
#[cfg_attr(feature = "nightly", doc = "```ignore")]
#[cfg_attr(not(feature = "nightly"), doc = "```")]
/// # #[macro_use] extern crate static_assertions;
/// // Requires a unique label in module scope
/// assert_obj_safe!(basic; Send, Sync, AsRef<str>);
///
/// mod inner {
///     // Works with traits that are not in the calling module
///     pub trait BasicTrait {
///         fn foo(&self);
///     }
/// }
///
/// trait MySafeTrait {
///     fn bar(&self) -> u32;
/// }
///
/// fn main() {
///     assert_obj_safe!(MySafeTrait);
///     assert_obj_safe!(inner::BasicTrait);
/// }
/// ```
///
/// The [labeling limitation](index.html#limitations) is not necessary if
/// compiling on nightly Rust with the `nightly` feature enabled:
///
#[cfg_attr(feature = "nightly", doc = "```")]
#[cfg_attr(not(feature = "nightly"), doc = "```ignore")]
/// #![feature(underscore_const_names)]
/// # #[macro_use] extern crate static_assertions;
///
/// use std::fmt;
///
/// assert_obj_safe!(fmt::Write);
///
/// fn main() {
///     assert_obj_safe!(fmt::Debug);
/// }
/// ```
///
/// Raw pointers cannot be sent between threads safely:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// assert_impl!(*const u8, Send);
/// # }
/// ```
///
/// Generics without `where Self: Sized` are not allowed in
/// [object-safe][object] trait methods:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// trait MyUnsafeTrait {
///     fn baz<T>(&self) -> T;
/// }
///
/// # fn main() {
/// assert_obj_safe!(MyUnsafeTrait);
/// # }
/// ```
///
/// [object]: https://doc.rust-lang.org/book/2018-edition/ch17-02-trait-objects.html#object-safety-is-required-for-trait-objects
#[macro_export(local_inner_macros)]
macro_rules! assert_obj_safe {
    ($($xs:tt)+) => { _assert_obj_safe!($($xs)+); };
}

#[doc(hidden)]
#[cfg(feature = "nightly")]
#[macro_export(local_inner_macros)]
macro_rules! _assert_obj_safe {
    ($($xs:ty),+ $(,)*) => {
        $(const _: Option<&$xs> = None;)+
    };
}

#[doc(hidden)]
#[cfg(not(feature = "nightly"))]
#[macro_export(local_inner_macros)]
macro_rules! _assert_obj_safe {
    ($($xs:ty),+ $(,)*) => {
        $(let _: &$xs;)+
    };
    ($label:ident; $($xs:tt)+) => {
        #[allow(dead_code, non_snake_case)]
        fn $label() { assert_obj_safe!($($xs)+); }
    };
}
<commit_msg>Improve docs for assert_obj_safe!<commit_after>/// Asserts that the traits are [object-safe][object].
///
/// This is useful for when changes are made to a trait that accidentally
/// prevent it from being used as an [object]. Such a case would be adding a
/// generic method and forgetting to add `where Self: Sized` after it. If left
/// unnoticed, that mistake will affect crate users and break both forward and
/// backward compatibility.
///
/// # Examples
///
/// When exposing a public API, it's important that traits that could previously
/// use dynamic dispatch can still do so in future compatible crate versions.
///
#[cfg_attr(feature = "nightly", doc = "```ignore")]
#[cfg_attr(not(feature = "nightly"), doc = "```")]
/// # #[macro_use] extern crate static_assertions;
/// // Requires a unique label in module scope
/// assert_obj_safe!(basic; Send, Sync, AsRef<str>);
///
/// mod inner {
///     // Works with traits that are not in the calling module
///     pub trait BasicTrait {
///         fn foo(&self);
///     }
/// }
///
/// trait MySafeTrait {
///     fn bar(&self) -> u32;
/// }
///
/// fn main() {
///     assert_obj_safe!(MySafeTrait);
///     assert_obj_safe!(inner::BasicTrait);
/// }
/// ```
///
/// The [labeling limitation](index.html#limitations) is not necessary if
/// compiling on nightly Rust with the `nightly` feature enabled:
///
#[cfg_attr(feature = "nightly", doc = "```")]
#[cfg_attr(not(feature = "nightly"), doc = "```ignore")]
/// #![feature(underscore_const_names)]
/// # #[macro_use] extern crate static_assertions;
///
/// use std::fmt;
///
/// assert_obj_safe!(fmt::Write);
///
/// fn main() {
///     assert_obj_safe!(fmt::Debug);
/// }
/// ```
///
/// Raw pointers cannot be sent between threads safely:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// assert_impl!(*const u8, Send);
/// # }
/// ```
///
/// Generics without `where Self: Sized` are not allowed in
/// [object-safe][object] trait methods:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// trait MyUnsafeTrait {
///     fn baz<T>(&self) -> T;
/// }
///
/// # fn main() {
/// assert_obj_safe!(MyUnsafeTrait);
/// # }
/// ```
///
/// [object]: https://doc.rust-lang.org/book/2018-edition/ch17-02-trait-objects.html#object-safety-is-required-for-trait-objects
#[macro_export(local_inner_macros)]
macro_rules! assert_obj_safe {
    ($($xs:tt)+) => { _assert_obj_safe!($($xs)+); };
}

#[doc(hidden)]
#[cfg(feature = "nightly")]
#[macro_export(local_inner_macros)]
macro_rules! _assert_obj_safe {
    ($($xs:ty),+ $(,)*) => {
        $(const _: Option<&$xs> = None;)+
    };
}

#[doc(hidden)]
#[cfg(not(feature = "nightly"))]
#[macro_export(local_inner_macros)]
macro_rules! _assert_obj_safe {
    ($($xs:ty),+ $(,)*) => {
        $(let _: &$xs;)+
    };
    ($label:ident; $($xs:tt)+) => {
        #[allow(dead_code, non_snake_case)]
        fn $label() { assert_obj_safe!($($xs)+); }
    };
}
<|endoftext|>


<commit_before>// Sync available crate categories from `src/categories.toml`.
// Runs when the server is started.

use crate::db;

use anyhow::{Context, Result};
use diesel::prelude::*;

#[derive(Debug)]
struct Category {
    slug: String,
    name: String,
    description: String,
}

impl Category {
    fn from_parent(
        slug: &str,
        name: &str,
        description: &str,
        parent: Option<&Category>,
    ) -> Category {
        match parent {
            Some(parent) => Category {
                slug: format!("{}::{}", parent.slug, slug),
                name: format!("{}::{}", parent.name, name),
                description: description.into(),
            },
            None => Category {
                slug: slug.into(),
                name: name.into(),
                description: description.into(),
            },
        }
    }
}

fn required_string_from_toml<'a>(toml: &'a toml::value::Table, key: &str) -> Result<&'a str> {
    toml.get(key)
        .and_then(toml::Value::as_str)
        .with_context(|| format!("Expected category TOML attribute '{}' to be a String", key))
}

fn optional_string_from_toml<'a>(toml: &'a toml::value::Table, key: &str) -> &'a str {
    toml.get(key).and_then(toml::Value::as_str).unwrap_or("")
}

fn categories_from_toml(
    categories: &toml::value::Table,
    parent: Option<&Category>,
) -> Result<Vec<Category>> {
    let mut result = vec![];

    for (slug, details) in categories {
        let details = details
            .as_table()
            .with_context(|| format!("category {} was not a TOML table", slug))?;

        let category = Category::from_parent(
            slug,
            required_string_from_toml(details, "name")?,
            optional_string_from_toml(details, "description"),
            parent,
        );

        if let Some(categories) = details.get("categories") {
            let categories = categories
                .as_table()
                .with_context(|| format!("child categories of {} were not a table", slug))?;

            result.extend(categories_from_toml(categories, Some(&category))?);
        }

        result.push(category)
    }

    Ok(result)
}

pub fn sync(toml_str: &str) -> Result<()> {
    let conn = db::connect_now()?;
    sync_with_connection(toml_str, &conn)
}

pub fn sync_with_connection(toml_str: &str, conn: &PgConnection) -> Result<()> {
    use crate::schema::categories::dsl::*;
    use diesel::dsl::all;
    use diesel::pg::upsert::excluded;

    let toml: toml::value::Table =
        toml::from_str(toml_str).context("Could not parse categories toml")?;

    let to_insert = categories_from_toml(&toml, None)
        .expect("Could not convert categories from TOML")
        .into_iter()
        .map(|c| {
            (
                slug.eq(c.slug.to_lowercase()),
                category.eq(c.name),
                description.eq(c.description),
            )
        })
        .collect::<Vec<_>>();

    conn.transaction(|| {
        let slugs = diesel::insert_into(categories)
            .values(&to_insert)
            .on_conflict(slug)
            .do_update()
            .set((
                category.eq(excluded(category)),
                description.eq(excluded(description)),
            ))
            .returning(slug)
            .get_results::<String>(&*conn)?;

        diesel::delete(categories)
            .filter(slug.ne(all(slugs)))
            .execute(&*conn)?;
        Ok(())
    })
}
<commit_msg>Remove turbofishes from `.get_results()` calls<commit_after>// Sync available crate categories from `src/categories.toml`.
// Runs when the server is started.

use crate::db;

use anyhow::{Context, Result};
use diesel::prelude::*;

#[derive(Debug)]
struct Category {
    slug: String,
    name: String,
    description: String,
}

impl Category {
    fn from_parent(
        slug: &str,
        name: &str,
        description: &str,
        parent: Option<&Category>,
    ) -> Category {
        match parent {
            Some(parent) => Category {
                slug: format!("{}::{}", parent.slug, slug),
                name: format!("{}::{}", parent.name, name),
                description: description.into(),
            },
            None => Category {
                slug: slug.into(),
                name: name.into(),
                description: description.into(),
            },
        }
    }
}

fn required_string_from_toml<'a>(toml: &'a toml::value::Table, key: &str) -> Result<&'a str> {
    toml.get(key)
        .and_then(toml::Value::as_str)
        .with_context(|| format!("Expected category TOML attribute '{}' to be a String", key))
}

fn optional_string_from_toml<'a>(toml: &'a toml::value::Table, key: &str) -> &'a str {
    toml.get(key).and_then(toml::Value::as_str).unwrap_or("")
}

fn categories_from_toml(
    categories: &toml::value::Table,
    parent: Option<&Category>,
) -> Result<Vec<Category>> {
    let mut result = vec![];

    for (slug, details) in categories {
        let details = details
            .as_table()
            .with_context(|| format!("category {} was not a TOML table", slug))?;

        let category = Category::from_parent(
            slug,
            required_string_from_toml(details, "name")?,
            optional_string_from_toml(details, "description"),
            parent,
        );

        if let Some(categories) = details.get("categories") {
            let categories = categories
                .as_table()
                .with_context(|| format!("child categories of {} were not a table", slug))?;

            result.extend(categories_from_toml(categories, Some(&category))?);
        }

        result.push(category)
    }

    Ok(result)
}

pub fn sync(toml_str: &str) -> Result<()> {
    let conn = db::connect_now()?;
    sync_with_connection(toml_str, &conn)
}

pub fn sync_with_connection(toml_str: &str, conn: &PgConnection) -> Result<()> {
    use crate::schema::categories::dsl::*;
    use diesel::dsl::all;
    use diesel::pg::upsert::excluded;

    let toml: toml::value::Table =
        toml::from_str(toml_str).context("Could not parse categories toml")?;

    let to_insert = categories_from_toml(&toml, None)
        .expect("Could not convert categories from TOML")
        .into_iter()
        .map(|c| {
            (
                slug.eq(c.slug.to_lowercase()),
                category.eq(c.name),
                description.eq(c.description),
            )
        })
        .collect::<Vec<_>>();

    conn.transaction(|| {
        let slugs: Vec<String> = diesel::insert_into(categories)
            .values(&to_insert)
            .on_conflict(slug)
            .do_update()
            .set((
                category.eq(excluded(category)),
                description.eq(excluded(description)),
            ))
            .returning(slug)
            .get_results(&*conn)?;

        diesel::delete(categories)
            .filter(slug.ne(all(slugs)))
            .execute(&*conn)?;
        Ok(())
    })
}
<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::collections::BTreeMap;
use std::error::Error;
use std::ops::Deref;
use std::ops::DerefMut;

use ruru::{Class, Object, AnyObject, Boolean, RString, VM, Hash, NilClass, VerifiedObject};
use uuid::Uuid;

use libimagstore::store::FileLockEntry as FLE;
use libimagstore::store::EntryHeader;
use libimagstore::store::EntryContent;
use libimagstore::store::Entry;
use libimagstore::storeid::StoreId;

use ruby_utils::IntoToml;
use toml_utils::IntoRuby;
use util::Wrap;
use util::Unwrap;
use cache::RUBY_STORE_CACHE;
use cache::StoreHandle;

pub struct FileLockEntryHandle(StoreHandle, StoreId);

impl FileLockEntryHandle {
    pub fn new(sh: StoreHandle, id: StoreId) -> FileLockEntryHandle {
        FileLockEntryHandle(sh, id)
    }

    pub fn store_handle(&self) -> &StoreHandle {
        &self.0
    }

    pub fn fle_handle(&self) -> &StoreId {
        &self.1
    }
}

impl Deref for FileLockEntryHandle {
    type Target = StoreId;

    fn deref(&self) -> &Self::Target {
        &self.1
    }
}

impl DerefMut for FileLockEntryHandle {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.1
    }
}

wrappable_struct!(FileLockEntryHandle, FileLockEntryWrapper, FLE_WRAPPER);
class!(RFileLockEntry);
impl_unwrap!(RFileLockEntry, FileLockEntryHandle, FLE_WRAPPER);
impl_wrap!(FileLockEntryHandle, FLE_WRAPPER);
impl_verified_object!(RFileLockEntry);

/// Helper macro for operating on RUBY_STORE_CACHE object
///
/// This macro fetches an ARC from the RUBY_STORE_CACHE, then locks the Mutex inside it
/// and calls the $operation on the element inside the Mutex, this synchronizing the
/// operation on the RUBY_STORE_CACHE.
///
/// Yes, this is performance-wise not very elegant, but we're working with Ruby, and we need
/// to cache objects (why, see documentation for RUBY_STORE_CACHE).
///
#[macro_export]
macro_rules! operate_on_fle_cache {
    (mut |$name: ident| $operation: block) => {{
        use cache::RUBY_STORE_CACHE;

        let arc = RUBY_STORE_CACHE.clone();
        {
            let lock = arc.lock();
            match lock {
                Ok(mut $name) => { $operation },
                Err(e) => {
                    VM::raise(Class::from_existing("RuntimeError"), e.description());
                    NilClass::new().to_any_object()
                }
            }
        }
    }};
    (|$name: ident| $operation: block) => {{
        use cache::RUBY_STORE_CACHE;

        let arc = RUBY_STORE_CACHE.clone();
        {
            let lock = arc.lock();
            match lock {
                Ok($name) => { $operation },
                Err(e) => {
                    VM::raise(Class::from_existing("RuntimeError"), e.description());
                    NilClass::new().to_any_object()
                }
            }
        }
    }};
}

methods!(
    RFileLockEntry,
    itself,

    fn r_get_location() -> AnyObject {
        operate_on_fle_cache!(|hm| {
            let handle = itself.get_data(&*FLE_WRAPPER);
            match hm.get(handle.store_handle()) {
                Some(store) => {
                    match store.get(handle.fle_handle().clone()) {
                        Ok(Some(mut fle)) => {
                            fle.get_location().clone().wrap()
                        },
                        Ok(None) => {
                            VM::raise(Class::from_existing("RuntimeError"), "Obj does not exist");
                            NilClass::new().to_any_object()
                        },
                        Err(e) => {
                            VM::raise(Class::from_existing("RuntimeError"), e.description());
                            NilClass::new().to_any_object()
                        },
                    }
                },
                None => {
                    VM::raise(Class::from_existing("RuntimeError"),
                            "Tried to operate on non-existing object");
                    NilClass::new().to_any_object()
                }
            }
        })
    }

    fn r_get_header() -> AnyObject {
        operate_on_fle_cache!(|hm| {
            let handle = itself.get_data(&*FLE_WRAPPER);
            match hm.get(handle.store_handle()) {
                Some(store) => {
                    match store.get(handle.fle_handle().clone()) {
                        Ok(Some(mut fle)) => {
                            fle.get_header().clone().wrap()
                        },
                        Ok(None) => {
                            VM::raise(Class::from_existing("RuntimeError"), "Obj does not exist");
                            NilClass::new().to_any_object()
                        },
                        Err(e) => {
                            VM::raise(Class::from_existing("RuntimeError"), e.description());
                            NilClass::new().to_any_object()
                        },
                    }
                },
                None => {
                    VM::raise(Class::from_existing("RuntimeError"),
                            "Tried to operate on non-existing object");
                    NilClass::new().to_any_object()
                }
            }
        })
    }

    fn r_set_header(hdr: Hash) -> NilClass {
        use ruby_utils::IntoToml;
        use toml_utils::IntoRuby;
        use toml::Value;

        let entryheader = match typecheck!(hdr or return NilClass::new()).into_toml() {
            Value::Table(t) => EntryHeader::from(t),
            _ => {
                let ec = Class::from_existing("RuntimeError");
                VM::raise(ec, "Something weird happened. Hash seems to be not a Hash");
                return NilClass::new();
            },
        };

        operate_on_fle_cache!(mut |hm| {
            let handle = itself.get_data(&*FLE_WRAPPER);
            match hm.get_mut(handle.store_handle()) {
                Some(mut store) => {
                    match store.get(handle.fle_handle().clone()) {
                        Ok(Some(mut fle)) => {
                            *fle.get_header_mut() = entryheader;
                        },
                        Ok(None) => {
                            VM::raise(Class::from_existing("RuntimeError"), "Obj does not exist");
                        },
                        Err(e) => {
                            VM::raise(Class::from_existing("RuntimeError"), e.description());
                        },
                    }
                },
                None => {
                    VM::raise(Class::from_existing("RuntimeError"),
                            "Tried to operate on non-existing object");
                }
            }
            NilClass::new().to_any_object()
        });

        NilClass::new()
    }

    fn r_get_content() -> AnyObject {
        operate_on_fle_cache!(|hm| {
            let handle = itself.get_data(&*FLE_WRAPPER);
            match hm.get(handle.store_handle()) {
                Some(store) => {
                    match store.get(handle.fle_handle().clone()) {
                        Ok(Some(mut fle)) => {
                            fle.get_content().clone().wrap()
                        },
                        Ok(None) => {
                            VM::raise(Class::from_existing("RuntimeError"), "Obj does not exist");
                            NilClass::new().to_any_object()
                        }
                        Err(e) => {
                            VM::raise(Class::from_existing("RuntimeError"), e.description());
                            NilClass::new().to_any_object()
                        },
                    }
                }
                None => NilClass::new().to_any_object()
            }
        })
    }

    fn r_set_content(ctt: RString) -> NilClass {
        use ruby_utils::IntoToml;
        use toml_utils::IntoRuby;
        use toml::Value;

        let content = match typecheck!(ctt).into_toml() {
            Value::String(s) => s,
            _ => {
                let ec = Class::from_existing("RuntimeError");
                VM::raise(ec, "Something weird happened. String seems to be not a String");
                return NilClass::new();
            },
        };

        operate_on_fle_cache!(mut |hm| {
            let handle = itself.get_data(&*FLE_WRAPPER);
            match hm.get_mut(handle.store_handle()) {
                Some(store) => {
                    match store.get(handle.fle_handle().clone()) {
                        Ok(Some(mut fle)) => {
                            *fle.get_content_mut() = content;
                        },
                        Ok(None) => {
                            VM::raise(Class::from_existing("RuntimeError"), "Obj does not exist");
                        }
                        Err(e) => {
                            VM::raise(Class::from_existing("RuntimeError"), e.description());
                        },
                    }
                }
                None => {
                    VM::raise(Class::from_existing("RuntimeError"),
                            "Tried to operate on non-existing object");
                }
            }
            NilClass::new().to_any_object()
        });

        NilClass::new()
    }

);

wrappable_struct!(EntryHeader, EntryHeaderWrapper, ENTRY_HEADER_WRAPPER);
class!(REntryHeader);
impl_wrap!(EntryHeader, ENTRY_HEADER_WRAPPER);
impl_unwrap!(REntryHeader, EntryHeader, ENTRY_HEADER_WRAPPER);
impl_verified_object!(REntryHeader);

methods!(
    REntryHeader,
    itself,

    fn r_entry_header_new() -> AnyObject {
        EntryHeader::new().wrap()
    }

    fn r_entry_header_insert(spec: RString, obj: AnyObject) -> Boolean {
        let spec = typecheck!(spec or return Boolean::new(false)).to_string();
        let obj = obj.unwrap(); // possibly not safe... TODO

        match itself.get_data(&*ENTRY_HEADER_WRAPPER).insert(&spec, obj.into_toml()) {
            Ok(b) => Boolean::new(b),
            Err(e) => {
                VM::raise(Class::from_existing("RuntimeError"), e.description());
                Boolean::new(false)
            }
        }
    }

    fn r_entry_header_set(spec: RString, obj: AnyObject) -> AnyObject {
        use ruru::NilClass;

        let spec = typecheck!(spec or return any Boolean::new(false)).to_string();
        let obj = obj.unwrap(); // possibly not safe... TODO

        match itself.get_data(&*ENTRY_HEADER_WRAPPER).set(&spec, obj.into_toml()) {
            Ok(Some(v)) => v.into_ruby(),
            Ok(None)    => NilClass::new().to_any_object(),
            Err(e) => {
                VM::raise(Class::from_existing("RuntimeError"), e.description());
                return Boolean::new(false).to_any_object();
            }
        }
    }

    fn r_entry_header_get(spec: RString) -> AnyObject {
        use ruru::NilClass;

        let spec = typecheck!(spec or return any Boolean::new(false)).to_string();

        match itself.get_data(&*ENTRY_HEADER_WRAPPER).read(&spec) {
            Ok(Some(v)) => v.into_ruby(),
            Ok(None)    => NilClass::new().to_any_object(),
            Err(e) => {
                VM::raise(Class::from_existing("RuntimeError"), e.description());
                return Boolean::new(false).to_any_object();
            }
        }
    }

);

wrappable_struct!(EntryContent, EntryContentWrapper, ENTRY_CONTENT_WRAPPER);
class!(REntryContent);
impl_wrap!(EntryContent, ENTRY_CONTENT_WRAPPER);
impl_unwrap!(REntryContent, EntryContent, ENTRY_CONTENT_WRAPPER);

wrappable_struct!(Entry, EntryWrapper, ENTRY_WRAPPER);
class!(REntry);
impl_unwrap!(REntry, Entry, ENTRY_WRAPPER);

pub fn setup_filelockentry() -> Class {
    let mut class = Class::new("RFileLockEntry", None);
    class.define(|itself| {
        itself.def("location", r_get_location);
        itself.def("header"  , r_get_header);
        itself.def("header=" , r_set_header);
        itself.def("content" , r_get_content);
        itself.def("content=", r_set_content);
    });
    class
}

pub fn setup_entryheader() -> Class {
    let mut class = Class::new("REntryHeader", None);
    class.define(|itself| {
        itself.def("insert", r_entry_header_insert);
        itself.def("set"   , r_entry_header_set);
        itself.def("read"  , r_entry_header_get);
    });
    class
}

pub fn setup_entrycontent() -> Class {
    let string = Class::from_existing("String");
    let mut class = Class::new("REntryContent", Some(&string));
    class
}
<commit_msg>Rewrite FileLockEntry interface to work with Store in cache<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::collections::BTreeMap;
use std::error::Error;
use std::ops::Deref;
use std::ops::DerefMut;

use ruru::{Class, Object, AnyObject, Boolean, RString, VM, Hash, NilClass, VerifiedObject};
use uuid::Uuid;

use libimagstore::store::FileLockEntry as FLE;
use libimagstore::store::EntryHeader;
use libimagstore::store::EntryContent;
use libimagstore::store::Entry;
use libimagstore::storeid::StoreId;

use ruby_utils::IntoToml;
use toml_utils::IntoRuby;
use util::Wrap;
use util::Unwrap;
use cache::RUBY_STORE_CACHE;
use cache::StoreHandle;

pub struct FileLockEntryHandle(StoreHandle, StoreId);

impl FileLockEntryHandle {
    pub fn new(sh: StoreHandle, id: StoreId) -> FileLockEntryHandle {
        FileLockEntryHandle(sh, id)
    }

    pub fn store_handle(&self) -> &StoreHandle {
        &self.0
    }

    pub fn fle_handle(&self) -> &StoreId {
        &self.1
    }
}

impl Deref for FileLockEntryHandle {
    type Target = StoreId;

    fn deref(&self) -> &Self::Target {
        &self.1
    }
}

impl DerefMut for FileLockEntryHandle {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.1
    }
}

wrappable_struct!(FileLockEntryHandle, FileLockEntryWrapper, FLE_WRAPPER);
class!(RFileLockEntry);
impl_unwrap!(RFileLockEntry, FileLockEntryHandle, FLE_WRAPPER);
impl_wrap!(FileLockEntryHandle, FLE_WRAPPER);
impl_verified_object!(RFileLockEntry);

/// Helper macro for operating on RUBY_STORE_CACHE object
///
/// This helps us calling operations on FileLockEntry objects.
///
/// What I do here: Fetch the Store object from the cache, fetch the appropriate FileLockEntry and
/// call the operation on it.
///
/// This could be improved with another cache, so not the store is cached but the FileLockEntry
/// only, but then we run into lifetime problems with the Store and its FileLockEntry objects.
/// Feel free to fix this, but for now, this is a workable solution.
///
#[macro_export]
macro_rules! call_on_fle_from_store {
    ($itself:ident ($wrapper:ident) -> $name:ident -> $operation:block) => {{
        use cache::RUBY_STORE_CACHE;

        let arc = RUBY_STORE_CACHE.clone();
        {
            let lock = arc.lock();
            match lock {
                Ok(mut hm) => {
                    let handle = $itself.get_data(&*$wrapper);
                    match hm.get(handle.store_handle()) {
                        Some(store) => {
                            match store.get(handle.fle_handle().clone()) {
                                Ok(Some(mut $name)) => {
                                    $operation
                                },
                                Ok(None) => {
                                    VM::raise(Class::from_existing("RuntimeError"), "Obj does not exist");
                                    NilClass::new().to_any_object()
                                },
                                Err(e) => {
                                    VM::raise(Class::from_existing("RuntimeError"), e.description());
                                    NilClass::new().to_any_object()
                                },
                            }
                        },
                        None => {
                            VM::raise(Class::from_existing("RuntimeError"),
                                    "Tried to operate on non-existing object");
                            NilClass::new().to_any_object()
                        }
                    }
                },
                Err(e) => {
                    VM::raise(Class::from_existing("RuntimeError"), e.description());
                    NilClass::new().to_any_object()
                }
            }
        }
    }};
    ($itself:ident ($wrapper:ident) -> $name:ident -> $operation: block on fail return $ex:expr) => {{
        use cache::RUBY_STORE_CACHE;

        let arc = RUBY_STORE_CACHE.clone();
        {
            let lock = arc.lock();
            match lock {
                Ok(mut hm) => {
                    let handle = $itself.get_data(&*$wrapper);
                    match hm.get(handle.store_handle()) {
                        Some(store) => {
                            match store.get(handle.fle_handle().clone()) {
                                Ok(Some(mut $name)) => {
                                    $operation
                                },
                                Ok(None) => {
                                    VM::raise(Class::from_existing("RuntimeError"), "Obj does not exist");
                                    $ex
                                },
                                Err(e) => {
                                    VM::raise(Class::from_existing("RuntimeError"), e.description());
                                    $ex
                                },
                            }
                        },
                        None => {
                            VM::raise(Class::from_existing("RuntimeError"),
                                    "Tried to operate on non-existing object");
                            NilClass::new().to_any_object()
                        }
                    }
                },
                Err(e) => {
                    VM::raise(Class::from_existing("RuntimeError"), e.description());
                    NilClass::new().to_any_object()
                }
            }
        }
    }};
}

methods!(
    RFileLockEntry,
    itself,

    fn r_get_location() -> AnyObject {
        call_on_fle_from_store!(itself (FLE_WRAPPER) -> fle -> { fle.get_location().clone().wrap() })
    }

    fn r_get_header() -> AnyObject {
        call_on_fle_from_store!(itself (FLE_WRAPPER) -> fle -> { fle.get_header().clone().wrap() })
    }

    fn r_set_header(hdr: Hash) -> NilClass {
        use ruby_utils::IntoToml;
        use toml_utils::IntoRuby;
        use toml::Value;

        let entryheader = match typecheck!(hdr or return NilClass::new()).into_toml() {
            Value::Table(t) => EntryHeader::from(t),
            _ => {
                let ec = Class::from_existing("RuntimeError");
                VM::raise(ec, "Something weird happened. Hash seems to be not a Hash");
                return NilClass::new();
            },
        };

        call_on_fle_from_store!(itself (FLE_WRAPPER) -> fle -> {
            *fle.get_header_mut() = entryheader;
            NilClass::new().to_any_object()
        });

        NilClass::new()
    }

    fn r_get_content() -> AnyObject {
        call_on_fle_from_store!(itself (FLE_WRAPPER) -> fle -> {
            fle.get_content().clone().wrap()
        } on fail return NilClass::new().to_any_object())
    }

    fn r_set_content(ctt: RString) -> NilClass {
        use ruby_utils::IntoToml;
        use toml_utils::IntoRuby;
        use toml::Value;

        let content = match typecheck!(ctt).into_toml() {
            Value::String(s) => s,
            _ => {
                let ec = Class::from_existing("RuntimeError");
                VM::raise(ec, "Something weird happened. String seems to be not a String");
                return NilClass::new();
            },
        };

        call_on_fle_from_store!(itself (FLE_WRAPPER) -> fle -> {
            *fle.get_content_mut() = content;
            NilClass::new().to_any_object()
        });

        NilClass::new()
    }

);

wrappable_struct!(EntryHeader, EntryHeaderWrapper, ENTRY_HEADER_WRAPPER);
class!(REntryHeader);
impl_wrap!(EntryHeader, ENTRY_HEADER_WRAPPER);
impl_unwrap!(REntryHeader, EntryHeader, ENTRY_HEADER_WRAPPER);
impl_verified_object!(REntryHeader);

methods!(
    REntryHeader,
    itself,

    fn r_entry_header_new() -> AnyObject {
        EntryHeader::new().wrap()
    }

    fn r_entry_header_insert(spec: RString, obj: AnyObject) -> Boolean {
        let spec = typecheck!(spec or return Boolean::new(false)).to_string();
        let obj = obj.unwrap(); // possibly not safe... TODO

        match itself.get_data(&*ENTRY_HEADER_WRAPPER).insert(&spec, obj.into_toml()) {
            Ok(b) => Boolean::new(b),
            Err(e) => {
                VM::raise(Class::from_existing("RuntimeError"), e.description());
                Boolean::new(false)
            }
        }
    }

    fn r_entry_header_set(spec: RString, obj: AnyObject) -> AnyObject {
        use ruru::NilClass;

        let spec = typecheck!(spec or return any Boolean::new(false)).to_string();
        let obj = obj.unwrap(); // possibly not safe... TODO

        match itself.get_data(&*ENTRY_HEADER_WRAPPER).set(&spec, obj.into_toml()) {
            Ok(Some(v)) => v.into_ruby(),
            Ok(None)    => NilClass::new().to_any_object(),
            Err(e) => {
                VM::raise(Class::from_existing("RuntimeError"), e.description());
                return Boolean::new(false).to_any_object();
            }
        }
    }

    fn r_entry_header_get(spec: RString) -> AnyObject {
        use ruru::NilClass;

        let spec = typecheck!(spec or return any Boolean::new(false)).to_string();

        match itself.get_data(&*ENTRY_HEADER_WRAPPER).read(&spec) {
            Ok(Some(v)) => v.into_ruby(),
            Ok(None)    => NilClass::new().to_any_object(),
            Err(e) => {
                VM::raise(Class::from_existing("RuntimeError"), e.description());
                return Boolean::new(false).to_any_object();
            }
        }
    }

);

wrappable_struct!(EntryContent, EntryContentWrapper, ENTRY_CONTENT_WRAPPER);
class!(REntryContent);
impl_wrap!(EntryContent, ENTRY_CONTENT_WRAPPER);
impl_unwrap!(REntryContent, EntryContent, ENTRY_CONTENT_WRAPPER);

wrappable_struct!(Entry, EntryWrapper, ENTRY_WRAPPER);
class!(REntry);
impl_unwrap!(REntry, Entry, ENTRY_WRAPPER);

pub fn setup_filelockentry() -> Class {
    let mut class = Class::new("RFileLockEntry", None);
    class.define(|itself| {
        itself.def("location", r_get_location);
        itself.def("header"  , r_get_header);
        itself.def("header=" , r_set_header);
        itself.def("content" , r_get_content);
        itself.def("content=", r_set_content);
    });
    class
}

pub fn setup_entryheader() -> Class {
    let mut class = Class::new("REntryHeader", None);
    class.define(|itself| {
        itself.def("insert", r_entry_header_insert);
        itself.def("set"   , r_entry_header_set);
        itself.def("read"  , r_entry_header_get);
    });
    class
}

pub fn setup_entrycontent() -> Class {
    let string = Class::from_existing("String");
    let mut class = Class::new("REntryContent", Some(&string));
    class
}
<|endoftext|>


<commit_before>use std::{
    cell::{Cell, RefCell},
    collections::BTreeMap,
};

use dces::prelude::Entity;

use crate::{prelude::*, render::RenderContext2D, tree::Tree, utils::prelude::*};

use super::{component, component_try_mut, try_component, Layout};

/// The text selection layout is used to measure and arrange a text selection cursor.
#[derive(Default)]
pub struct TextSelectionLayout {
    desired_size: RefCell<DirtySize>,
    old_text_selection: Cell<TextSelection>,
}

impl TextSelectionLayout {
    pub fn new() -> Self {
        TextSelectionLayout::default()
    }
}

impl Into<Box<dyn Layout>> for TextSelectionLayout {
    fn into(self) -> Box<dyn Layout> {
        Box::new(self)
    }
}

impl Layout for TextSelectionLayout {
    fn measure(
        &self,
        render_context_2_d: &mut RenderContext2D,
        entity: Entity,
        ecm: &mut EntityComponentManager<Tree, StringComponentStore>,
        layouts: &BTreeMap<Entity, Box<dyn Layout>>,
        theme: &ThemeValue,
    ) -> DirtySize {
        if component::<Visibility>(ecm, entity, "visibility") == Visibility::Collapsed {
            self.desired_size.borrow_mut().set_size(0.0, 0.0);
            return *self.desired_size.borrow();
        }

        let constraint: Constraint = component(ecm, entity, "constraint");

        if let Ok(selection) = ecm
            .component_store()
            .get::<TextSelection>("text_selection", entity)
        {
            if *selection != self.old_text_selection.get() {
                self.desired_size.borrow_mut().set_dirty(true);
            }

            self.old_text_selection.set(*selection);
        }

        for index in 0..ecm.entity_store().children[&entity].len() {
            let child = ecm.entity_store().children[&entity][index];

            if let Some(child_layout) = layouts.get(&child) {
                let dirty = child_layout
                    .measure(render_context_2_d, child, ecm, layouts, theme)
                    .dirty()
                    || self.desired_size.borrow().dirty();
                self.desired_size.borrow_mut().set_dirty(dirty);
            }
        }

        if constraint.width() > 0.0 {
            self.desired_size.borrow_mut().set_width(constraint.width());
        }

        if constraint.height() > 0.0 {
            self.desired_size
                .borrow_mut()
                .set_height(constraint.height());
        }

        for index in 0..ecm.entity_store().children[&entity].len() {
            let child = ecm.entity_store().children[&entity][index];

            if let Some(child_layout) = layouts.get(&child) {
                let dirty = child_layout
                    .measure(render_context_2_d, child, ecm, layouts, theme)
                    .dirty()
                    || self.desired_size.borrow().dirty();
                self.desired_size.borrow_mut().set_dirty(dirty);
            }
        }

        *self.desired_size.borrow()
    }

    fn arrange(
        &self,
        render_context_2_d: &mut RenderContext2D,
        parent_size: (f64, f64),
        entity: Entity,
        ecm: &mut EntityComponentManager<Tree, StringComponentStore>,
        layouts: &BTreeMap<Entity, Box<dyn Layout>>,
        theme: &ThemeValue,
    ) -> (f64, f64) {
        if component::<Visibility>(ecm, entity, "visibility") == Visibility::Collapsed {
            self.desired_size.borrow_mut().set_size(0.0, 0.0);
            return (0.0, 0.0);
        }

        let mut pos = 0.0;
        let mut size = self.desired_size.borrow().size();

        let vertical_alignment: Alignment = component(ecm, entity, "vertical_alignment");
        let margin: Thickness = *ecm.component_store().get("margin", entity).unwrap();
        let text_block: Entity = component::<u32>(ecm, entity, "text_block").into();
        let width = component::<Constraint>(ecm, entity, "constraint").width();
        let mut text_len = 0;

        {
            size.1 = vertical_alignment.align_measure(
                parent_size.1,
                size.1,
                margin.top(),
                margin.bottom(),
            );

            if let Some(text) = try_component::<String16>(ecm, text_block, "text") {
                let font: String = component(ecm, text_block, "font");
                let font_size: f64 = component(ecm, text_block, "font_size");
                text_len = text.len();

                if let Some(selection) =
                    try_component::<TextSelection>(ecm, entity, "text_selection")
                {
                    if let Some(text_part) = text.get_string(0, selection.start_index) {
                        pos = render_context_2_d
                            .measure(text_part.as_str(), font_size, font.as_str())
                            .width;
                    }

                    if selection.length > 0 {
                        if let Some(text_part) = text.get_string(
                            selection.start_index,
                            selection.start_index + selection.length,
                        ) {
                            size.0 = render_context_2_d
                                .measure(text_part.as_str(), font_size, font.as_str())
                                .width;
                        }
                    } else {
                        size.0 = width;
                    }
                }
            }

            let parent = ecm
                .entity_store()
                .parent
                .get(&entity)
                .expect("TextSelection.arrange: Cursor does not have a parent.")
                .unwrap();

            let view_port_width = component::<Rectangle>(ecm, parent, "bounds").width();

            if !component::<bool>(ecm, entity, "expanded") {
                // reset text block position
                if let Some(margin) = component_try_mut::<Thickness>(ecm, text_block, "margin") {
                    margin.set_left(0.0);
                }

                if pos < 0.0 || pos + size.0 > view_port_width {
                    let delta = if pos < 0.0 {
                        pos
                    } else {
                        pos - view_port_width + size.0
                    };

                    pos = pos - delta;

                    // adjust the position of the text block
                    if text_len > 0 {
                        if let Some(margin) =
                            component_try_mut::<Thickness>(ecm, text_block, "margin")
                        {
                            margin.set_left(-delta);
                        }
                    }
                }
            }

            if let Some(margin) = component_try_mut::<Thickness>(ecm, entity, "margin") {
                margin.set_left(pos);
            }

            if let Some(bounds) = component_try_mut::<Rectangle>(ecm, entity, "bounds") {
                bounds.set_width(size.0);
                bounds.set_height(size.1);
            }
        }

        for index in 0..ecm.entity_store().children[&entity].len() {
            let child = ecm.entity_store().children[&entity][index];

            if let Some(child_layout) = layouts.get(&child) {
                child_layout.arrange(render_context_2_d, size, child, ecm, layouts, theme);
            }
        }

        self.desired_size.borrow_mut().set_dirty(false);
        size
    }
}
<commit_msg>Small fix.<commit_after>use std::{
    cell::{Cell, RefCell},
    collections::BTreeMap,
};

use dces::prelude::Entity;

use crate::{prelude::*, render::RenderContext2D, tree::Tree, utils::prelude::*};

use super::{component, component_try_mut, try_component, Layout};

/// The text selection layout is used to measure and arrange a text selection cursor.
#[derive(Default)]
pub struct TextSelectionLayout {
    desired_size: RefCell<DirtySize>,
    old_text_selection: Cell<TextSelection>,
}

impl TextSelectionLayout {
    pub fn new() -> Self {
        TextSelectionLayout::default()
    }
}

impl Into<Box<dyn Layout>> for TextSelectionLayout {
    fn into(self) -> Box<dyn Layout> {
        Box::new(self)
    }
}

impl Layout for TextSelectionLayout {
    fn measure(
        &self,
        render_context_2_d: &mut RenderContext2D,
        entity: Entity,
        ecm: &mut EntityComponentManager<Tree, StringComponentStore>,
        layouts: &BTreeMap<Entity, Box<dyn Layout>>,
        theme: &ThemeValue,
    ) -> DirtySize {
        if component::<Visibility>(ecm, entity, "visibility") == Visibility::Collapsed {
            self.desired_size.borrow_mut().set_size(0.0, 0.0);
            return *self.desired_size.borrow();
        }

        let constraint: Constraint = component(ecm, entity, "constraint");

        if let Ok(selection) = ecm
            .component_store()
            .get::<TextSelection>("text_selection", entity)
        {
            if *selection != self.old_text_selection.get() {
                self.desired_size.borrow_mut().set_dirty(true);
            }

            self.old_text_selection.set(*selection);
        }

        for index in 0..ecm.entity_store().children[&entity].len() {
            let child = ecm.entity_store().children[&entity][index];

            if let Some(child_layout) = layouts.get(&child) {
                let dirty = child_layout
                    .measure(render_context_2_d, child, ecm, layouts, theme)
                    .dirty()
                    || self.desired_size.borrow().dirty();
                self.desired_size.borrow_mut().set_dirty(dirty);
            }
        }

        if constraint.width() > 0.0 {
            self.desired_size.borrow_mut().set_width(constraint.width());
        }

        if constraint.height() > 0.0 {
            self.desired_size
                .borrow_mut()
                .set_height(constraint.height());
        }

        for index in 0..ecm.entity_store().children[&entity].len() {
            let child = ecm.entity_store().children[&entity][index];

            if let Some(child_layout) = layouts.get(&child) {
                let dirty = child_layout
                    .measure(render_context_2_d, child, ecm, layouts, theme)
                    .dirty()
                    || self.desired_size.borrow().dirty();
                self.desired_size.borrow_mut().set_dirty(dirty);
            }
        }

        *self.desired_size.borrow()
    }

    fn arrange(
        &self,
        render_context_2_d: &mut RenderContext2D,
        parent_size: (f64, f64),
        entity: Entity,
        ecm: &mut EntityComponentManager<Tree, StringComponentStore>,
        layouts: &BTreeMap<Entity, Box<dyn Layout>>,
        theme: &ThemeValue,
    ) -> (f64, f64) {
        if component::<Visibility>(ecm, entity, "visibility") == Visibility::Collapsed {
            self.desired_size.borrow_mut().set_size(0.0, 0.0);
            return (0.0, 0.0);
        }

        let mut pos = 0.0;
        let mut size = self.desired_size.borrow().size();

        let vertical_alignment: Alignment = component(ecm, entity, "vertical_alignment");
        let margin: Thickness = *ecm.component_store().get("margin", entity).unwrap();
        let text_block: Entity = component::<u32>(ecm, entity, "text_block").into();
        let width = component::<Constraint>(ecm, entity, "constraint").width();
        let mut text_len = 0;

        {
            size.1 = vertical_alignment.align_measure(
                parent_size.1,
                size.1,
                margin.top(),
                margin.bottom(),
            );

            if let Some(text) = try_component::<String16>(ecm, text_block, "text") {
                let font: String = component(ecm, text_block, "font");
                let font_size: f64 = component(ecm, text_block, "font_size");
                text_len = text.len();

                if let Some(selection) =
                    try_component::<TextSelection>(ecm, entity, "text_selection")
                {
                    if let Some(text_part) = text.get_string(0, selection.start_index) {
                        pos = render_context_2_d
                            .measure(text_part.as_str(), font_size, font.as_str())
                            .width;
                    }

                    if selection.length > 0 {
                        if let Some(text_part) = text.get_string(
                            selection.start_index,
                            selection.start_index + selection.length,
                        ) {
                            size.0 = render_context_2_d
                                .measure(text_part.as_str(), font_size, font.as_str())
                                .width;
                        }
                    } else {
                        size.0 = width;
                    }
                }
            }

            let parent = ecm
                .entity_store()
                .parent
                .get(&entity)
                .expect("TextSelection.arrange: Cursor does not have a parent.")
                .unwrap();

            let view_port_width = component::<Rectangle>(ecm, parent, "bounds").width();

            // reset text block position
            if !component::<bool>(ecm, entity, "expanded") || text_len == 0 {
                if let Some(margin) = component_try_mut::<Thickness>(ecm, text_block, "margin") {
                    margin.set_left(0.0);
                }
            }

            if !component::<bool>(ecm, entity, "expanded") {
                if pos < 0.0 || pos + size.0 > view_port_width {
                    let delta = if pos < 0.0 {
                        pos
                    } else {
                        pos - view_port_width + size.0
                    };

                    pos = pos - delta;

                    // adjust the position of the text block
                    if text_len > 0 {
                        if let Some(margin) =
                            component_try_mut::<Thickness>(ecm, text_block, "margin")
                        {
                            margin.set_left(-delta);
                        }
                    }
                }
            }
            if let Some(margin) = component_try_mut::<Thickness>(ecm, entity, "margin") {
                margin.set_left(pos);
            }

            if let Some(bounds) = component_try_mut::<Rectangle>(ecm, entity, "bounds") {
                bounds.set_width(size.0);
                bounds.set_height(size.1);
            }
        }

        for index in 0..ecm.entity_store().children[&entity].len() {
            let child = ecm.entity_store().children[&entity][index];

            if let Some(child_layout) = layouts.get(&child) {
                child_layout.arrange(render_context_2_d, size, child, ecm, layouts, theme);
            }
        }

        self.desired_size.borrow_mut().set_dirty(false);
        size
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

#[derive(Debug, PartialEq)]
pub enum CacheError {
    CapacityExceeded,
    KeyNotFound,
}

pub type CacheResult<T> = Result<T, CacheError>;

#[derive(PartialEq, Eq, Hash, Debug, Clone)]
pub struct Key {
    item: Vec<u8>,
}

impl Key {
    fn new(item: Vec<u8>) -> Key {
        Key { item: item }
    }

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

#[derive(PartialEq, Eq, Debug, Clone)]
pub struct Value {
    item: Vec<u8>,
}

impl Value {
    fn new(item: Vec<u8>) -> Value {
        Value { item: item }
    }

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

pub struct Cache {
    capacity: u64,
//    key_maxlen: u64,
//    value_maxlen: u64,
    storage: HashMap<Key, Value>,
}

impl Cache {
    fn new(capacity: u64) -> Cache {
        Cache {
            capacity: capacity,
            storage: HashMap::new(),
        }
    }

    fn get(&self, key: Key) -> CacheResult<&Value> {
        // check key size

        match self.storage.get(&key) {
            Some(value) => Ok(value),
            None => Err(CacheError::KeyNotFound),
        }
    }

    fn set(&mut self, key: Key, value: Value) -> CacheResult<()> {
        // check key & value sizes

        // Check capacity if adding new key
        if (!self.storage.contains_key(&key)) {
            if (self.storage.len() as u64 == self.capacity) {
                return Err(CacheError::CapacityExceeded);
            }
        }

        self.storage.insert(key, value);
        Ok(())
    }
}

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

    // helper func since assert_eq!(rv.unwrap(), err) does not work
    fn assert_rv_eq<T>(rv: CacheResult<T>, err: CacheError) {
        // First of all it's supposed to be an error
        assert!(rv.is_err());

        // Now check if the error constructor is the right one
        match rv {
            Ok(_) => (),
            Err(e) => { assert_eq!(e, err); }
        };
    }

    #[test]
    fn test_set_one_key() {
        let mut cache = Cache::new(1);

        let key = Key::new(vec![1, 2, 3]);
        let value = Value::new(vec![4, 5, 6]);

        cache.set(key.clone(), value.clone());
        let value_found = cache.get(key).unwrap();

        assert_eq!(&value, value_found);
    }

    #[test]
    fn test_key_not_found() {
        let mut cache = Cache::new(1);

        // Retrieve a different key to the one set
        cache.set(Key::new(vec![1]), Value::new(vec![9]));
        let rv = cache.get(Key::new(vec![2]));

        assert_rv_eq(rv, CacheError::KeyNotFound);
    }

    #[test]
    fn test_store_beyond_capacity() {
        let mut cache = Cache::new(1);

        // we reached capacity
        let rv = cache.set(Key::new(vec![1]), Value::new(vec![9]));
        assert!(rv.is_ok());

        // overwrite is ok
        let rv = cache.set(Key::new(vec![1]), Value::new(vec![9]));
        assert!(rv.is_ok());

        // cannot store a new key
        let rv = cache.set(Key::new(vec![2]), Value::new(vec![9]));
        assert_rv_eq(rv, CacheError::CapacityExceeded);
    }

}
<commit_msg>implement size checks for keys and values<commit_after>use std::collections::HashMap;

#[derive(Debug, PartialEq)]
pub enum CacheError {
    CapacityExceeded,
    KeyNotFound,
    KeyTooLong,
    ValueTooLong,
}

pub type CacheResult<T> = Result<T, CacheError>;

#[derive(PartialEq, Eq, Hash, Debug, Clone)]
pub struct Key {
    item: Vec<u8>,
}

impl Key {
    fn new(item: Vec<u8>) -> Key {
        Key { item: item }
    }

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

#[derive(PartialEq, Eq, Debug, Clone)]
pub struct Value {
    item: Vec<u8>,
}

impl Value {
    fn new(item: Vec<u8>) -> Value {
        Value { item: item }
    }

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

pub struct Cache {
    capacity: u64,
    key_maxlen: u64,
    value_maxlen: u64,
    storage: HashMap<Key, Value>,
}

impl Cache {
    fn new(capacity: u64, key_maxlen: u64, value_maxlen: u64) -> Cache {
        Cache {
            capacity: capacity,
            key_maxlen: key_maxlen,
            value_maxlen: value_maxlen,
            storage: HashMap::new(),
        }
    }

    fn with_defaults(capacity: u64) -> Cache {
        Cache::new(
            capacity,
            1024, // key_maxlen = 1kb
            1048576 // value_maxlen = 1mb
            )
    }

    fn check_key_len(&self, key: &Key) -> bool {
        key.len() as u64 <= self.key_maxlen
    }

    fn check_value_len(&self, value: &Value) -> bool {
        value.len() as u64 <= self.value_maxlen
    }

    fn get(&self, key: Key) -> CacheResult<&Value> {
        // Check key size
        if (!self.check_key_len(&key)) {
            return Err(CacheError::KeyTooLong);
        }

        match self.storage.get(&key) {
            Some(value) => Ok(value),
            None => Err(CacheError::KeyNotFound),
        }
    }

    fn set(&mut self, key: Key, value: Value) -> CacheResult<()> {
        // Check key & value sizes
        if (!self.check_key_len(&key)) {
            return Err(CacheError::KeyTooLong);
        }
        if (!self.check_value_len(&value)) {
            return Err(CacheError::ValueTooLong);
        }

        // Check capacity if adding new key
        if (!self.storage.contains_key(&key)) {
            if (self.storage.len() as u64 == self.capacity) {
                return Err(CacheError::CapacityExceeded);
            }
        }

        self.storage.insert(key, value);
        Ok(())
    }
}

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

    // helper func since assert_eq!(rv.unwrap(), err) does not work
    fn assert_rv_eq<T>(rv: CacheResult<T>, err: CacheError) {
        // First of all it's supposed to be an error
        assert!(rv.is_err());

        // Now check if the error constructor is the right one
        match rv {
            Ok(_) => (),
            Err(e) => { assert_eq!(e, err); }
        };
    }

    #[test]
    fn test_set_one_key() {
        let mut cache = Cache::with_defaults(1);

        let key = Key::new(vec![1, 2, 3]);
        let value = Value::new(vec![4, 5, 6]);

        cache.set(key.clone(), value.clone());
        let value_found = cache.get(key).unwrap();

        assert_eq!(&value, value_found);
    }

    #[test]
    fn test_key_not_found() {
        let mut cache = Cache::with_defaults(1);

        // Retrieve a different key to the one set
        cache.set(Key::new(vec![1]), Value::new(vec![9]));
        let rv = cache.get(Key::new(vec![2]));

        assert_rv_eq(rv, CacheError::KeyNotFound);
    }

    #[test]
    fn test_store_beyond_capacity() {
        let mut cache = Cache::with_defaults(1);

        // we've now reached capacity
        let rv = cache.set(Key::new(vec![1]), Value::new(vec![9]));
        assert!(rv.is_ok());

        // overwriting is ok
        let rv = cache.set(Key::new(vec![1]), Value::new(vec![9]));
        assert!(rv.is_ok());

        // but we cannot store a new key
        let rv = cache.set(Key::new(vec![2]), Value::new(vec![9]));
        assert_rv_eq(rv, CacheError::CapacityExceeded);
    }

    #[test]
    fn test_exceed_item_size_limits() {
        let mut cache = Cache::new(1, 1, 1);

        // use a key that is too long
        let rv = cache.set(Key::new(vec![1, 2]), Value::new(vec![9]));
        assert_rv_eq(rv, CacheError::KeyTooLong);

        // use a value that is too long
        let rv = cache.set(Key::new(vec![1]), Value::new(vec![9, 8]));
        assert_rv_eq(rv, CacheError::ValueTooLong);

        // use a key that is too long
        let rv = cache.get(Key::new(vec![1, 2]));
        assert_rv_eq(rv, CacheError::KeyTooLong);
    }
}
<|endoftext|>