<commit_before>use syn::{self, Token, TokenTree, Ident, BinOpToken, DelimToken};
use quote::{Tokens, ToTokens};
use std::collections::HashMap;

#[derive(Debug)]
pub struct Args<'a> {
    input: &'a str,
    pub tokens: Tokens,
}

#[derive(Debug, Clone, Copy, PartialEq)]
enum Type {
    Expr,
    Closure,
    List,
    Object,
}

#[derive(Debug, Clone)]
enum Group {
    Expr(Vec<TokenTree>),
    Closure(Vec<TokenTree>),
    List(Vec<Group>),
    Object(HashMap<String, Group>),
}

impl<'a> Args<'a> {
    pub fn new(input: &str) -> Args {
        Args {
            input: input.trim(),
            tokens: Tokens::new(),
        }
    }

    pub fn process(&mut self) -> &mut Self {
        if self.input.is_empty() {
            self.tokens = quote!(reql::Term::new());
            return self;
        }

        let args = format!("args!({})", self.input);
        let body = self.body();

        self.tokens = quote!({
            #[allow(unused_imports)]
            use reql::{IntoArg, Arg, Term};

            let mut args = Arg::new();
            args.set_string(#args);
            #body
            args
        });

        self
    }

    fn body(&mut self) -> Tokens {
        let tt = syn::parse_token_trees(self.input).expect("failed to parse token tree");
        let mut tokens = Tokens::new();
        let args = self.group_by_comma(tt);
        panic!(format!("{:?}", args));
        let last = args.len()-1;
        for (i, arg) in args.into_iter().enumerate() {
            arg.tokenise("args", i == last).to_tokens(&mut tokens);
        }
        tokens
    }

    fn finalise_arg(&self, args: &mut Vec<Group>, tokens: &mut Vec<TokenTree>, typ: &mut Type, found_lbar: &mut bool, found_rbar: &mut bool, found_body: &mut bool) {
        let group = match *typ {
            Type::Expr => Group::Expr(tokens.clone()),
            Type::Closure => Group::Closure(tokens.clone()),
            Type::List => Group::List(self.group_by_comma(tokens.clone())),
            Type::Object => Group::Object(self.hash_from_tokens(tokens.clone())),
        };
        args.push(group);
        *tokens = Vec::new();
        *typ = Type::Expr;
        *found_lbar = false;
        *found_rbar = false;
        *found_body = false;
    }

    fn hash_from_tokens(&self, tt: Vec<TokenTree>) -> HashMap<String, Group> {
        let mut hash = HashMap::new();

        let mut is_key = true;
        let mut key = String::new();
        let mut val = Vec::new();
        let last = tt.len()-1;

        for (i, tree) in tt.into_iter().enumerate() {
            if let TokenTree::Token(Token::Colon) = tree {
                if key.is_empty() {
                    panic!("A key of an object cannot be empty.");
                }
                is_key = false;
                continue;
            }
            if is_key {
                key = match tree {
                    TokenTree::Token(Token::Literal(syn::Lit::Str(key, _))) => key,
                    tree => {
                        let mut tokens = Tokens::new();
                        tree.to_tokens(&mut tokens);
                        tokens.to_string()
                    }
                };
                continue;
            }
            let token_is_comma = if let TokenTree::Token(Token::Comma) = tree { true } else { false };
            let on_last_token = if i == last { true } else { false };
            let val_end = token_is_comma || on_last_token;
            if !token_is_comma {
                val.push(tree);
            }
            if val_end {
                let arg = self.group_by_comma(val.clone());
                let len = arg.len();
                if len == 0 {
                    panic!("An object key cannot have no value.");
                } else if len == 1 {
                    hash.insert(key.clone(), arg.into_iter().next().unwrap());
                } else {
                    panic!("An object key cannot have more than 1 values.");
                }
                is_key = true;
                val = Vec::new();
            }
        }

        hash
    }

    fn group_by_comma(&self, tt: Vec<TokenTree>) -> Vec<Group> {
        let mut args = Vec::new();
        let mut tokens = Vec::new();
        let mut typ = Type::Expr;
        let last = tt.len()-1;
        let mut found_lbar = false;
        let mut found_rbar = false;
        let mut found_body = false;

        for (i, tree) in tt.into_iter().enumerate() {
            let token_is_comma = if let TokenTree::Token(Token::Comma) = tree { true } else { false };
            let on_last_token = if i == last { true } else { false };
            let arg_end = token_is_comma || on_last_token;
            let mut token_is_bar = false;

            match Type::new(&tokens, &tree, &mut typ, &mut token_is_bar, &mut found_lbar, &mut found_rbar) {
                Type::Expr => {
                    if !token_is_comma {
                        tokens.push(tree);
                    }
                    if arg_end {
                        self.finalise_arg(&mut args, &mut tokens, &mut typ, &mut found_lbar, &mut found_rbar, &mut found_body);
                    }
                }
                Type::Closure => {
                    if !token_is_comma && !token_is_bar {
                        tokens.push(tree);
                        if found_rbar {
                            found_body = true;
                        }
                    }
                    if on_last_token || found_body {
                        self.finalise_arg(&mut args, &mut tokens, &mut typ, &mut found_lbar, &mut found_rbar, &mut found_body);
                    }
                }
                Type::List => {
                    if !token_is_comma {
                        if let TokenTree::Delimited(d) = tree {
                            tokens = d.tts;
                        }
                    }
                    if arg_end {
                        self.finalise_arg(&mut args, &mut tokens, &mut typ, &mut found_lbar, &mut found_rbar, &mut found_body);
                    }
                }
                Type::Object => {
                    if !token_is_comma {
                        if let TokenTree::Delimited(d) = tree {
                            tokens = d.tts;
                        }
                    }
                    if arg_end {
                        self.finalise_arg(&mut args, &mut tokens, &mut typ, &mut found_lbar, &mut found_rbar, &mut found_body);
                    }
                }
            }
        }
        args
    }
}

impl Group {
    fn tokenise(self, var: &str, last: bool) -> Tokens {
        let mut tokens = Tokens::new();
        let var = Ident::from(var);
        match self {
            Group::Expr(tt) => {
                let mut expr = Tokens::new();
                for token in tt {
                    token.to_tokens(&mut expr);
                }
                quote!(#var.add_arg(#expr.into_arg());)
                    .to_tokens(&mut tokens);
            }
            Group::Closure(tt) => {
                let mut func = Tokens::new();
                let mut args = Tokens::new();
                let mut found_lbar = false;
                let mut found_rbar = false;
                for token in tt {
                    token.to_tokens(&mut func);
                    if found_rbar {
                        continue;
                    }
                    if let TokenTree::Token(Token::BinOp(BinOpToken::Or)) = token {
                        if found_lbar {
                            found_rbar = true;
                            continue;
                        } else {
                            found_lbar = true;
                            continue;
                        }
                    }
                    else if let TokenTree::Token(Token::OrOr) = token {
                        found_lbar = true;
                        found_rbar = true;
                        continue;
                    }
                    if found_lbar && !found_rbar {
                        quote!(var!(),)
                            .to_tokens(&mut args);
                    }
                }
                let closure = quote! {
                    let func = func!((#func), #args);
                    #var.add_arg(func.into_arg());
                };
                closure.to_tokens(&mut tokens);
            }
            Group::List(tt) => {
                let mut list = quote!(let mut list_arg = Arg::new(););
                for group in tt {
                    quote!(let mut list_val = Arg::new();)
                        .to_tokens(&mut list);
                    group.tokenise("list_val", false)
                        .to_tokens(&mut list);
                    quote!(list_arg.add_arg(list_val);)
                        .to_tokens(&mut list);
                }
                quote!(#var.add_arg(list_arg);)
                        .to_tokens(&mut list);
                list.to_tokens(&mut tokens);
            }
            Group::Object(tt) => {
                let mut obj = Tokens::new();
                if !last {
                    quote!(let mut obj_arg = Arg::new();)
                        .to_tokens(&mut obj);
                }
                for (key, group) in tt {
                    quote!(let mut obj_val = Arg::new();)
                        .to_tokens(&mut obj);
                    group.tokenise("obj_val", false)
                        .to_tokens(&mut obj);
                    if last {
                        quote!(
                            match Arg::create_term_pair(#key, obj_val) {
                                Ok(temp_pair) => #var.add_opt(temp_pair),
                                Err(error) => #var.set_term(Err(error)),
                            })
                        .to_tokens(&mut obj);
                    } else {
                        quote!(
                            match Arg::create_term_pair(#key, obj_val) {
                                Ok(temp_pair) => obj_arg.add_opt(temp_pair),
                                Err(error) => obj_arg.set_term(Err(error)),
                            })
                        .to_tokens(&mut obj);
                    }
                }
                if !last {
                    quote!(#var.add_arg(obj_arg);)
                        .to_tokens(&mut obj);
                }
                obj.to_tokens(&mut tokens);
            }
        }
        tokens
    }
}

impl Type {
    fn new(tokens: &Vec<TokenTree>, tree: &TokenTree, typ: &mut Type, token_is_bar: &mut bool, found_lbar: &mut bool, found_rbar: &mut bool) -> Type {
        if tokens.is_empty() {
            if let TokenTree::Delimited(ref d) = *tree {
                if d.delim == DelimToken::Bracket { *typ = Type::List; }
                else if d.delim == DelimToken::Brace { *typ = Type::Object; }
            }
            else if let TokenTree::Token(Token::BinOp(BinOpToken::Or)) = *tree {
                *typ = Type::Closure;
                *token_is_bar = true;
                if *found_lbar {
                    *found_rbar = true;
                } else {
                    *found_lbar = true;
                }
            }
            else if let TokenTree::Token(Token::OrOr) = *tree {
                *typ = Type::Closure;
                *token_is_bar = true;
                *found_lbar = true;
                *found_rbar = true;
            }
        } else if tokens.len() == 1 {
            if let TokenTree::Token(Token::Ident(ref i)) = tokens[0] {
                if i == "move" {
                    if let TokenTree::Token(Token::BinOp(BinOpToken::Or)) = *tree {
                        *typ = Type::Closure;
                        *token_is_bar = true;
                        if *found_lbar {
                            *found_rbar = true;
                        } else {
                            *found_lbar = true;
                        }
                    }
                    else if let TokenTree::Token(Token::OrOr) = *tree {
                        *typ = Type::Closure;
                        *token_is_bar = true;
                        *found_lbar = true;
                        *found_rbar = true;
                    }
                }
            }
        }
        *typ
    }
}
<commit_msg>Annotate closure types<commit_after>use syn::{self, Token, TokenTree, Ident, BinOpToken, DelimToken};
use quote::{Tokens, ToTokens};
use std::collections::HashMap;

#[derive(Debug)]
pub struct Args<'a> {
    input: &'a str,
    pub tokens: Tokens,
}

#[derive(Debug, Clone, Copy, PartialEq)]
enum Type {
    Expr,
    Closure,
    List,
    Object,
}

#[derive(Debug, Clone)]
enum Group {
    Expr(Vec<TokenTree>),
    Closure(Vec<TokenTree>),
    List(Vec<Group>),
    Object(HashMap<String, Group>),
}

impl<'a> Args<'a> {
    pub fn new(input: &str) -> Args {
        Args {
            input: input.trim(),
            tokens: Tokens::new(),
        }
    }

    pub fn process(&mut self) -> &mut Self {
        if self.input.is_empty() {
            self.tokens = quote!(reql::Term::new());
            return self;
        }

        let args = format!("args!({})", self.input);
        let body = self.body();

        self.tokens = quote!({
            #[allow(unused_imports)]
            use reql::{IntoArg, Arg, Term};

            let mut args = Arg::new();
            args.set_string(#args);
            #body
            args
        });

        self
    }

    fn body(&mut self) -> Tokens {
        let tt = syn::parse_token_trees(self.input).expect("failed to parse token tree");
        let mut tokens = Tokens::new();
        let args = self.group_by_comma(tt);
        let last = args.len()-1;
        for (i, arg) in args.into_iter().enumerate() {
            arg.tokenise("args", i == last).to_tokens(&mut tokens);
        }
        tokens
    }

    fn finalise_arg(&self, args: &mut Vec<Group>, tokens: &mut Vec<TokenTree>, typ: &mut Type, found_lbar: &mut bool, found_rbar: &mut bool, found_body: &mut bool) {
        let group = match *typ {
            Type::Expr => Group::Expr(tokens.clone()),
            Type::Closure => Group::Closure(tokens.clone()),
            Type::List => Group::List(self.group_by_comma(tokens.clone())),
            Type::Object => Group::Object(self.hash_from_tokens(tokens.clone())),
        };
        args.push(group);
        *tokens = Vec::new();
        *typ = Type::Expr;
        *found_lbar = false;
        *found_rbar = false;
        *found_body = false;
    }

    fn hash_from_tokens(&self, tt: Vec<TokenTree>) -> HashMap<String, Group> {
        let mut hash = HashMap::new();

        let mut is_key = true;
        let mut key = String::new();
        let mut val = Vec::new();
        let last = tt.len()-1;

        for (i, tree) in tt.into_iter().enumerate() {
            if let TokenTree::Token(Token::Colon) = tree {
                if key.is_empty() {
                    panic!("A key of an object cannot be empty.");
                }
                is_key = false;
                continue;
            }
            if is_key {
                key = match tree {
                    TokenTree::Token(Token::Literal(syn::Lit::Str(key, _))) => key,
                    tree => {
                        let mut tokens = Tokens::new();
                        tree.to_tokens(&mut tokens);
                        tokens.to_string()
                    }
                };
                continue;
            }
            let token_is_comma = if let TokenTree::Token(Token::Comma) = tree { true } else { false };
            let on_last_token = if i == last { true } else { false };
            let val_end = token_is_comma || on_last_token;
            if !token_is_comma {
                val.push(tree);
            }
            if val_end {
                let arg = self.group_by_comma(val.clone());
                let len = arg.len();
                if len == 0 {
                    panic!("An object key cannot have no value.");
                } else if len == 1 {
                    hash.insert(key.clone(), arg.into_iter().next().unwrap());
                } else {
                    panic!("An object key cannot have more than 1 values.");
                }
                is_key = true;
                val = Vec::new();
            }
        }

        hash
    }

    fn group_by_comma(&self, tt: Vec<TokenTree>) -> Vec<Group> {
        let mut args = Vec::new();
        let mut tokens = Vec::new();
        let mut typ = Type::Expr;
        let last = tt.len()-1;
        let mut found_lbar = false;
        let mut found_rbar = false;
        let mut found_body = false;

        for (i, tree) in tt.into_iter().enumerate() {
            let token_is_comma = if let TokenTree::Token(Token::Comma) = tree { true } else { false };
            let on_last_token = if i == last { true } else { false };
            let arg_end = token_is_comma || on_last_token;

            match Type::new(&tokens, &tree, &mut typ, &mut found_lbar, &mut found_rbar, &mut found_body) {
                Type::Expr => {
                    if !token_is_comma {
                        tokens.push(tree);
                    }
                    if arg_end {
                        self.finalise_arg(&mut args, &mut tokens, &mut typ, &mut found_lbar, &mut found_rbar, &mut found_body);
                    }
                }
                Type::Closure => {
                    if !token_is_comma {
                        tokens.push(tree);
                    }
                    if on_last_token || (found_body && token_is_comma) {
                        self.finalise_arg(&mut args, &mut tokens, &mut typ, &mut found_lbar, &mut found_rbar, &mut found_body);
                    }
                }
                Type::List => {
                    if !token_is_comma {
                        if let TokenTree::Delimited(d) = tree {
                            tokens = d.tts;
                        }
                    }
                    if arg_end {
                        self.finalise_arg(&mut args, &mut tokens, &mut typ, &mut found_lbar, &mut found_rbar, &mut found_body);
                    }
                }
                Type::Object => {
                    if !token_is_comma {
                        if let TokenTree::Delimited(d) = tree {
                            tokens = d.tts;
                        }
                    }
                    if arg_end {
                        self.finalise_arg(&mut args, &mut tokens, &mut typ, &mut found_lbar, &mut found_rbar, &mut found_body);
                    }
                }
            }
        }
        args
    }
}

impl Group {
    fn tokenise(self, var: &str, last: bool) -> Tokens {
        let mut tokens = Tokens::new();
        let var = Ident::from(var);
        match self {
            Group::Expr(tt) => {
                let mut expr = Tokens::new();
                for token in tt {
                    token.to_tokens(&mut expr);
                }
                quote!(#var.add_arg(#expr.into_arg());)
                    .to_tokens(&mut tokens);
            }
            Group::Closure(tt) => {
                let mut func = Tokens::new();
                let mut args = Tokens::new();
                let mut found_lbar = false;
                let mut found_rbar = false;
                for token in tt {
                    token.to_tokens(&mut func);
                    if found_rbar {
                        continue;
                    }
                    if let TokenTree::Token(Token::BinOp(BinOpToken::Or)) = token {
                        if found_lbar {
                            found_rbar = true;
                            continue;
                        } else {
                            found_lbar = true;
                            continue;
                        }
                    }
                    else if let TokenTree::Token(Token::OrOr) = token {
                        found_lbar = true;
                        found_rbar = true;
                        continue;
                    }
                    if found_lbar {
                        quote!(: Client,)
                            .to_tokens(&mut func);
                        quote!(var!(),)
                            .to_tokens(&mut args);
                    }
                }
                let closure = quote! {
                    let func = func!((#func), #args);
                    #var.add_arg(func.into_arg());
                };
                closure.to_tokens(&mut tokens);
            }
            Group::List(tt) => {
                let mut list = quote!(let mut list_arg = Arg::new(););
                for group in tt {
                    quote!(let mut list_val = Arg::new();)
                        .to_tokens(&mut list);
                    group.tokenise("list_val", false)
                        .to_tokens(&mut list);
                    quote!(list_arg.add_arg(list_val);)
                        .to_tokens(&mut list);
                }
                quote!(#var.add_arg(list_arg);)
                        .to_tokens(&mut list);
                list.to_tokens(&mut tokens);
            }
            Group::Object(tt) => {
                let mut obj = Tokens::new();
                if !last {
                    quote!(let mut obj_arg = Arg::new();)
                        .to_tokens(&mut obj);
                }
                for (key, group) in tt {
                    quote!(let mut obj_val = Arg::new();)
                        .to_tokens(&mut obj);
                    group.tokenise("obj_val", false)
                        .to_tokens(&mut obj);
                    if last {
                        quote!(
                            match Arg::create_term_pair(#key, obj_val) {
                                Ok(temp_pair) => #var.add_opt(temp_pair),
                                Err(error) => #var.set_term(Err(error)),
                            })
                        .to_tokens(&mut obj);
                    } else {
                        quote!(
                            match Arg::create_term_pair(#key, obj_val) {
                                Ok(temp_pair) => obj_arg.add_opt(temp_pair),
                                Err(error) => obj_arg.set_term(Err(error)),
                            })
                        .to_tokens(&mut obj);
                    }
                }
                if !last {
                    quote!(#var.add_arg(obj_arg);)
                        .to_tokens(&mut obj);
                }
                obj.to_tokens(&mut tokens);
            }
        }
        tokens
    }
}

impl Type {
    fn new(tokens: &Vec<TokenTree>, tree: &TokenTree, typ: &mut Type, found_lbar: &mut bool, found_rbar: &mut bool, found_body: &mut bool) -> Type {
        if *typ == Type::Closure {
            if *found_rbar {
                *found_body = true;
            }
            else if let TokenTree::Token(Token::BinOp(BinOpToken::Or)) = *tree {
                if *found_lbar {
                    *found_rbar = true;
                } else {
                    *found_lbar = true;
                }
            }
            return *typ;
        }
        if tokens.is_empty() {
            if let TokenTree::Delimited(ref d) = *tree {
                if d.delim == DelimToken::Bracket { *typ = Type::List; }
                else if d.delim == DelimToken::Brace { *typ = Type::Object; }
            }
            else if let TokenTree::Token(Token::BinOp(BinOpToken::Or)) = *tree {
                *typ = Type::Closure;
                if *found_lbar {
                    *found_rbar = true;
                } else {
                    *found_lbar = true;
                }
            }
            else if let TokenTree::Token(Token::OrOr) = *tree {
                *typ = Type::Closure;
                *found_lbar = true;
                *found_rbar = true;
            }
        } else if tokens.len() == 1 {
            if let TokenTree::Token(Token::Ident(ref i)) = tokens[0] {
                if i == "move" {
                    if let TokenTree::Token(Token::BinOp(BinOpToken::Or)) = *tree {
                        *typ = Type::Closure;
                        if *found_lbar {
                            *found_rbar = true;
                        } else {
                            *found_lbar = true;
                        }
                    }
                    else if let TokenTree::Token(Token::OrOr) = *tree {
                        *typ = Type::Closure;
                        *found_lbar = true;
                        *found_rbar = true;
                    }
                }
            }
        }
        *typ
    }
}
<|endoftext|>


<commit_before>use cairo;

use librsvg::{
    CairoRenderer, DefsLookupErrorKind, HrefError, IntrinsicDimensions, Length, LengthUnit,
    RenderingError,
};

mod utils;

use self::utils::load_svg;

#[test]
fn no_intrinsic_dimensions() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg"/>
"#,
    );

    assert_eq!(
        CairoRenderer::new(&svg).intrinsic_dimensions(),
        IntrinsicDimensions {
            width: None,
            height: None,
            vbox: None,
        }
    );
}

#[test]
fn has_intrinsic_dimensions() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="10cm" height="20" viewBox="0 0 100 200"/>
"#,
    );

    assert_eq!(
        CairoRenderer::new(&svg).intrinsic_dimensions(),
        IntrinsicDimensions {
            width: Some(Length::new(10.0, LengthUnit::Cm)),
            height: Some(Length::new(20.0, LengthUnit::Px)),
            vbox: Some(cairo::Rectangle {
                x: 0.0,
                y: 0.0,
                width: 100.0,
                height: 200.0,
            }),
        }
    );
}

#[test]
fn root_geometry_with_percent_viewport() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
  <rect x="10" y="20" width="30" height="40"/>
</svg>
"#,
    );

    let renderer = CairoRenderer::new(&svg);

    let viewport = cairo::Rectangle {
        x: 0.0,
        y: 0.0,
        width: 100.0,
        height: 100.0,
    };

    let (ink_r, logical_r) = renderer.geometry_for_layer(None, &viewport).unwrap();

    let rect = cairo::Rectangle {
        x: 10.0,
        y: 20.0,
        width: 30.0,
        height: 40.0,
    };

    assert_eq!((ink_r, logical_r), (rect, rect));
}

#[test]
fn layer_geometry_with_percent_viewport() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
  <rect id="foo" x="10" y="20" width="30" height="40"/>
</svg>
"#,
    );

    let renderer = CairoRenderer::new(&svg);

    let viewport = cairo::Rectangle {
        x: 0.0,
        y: 0.0,
        width: 100.0,
        height: 100.0,
    };

    let (ink_r, logical_r) = renderer
        .geometry_for_layer(Some("#foo"), &viewport)
        .unwrap();

    let rect = cairo::Rectangle {
        x: 10.0,
        y: 20.0,
        width: 30.0,
        height: 40.0,
    };

    assert_eq!((ink_r, logical_r), (rect, rect));
}

#[test]
fn layer_geometry_viewport_viewbox() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="400" viewBox="0 0 100 400">
  <rect id="one" x="0" y="0" width="100" height="200" fill="rgb(0,255,0)"/>
  <rect id="two" x="0" y="200" width="100" height="200" fill="rgb(0,0,255)"/>
</svg>
"#,
    );

    let renderer = CairoRenderer::new(&svg);

    let viewport = cairo::Rectangle {
        x: 0.0,
        y: 0.0,
        width: 100.0,
        height: 400.0,
    };

    let (ink_r, logical_r) = renderer
        .geometry_for_layer(Some("#two"), &viewport)
        .unwrap();

    let rect = cairo::Rectangle {
        x: 0.0,
        y: 200.0,
        width: 100.0,
        height: 200.0,
    };

    assert_eq!((ink_r, logical_r), (rect, rect));
}

#[test]
fn layer_geometry_for_nonexistent_element() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"/>
"#,
    );

    let viewport = cairo::Rectangle {
        x: 0.0,
        y: 0.0,
        width: 100.0,
        height: 100.0,
    };

    let renderer = CairoRenderer::new(&svg);

    match renderer.geometry_for_layer(Some("#foo"), &viewport) {
        Err(RenderingError::InvalidId(DefsLookupErrorKind::NotFound)) => (),
        _ => panic!(),
    }
}

#[test]
fn layer_geometry_for_invalid_id() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"/>
"#,
    );

    let viewport = cairo::Rectangle {
        x: 0.0,
        y: 0.0,
        width: 100.0,
        height: 100.0,
    };

    let renderer = CairoRenderer::new(&svg);
    match renderer.geometry_for_layer(Some("foo"), &viewport) {
        Err(RenderingError::InvalidId(DefsLookupErrorKind::CannotLookupExternalReferences)) => (),
        _ => panic!(),
    }

    match renderer.geometry_for_layer(Some("foo.svg#foo"), &viewport) {
        Err(RenderingError::InvalidId(DefsLookupErrorKind::CannotLookupExternalReferences)) => (),
        _ => panic!(),
    }

    match renderer.geometry_for_layer(Some(""), &viewport) {
        Err(RenderingError::InvalidId(DefsLookupErrorKind::HrefError(HrefError::ParseError))) => (),
        _ => panic!(),
    }
}
<commit_msg>WIP: Add a failing test for obtaining geometries with an offsetted viewport<commit_after>use cairo;

use librsvg::{
    CairoRenderer, DefsLookupErrorKind, HrefError, IntrinsicDimensions, Length, LengthUnit,
    RenderingError,
};

mod utils;

use self::utils::load_svg;

#[test]
fn no_intrinsic_dimensions() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg"/>
"#,
    );

    assert_eq!(
        CairoRenderer::new(&svg).intrinsic_dimensions(),
        IntrinsicDimensions {
            width: None,
            height: None,
            vbox: None,
        }
    );
}

#[test]
fn has_intrinsic_dimensions() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="10cm" height="20" viewBox="0 0 100 200"/>
"#,
    );

    assert_eq!(
        CairoRenderer::new(&svg).intrinsic_dimensions(),
        IntrinsicDimensions {
            width: Some(Length::new(10.0, LengthUnit::Cm)),
            height: Some(Length::new(20.0, LengthUnit::Px)),
            vbox: Some(cairo::Rectangle {
                x: 0.0,
                y: 0.0,
                width: 100.0,
                height: 200.0,
            }),
        }
    );
}

#[test]
fn root_geometry_with_percent_viewport() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
  <rect x="10" y="20" width="30" height="40"/>
</svg>
"#,
    );

    let renderer = CairoRenderer::new(&svg);

    let viewport = cairo::Rectangle {
        x: 0.0,
        y: 0.0,
        width: 100.0,
        height: 100.0,
    };

    let (ink_r, logical_r) = renderer.geometry_for_layer(None, &viewport).unwrap();

    let rect = cairo::Rectangle {
        x: 10.0,
        y: 20.0,
        width: 30.0,
        height: 40.0,
    };

    assert_eq!((ink_r, logical_r), (rect, rect));
}

#[test]
fn root_geometry_with_percent_viewport_with_offset() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
  <rect x="10" y="20" width="30" height="40"/>
</svg>
"#,
    );

    let renderer = CairoRenderer::new(&svg);

    let viewport = cairo::Rectangle {
        x: 100.0,
        y: 100.0,
        width: 100.0,
        height: 100.0,
    };

    let (ink_r, logical_r) = renderer.geometry_for_layer(None, &viewport).unwrap();

    let rect = cairo::Rectangle {
        x: 110.0,
        y: 120.0,
        width: 30.0,
        height: 40.0,
    };

    assert_eq!((ink_r, logical_r), (rect, rect));
}

#[test]
fn layer_geometry_with_percent_viewport() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
  <rect id="foo" x="10" y="20" width="30" height="40"/>
</svg>
"#,
    );

    let renderer = CairoRenderer::new(&svg);

    let viewport = cairo::Rectangle {
        x: 0.0,
        y: 0.0,
        width: 100.0,
        height: 100.0,
    };

    let (ink_r, logical_r) = renderer
        .geometry_for_layer(Some("#foo"), &viewport)
        .unwrap();

    let rect = cairo::Rectangle {
        x: 10.0,
        y: 20.0,
        width: 30.0,
        height: 40.0,
    };

    assert_eq!((ink_r, logical_r), (rect, rect));
}

#[test]
fn layer_geometry_viewport_viewbox() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="400" viewBox="0 0 100 400">
  <rect id="one" x="0" y="0" width="100" height="200" fill="rgb(0,255,0)"/>
  <rect id="two" x="0" y="200" width="100" height="200" fill="rgb(0,0,255)"/>
</svg>
"#,
    );

    let renderer = CairoRenderer::new(&svg);

    let viewport = cairo::Rectangle {
        x: 0.0,
        y: 0.0,
        width: 100.0,
        height: 400.0,
    };

    let (ink_r, logical_r) = renderer
        .geometry_for_layer(Some("#two"), &viewport)
        .unwrap();

    let rect = cairo::Rectangle {
        x: 0.0,
        y: 200.0,
        width: 100.0,
        height: 200.0,
    };

    assert_eq!((ink_r, logical_r), (rect, rect));
}

#[test]
fn layer_geometry_for_nonexistent_element() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"/>
"#,
    );

    let viewport = cairo::Rectangle {
        x: 0.0,
        y: 0.0,
        width: 100.0,
        height: 100.0,
    };

    let renderer = CairoRenderer::new(&svg);

    match renderer.geometry_for_layer(Some("#foo"), &viewport) {
        Err(RenderingError::InvalidId(DefsLookupErrorKind::NotFound)) => (),
        _ => panic!(),
    }
}

#[test]
fn layer_geometry_for_invalid_id() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"/>
"#,
    );

    let viewport = cairo::Rectangle {
        x: 0.0,
        y: 0.0,
        width: 100.0,
        height: 100.0,
    };

    let renderer = CairoRenderer::new(&svg);
    match renderer.geometry_for_layer(Some("foo"), &viewport) {
        Err(RenderingError::InvalidId(DefsLookupErrorKind::CannotLookupExternalReferences)) => (),
        _ => panic!(),
    }

    match renderer.geometry_for_layer(Some("foo.svg#foo"), &viewport) {
        Err(RenderingError::InvalidId(DefsLookupErrorKind::CannotLookupExternalReferences)) => (),
        _ => panic!(),
    }

    match renderer.geometry_for_layer(Some(""), &viewport) {
        Err(RenderingError::InvalidId(DefsLookupErrorKind::HrefError(HrefError::ParseError))) => (),
        _ => panic!(),
    }
}
<|endoftext|>


<commit_before>use std::{error, fmt, io};

#[derive(Debug)]
pub enum Error {
    DbConnect(diesel::result::ConnectionError),
    DbQuery(diesel::result::Error),
    DotEnv(dotenv::Error),
    Http(http::Error),
    Internal(String),
    Io(io::Error),
    JobEnqueue(swirl::EnqueueError),
    Reqwest(reqwest::Error),
}

impl error::Error for Error {}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Error::DbConnect(inner) => inner.fmt(f),
            Error::DbQuery(inner) => inner.fmt(f),
            Error::DotEnv(inner) => inner.fmt(f),
            Error::Http(inner) => inner.fmt(f),
            Error::Internal(inner) => inner.fmt(f),
            Error::Io(inner) => inner.fmt(f),
            Error::JobEnqueue(inner) => inner.fmt(f),
            Error::Openssl(inner) => inner.fmt(f),
            Error::Reqwest(inner) => inner.fmt(f),
        }
    }
}

impl From<diesel::result::ConnectionError> for Error {
    fn from(err: diesel::result::ConnectionError) -> Self {
        Error::DbConnect(err)
    }
}

impl From<diesel::result::Error> for Error {
    fn from(err: diesel::result::Error) -> Self {
        Error::DbQuery(err)
    }
}

impl From<dotenv::Error> for Error {
    fn from(err: dotenv::Error) -> Self {
        Error::DotEnv(err)
    }
}

impl From<http::Error> for Error {
    fn from(err: http::Error) -> Self {
        Error::Http(err)
    }
}

impl From<String> for Error {
    fn from(err: String) -> Self {
        Error::Internal(err)
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Self {
        Error::Io(err)
    }
}

impl From<swirl::EnqueueError> for Error {
    fn from(err: swirl::EnqueueError) -> Self {
        Error::JobEnqueue(err)
    }
}

impl From<s3::Error> for Error {
    fn from(err: s3::Error) -> Self {
        match err {
            s3::Error::Openssl(e) => Error::Openssl(e),
            s3::Error::Reqwest(e) => Error::Reqwest(e),
        }
    }
}

impl From<reqwest::Error> for Error {
    fn from(err: reqwest::Error) -> Self {
        Error::Reqwest(err)
    }
}
<commit_msg>fix error impls<commit_after>use std::{error, fmt, io};

#[derive(Debug)]
pub enum Error {
    DbConnect(diesel::result::ConnectionError),
    DbQuery(diesel::result::Error),
    DotEnv(dotenv::Error),
    Http(http::Error),
    Internal(String),
    Io(io::Error),
    JobEnqueue(swirl::EnqueueError),
    Reqwest(reqwest::Error),
}

impl error::Error for Error {}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Error::DbConnect(inner) => inner.fmt(f),
            Error::DbQuery(inner) => inner.fmt(f),
            Error::DotEnv(inner) => inner.fmt(f),
            Error::Http(inner) => inner.fmt(f),
            Error::Internal(inner) => inner.fmt(f),
            Error::Io(inner) => inner.fmt(f),
            Error::JobEnqueue(inner) => inner.fmt(f),
            Error::Reqwest(inner) => inner.fmt(f),
        }
    }
}

impl From<diesel::result::ConnectionError> for Error {
    fn from(err: diesel::result::ConnectionError) -> Self {
        Error::DbConnect(err)
    }
}

impl From<diesel::result::Error> for Error {
    fn from(err: diesel::result::Error) -> Self {
        Error::DbQuery(err)
    }
}

impl From<dotenv::Error> for Error {
    fn from(err: dotenv::Error) -> Self {
        Error::DotEnv(err)
    }
}

impl From<http::Error> for Error {
    fn from(err: http::Error) -> Self {
        Error::Http(err)
    }
}

impl From<String> for Error {
    fn from(err: String) -> Self {
        Error::Internal(err)
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Self {
        Error::Io(err)
    }
}

impl From<swirl::EnqueueError> for Error {
    fn from(err: swirl::EnqueueError) -> Self {
        Error::JobEnqueue(err)
    }
}

impl From<reqwest::Error> for Error {
    fn from(err: reqwest::Error) -> Self {
        Error::Reqwest(err)
    }
}
<|endoftext|>


<commit_before>

use With;
use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;
use utils::prefix;

/// Generates rows of text in constrained width.
///
/// Given a long text and a width constraint, it iterates over
/// substrings of the text, each within the constraint.
pub struct LinesIterator<'a> {
    /// Content to iterate on.
    content: &'a str,
    /// Current offset in the content.
    offset: usize,
    /// Available width. Don't output lines wider than that.
    width: usize,

    /// If `true`, keep a blank cell at the end of lines
    /// when a whitespace or newline should be.
    show_spaces: bool,
}

impl<'a> LinesIterator<'a> {
    /// Returns a new `LinesIterator` on `content`.
    ///
    /// Yields rows of `width` cells or less.
    pub fn new(content: &'a str, width: usize) -> Self {
        LinesIterator {
            content: content,
            width: width,
            offset: 0,
            show_spaces: false,
        }
    }

    /// Leave a blank cell at the end of lines.
    ///
    /// Unless a word had to be truncated, in which case
    /// it takes the entire width.
    pub fn show_spaces(mut self) -> Self {
        self.show_spaces = true;
        self
    }
}

/// Represents a row of text within a `String`.
///
/// A row is made of offsets into a parent `String`.
/// The corresponding substring should take `width` cells when printed.
#[derive(Debug, Clone, Copy)]
pub struct Row {
    /// Beginning of the row in the parent `String`.
    pub start: usize,
    /// End of the row (excluded)
    pub end: usize,
    /// Width of the row, in cells.
    pub width: usize,
}

impl Row {
    /// Shift a row start and end by `offset`.
    pub fn shift(&mut self, offset: usize) {
        self.start += offset;
        self.end += offset;
    }

    /// Shift a row start and end by `offset`.
    ///
    /// Chainable variant;
    pub fn shifted(self, offset: usize) -> Self {
        self.with(|s| s.shift(offset))
    }

    /// Shift back a row start and end by `offset`.
    pub fn rev_shift(&mut self, offset: usize) {
        self.start -= offset;
        self.end -= offset;
    }
}

impl<'a> Iterator for LinesIterator<'a> {
    type Item = Row;

    fn next(&mut self) -> Option<Row> {
        if self.offset >= self.content.len() {
            // This is the end.
            return None;
        }

        // We start at the current offset.
        let start = self.offset;
        let content = &self.content[start..];

        // Find the ideal line, in an infinitely wide world.
        // We'll make a line larger than that.
        let next = content.find('\n').unwrap_or_else(|| content.len());
        let content = &content[..next];

        let allowed_width = if self.show_spaces {
            self.width - 1
        } else {
            self.width
        };

        let line_width = content.width();
        if line_width <= allowed_width {
            // We found a newline before the allowed limit.
            // Break early.
            // Advance the cursor to after the newline.
            self.offset += next + 1;
            return Some(Row {
                start: start,
                end: start + next,
                width: line_width,
            });
        }

        // First attempt: only break on spaces.
        let prefix_length =
            match prefix(content.split(' '), allowed_width, " ").length {
                // If this fail, fallback: only break on graphemes.
                // There's no whitespace to skip there.
                // And don't reserve the white space anymore.
                0 => prefix(content.graphemes(true), self.width, "").length,
                other => {
                    // If it works, advance the cursor by 1
                    // to jump the whitespace.
                    // We don't want to add 1 to `prefix_length` though, it
                    // would include the whitespace in the row.
                    self.offset += 1;
                    other
                }
            };

        if prefix_length == 0 {
            // This mean we can't even get a single char?
            // Sucks. Let's bail.
            return None;
        }

        // Advance the offset to the end of the line.
        self.offset += prefix_length;

        Some(Row {
            start: start,
            end: start + prefix_length,
            width: self.width,
        })
    }
}

#[cfg(test)]
mod tests {

    #[test]
    fn test_layout() {}
}
<commit_msg>Protect LinesIterator from zero width<commit_after>

use With;
use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;
use utils::prefix;

/// Generates rows of text in constrained width.
///
/// Given a long text and a width constraint, it iterates over
/// substrings of the text, each within the constraint.
pub struct LinesIterator<'a> {
    /// Content to iterate on.
    content: &'a str,
    /// Current offset in the content.
    offset: usize,
    /// Available width. Don't output lines wider than that.
    width: usize,

    /// If `true`, keep a blank cell at the end of lines
    /// when a whitespace or newline should be.
    show_spaces: bool,
}

impl<'a> LinesIterator<'a> {
    /// Returns a new `LinesIterator` on `content`.
    ///
    /// Yields rows of `width` cells or less.
    pub fn new(content: &'a str, width: usize) -> Self {
        LinesIterator {
            content: content,
            width: width,
            offset: 0,
            show_spaces: false,
        }
    }

    /// Leave a blank cell at the end of lines.
    ///
    /// Unless a word had to be truncated, in which case
    /// it takes the entire width.
    pub fn show_spaces(mut self) -> Self {
        self.show_spaces = true;
        self
    }
}

/// Represents a row of text within a `String`.
///
/// A row is made of offsets into a parent `String`.
/// The corresponding substring should take `width` cells when printed.
#[derive(Debug, Clone, Copy)]
pub struct Row {
    /// Beginning of the row in the parent `String`.
    pub start: usize,
    /// End of the row (excluded)
    pub end: usize,
    /// Width of the row, in cells.
    pub width: usize,
}

impl Row {
    /// Shift a row start and end by `offset`.
    pub fn shift(&mut self, offset: usize) {
        self.start += offset;
        self.end += offset;
    }

    /// Shift a row start and end by `offset`.
    ///
    /// Chainable variant;
    pub fn shifted(self, offset: usize) -> Self {
        self.with(|s| s.shift(offset))
    }

    /// Shift back a row start and end by `offset`.
    pub fn rev_shift(&mut self, offset: usize) {
        self.start -= offset;
        self.end -= offset;
    }
}

impl<'a> Iterator for LinesIterator<'a> {
    type Item = Row;

    fn next(&mut self) -> Option<Row> {
        if self.offset >= self.content.len() {
            // This is the end.
            return None;
        }

        // We start at the current offset.
        let start = self.offset;
        let content = &self.content[start..];

        // Find the ideal line, in an infinitely wide world.
        // We'll make a line larger than that.
        let next = content.find('\n').unwrap_or_else(|| content.len());
        let content = &content[..next];

        let allowed_width = if self.show_spaces {
            if self.width < 1 {
                0
            } else {
                self.width - 1
            }
        } else {
            self.width
        };

        let line_width = content.width();
        if line_width <= allowed_width {
            // We found a newline before the allowed limit.
            // Break early.
            // Advance the cursor to after the newline.
            self.offset += next + 1;
            return Some(Row {
                start: start,
                end: start + next,
                width: line_width,
            });
        }

        // First attempt: only break on spaces.
        let prefix_length =
            match prefix(content.split(' '), allowed_width, " ").length {
                // If this fail, fallback: only break on graphemes.
                // There's no whitespace to skip there.
                // And don't reserve the white space anymore.
                0 => prefix(content.graphemes(true), self.width, "").length,
                other => {
                    // If it works, advance the cursor by 1
                    // to jump the whitespace.
                    // We don't want to add 1 to `prefix_length` though, it
                    // would include the whitespace in the row.
                    self.offset += 1;
                    other
                }
            };

        if prefix_length == 0 {
            // This mean we can't even get a single char?
            // Sucks. Let's bail.
            return None;
        }

        // Advance the offset to the end of the line.
        self.offset += prefix_length;

        Some(Row {
            start: start,
            end: start + prefix_length,
            width: self.width,
        })
    }
}

#[cfg(test)]
mod tests {

    #[test]
    fn test_layout() {}
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
// spell-checker:ignore (change!) each's
// spell-checker:ignore (ToDO) LONGHELP FORMATSTRING templating parameterizing formatstr

use clap::{crate_version, App, AppSettings, Arg};
use uucore::error::{UResult, UUsageError};
use uucore::memo;
use uucore::InvalidEncodingHandling;

const VERSION: &str = "version";
const HELP: &str = "help";
static LONGHELP_LEAD: &str = "printf

 USAGE: printf FORMATSTRING [ARGUMENT]...

 basic anonymous string templating:

 prints format string at least once, repeating as long as there are remaining arguments
 output prints escaped literals in the format string as character literals
 output replaces anonymous fields with the next unused argument, formatted according to the field.

Options:
    --help              display this help and exit
    --version           output version information and exit

";
static LONGHELP_BODY: &str = "
  Prints the , replacing escaped character sequences with character literals
    and substitution field sequences with passed arguments

 literally, with the exception of the below
    escaped character sequences, and the substitution sequences described further down.

  ESCAPE SEQUENCES

  The following escape sequences, organized here in alphabetical order,
  will print the corresponding character literal:

  \"   double quote

  \\\\    backslash

  \\a  alert (BEL)

  \\b  backspace

  \\c  End-of-Input

  \\e  escape

  \\f  form feed

  \\n  new line

  \\r  carriage return

  \\t  horizontal tab

  \\v  vertical tab

  \\NNN byte with value expressed in octal value NNN (1 to 3 digits)
        values greater than 256 will be treated

  \\xHH byte with value expressed in hexadecimal value NN (1 to 2 digits)

  \\uHHHH Unicode (IEC 10646) character with value expressed in hexadecimal value HHHH (4 digits)

  \\uHHHH Unicode character with value expressed in hexadecimal value HHHH (8 digits)

  %% a single %

  SUBSTITUTIONS

  SUBSTITUTION QUICK REFERENCE

  Fields

  %s - string
  %b - string parsed for literals
    second parameter is max length

  %c - char
    no second parameter

  %i or %d - 64-bit integer
  %u - 64 bit unsigned integer
  %x or %X - 64-bit unsigned integer as hex
  %o - 64-bit unsigned integer as octal
    second parameter is min-width, integer
     output below that width is padded with leading zeroes

  %f or %F - decimal floating point value
  %e or %E - scientific notation floating point value
  %g or %G - shorter of specially interpreted decimal or SciNote floating point value.
    second parameter is
      -max places after decimal point for floating point output
      -max number of significant digits for scientific notation output

  parameterizing fields

  examples:

  printf '%4.3i' 7
  has a first parameter of 4
     and a second parameter of 3
  will result in ' 007'

  printf '%.1s' abcde
  has no first parameter
     and a second parameter of 1
  will result in 'a'

  printf '%4c' q
  has a first parameter of 4
     and no second parameter
  will result in  '   q'

  The first parameter of a field is the minimum width to pad the output to
   if the output is less than this absolute value of this width,
   it will be padded with leading spaces, or, if the argument is negative,
   with trailing spaces. the default is zero.

  The second parameter of a field is particular to the output field type.
   defaults can be found in the full substitution help below

  special prefixes to numeric arguments
    0 (e.g. 010) - interpret argument as octal (integer output fields only)
    0x (e.g. 0xABC) - interpret argument as hex (numeric output fields only)
    \' (e.g. \'a) - interpret argument as a character constant

  HOW TO USE SUBSTITUTIONS

  Substitutions are used to pass additional argument(s) into the FORMAT string, to be formatted a
  particular way. E.g.

      printf 'the letter %X comes before the letter %X' 10 11

  will print

     'the letter A comes before the letter B'

  because the substitution field %X means
  'take an integer argument and write it as a hexadecimal number'

  Passing more arguments than are in the format string will cause the format string to be
   repeated for the remaining substitutions

     printf 'it is %i F in %s \n' 22 Portland 25 Boston 27 New York

  will print

     'it is 22 F in Portland
      it is 25 F in Boston
      it is 27 F in Boston
     '
  If a format string is printed but there are less arguments remaining
   than there are substitution fields, substitution fields without
   an argument will default to empty strings, or for numeric fields
   the value 0

  AVAILABLE SUBSTITUTIONS

  This program, like GNU coreutils printf,
  interprets a modified subset of the POSIX C printf spec,
  a quick reference to substitutions is below.

   STRING SUBSTITUTIONS
    All string fields have a 'max width' parameter
    %.3s means 'print no more than three characters of the original input'

   %s - string

   %b - escaped string - the string will be checked for any escaped literals from
         the escaped literal list above, and translate them to literal characters.
         e.g. \\n will be transformed into a newline character.

        One special rule about %b mode is that octal literals are interpreted differently
        In arguments passed by %b, pass octal-interpreted literals must be in the form of \\0NNN
        instead of \\NNN. (Although, for legacy reasons, octal literals in the form of \\NNN will
        still be interpreted and not throw a warning, you will have problems if you use this for a
        literal whose code begins with zero, as it will be viewed as in \\0NNN form.)

   CHAR SUBSTITUTIONS
    The character field does not have a secondary parameter.

   %c - a single character

   INTEGER SUBSTITUTIONS
    All integer fields have a 'pad with zero' parameter
    %.4i means an integer which if it is less than 4 digits in length,
    is padded with leading zeros until it is 4 digits in length.

   %d or %i - 64-bit integer

   %u - 64 bit unsigned integer

   %x or %X - 64 bit unsigned integer printed in Hexadecimal (base 16)
            %X instead of %x means to use uppercase letters for 'a' through 'f'

   %o - 64 bit unsigned integer printed in octal (base 8)

   FLOATING POINT SUBSTITUTIONS

    All floating point fields have a 'max decimal places / max significant digits' parameter
    %.10f means a decimal floating point with 7 decimal places past 0
    %.10e means a scientific notation number with 10 significant digits
    %.10g means the same behavior for decimal and Sci. Note, respectively, and provides the shorter
          of each's output.

    Like with GNU coreutils, the value after the decimal point is these outputs is parsed as a
    double first before being rendered to text. For both implementations do not expect meaningful
    precision past the 18th decimal place. When using a number of decimal places that is 18 or
    higher, you can expect variation in output between GNU coreutils printf and this printf at the
    18th decimal place of +/- 1

   %f - floating point value presented in decimal, truncated and displayed to 6 decimal places by
        default. There is not past-double behavior parity with Coreutils printf, values are not
        estimated or adjusted beyond input values.

   %e or %E - floating point value presented in scientific notation
            7 significant digits by default
            %E means use to use uppercase E for the mantissa.

   %g or %G - floating point value presented in the shorter of decimal and scientific notation
            behaves differently from %f and %E, please see posix printf spec for full details,
            some examples of different behavior:

            Sci Note has 6 significant digits by default
            Trailing zeroes are removed
            Instead of being truncated, digit after last is rounded

   Like other behavior in this utility, the design choices of floating point
    behavior in this utility is selected to reproduce in exact
    the behavior of GNU coreutils' printf from an inputs and outputs standpoint.

  USING PARAMETERS
   Most substitution fields can be parameterized using up to 2 numbers that can
   be passed to the field, between the % sign and the field letter.

   The 1st parameter always indicates the minimum width of output, it is useful for creating
     columnar output. Any output that would be less than this minimum width is padded with
     leading spaces
   The 2nd parameter is proceeded by a dot.
   You do not have to use parameters

  SPECIAL FORMS OF INPUT
   For numeric input, the following additional forms of input are accepted besides decimal:

    Octal (only with integer): if the argument begins with a 0 the proceeding characters
      will be interpreted as octal (base 8) for integer fields

    Hexadecimal: if the argument begins with 0x the proceeding characters will be interpreted
      will be interpreted as hex (base 16) for any numeric fields
      for float fields, hexadecimal input results in a precision
      limit (in converting input past the decimal point) of 10^-15

    Character Constant: if the argument begins with a single quote character, the first byte
      of the next character will be interpreted as an 8-bit unsigned integer. If there are
      additional bytes, they will throw an error (unless the environment variable POSIXLY_CORRECT
      is set)

WRITTEN BY :
  Nathan E. Ross, et al. for the uutils project

MORE INFO :
  https://github.com/uutils/coreutils

COPYRIGHT :
  Copyright 2015 uutils project.
  Licensed under the MIT License, please see LICENSE file for details

";

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let args = args
        .collect_str(InvalidEncodingHandling::Ignore)
        .accept_any();

    if args.len() <= 1 {
        return Err(UUsageError::new(1, "missing operand"));
    }
    let formatstr = &args[1];

    if formatstr == "--help" {
        print!("{} {}", LONGHELP_LEAD, LONGHELP_BODY);
    } else if formatstr == "--version" {
        println!("{} {}", uucore::util_name(), crate_version!());
    } else {
        let printf_args = &args[2..];
        memo::Memo::run_all(formatstr, printf_args);
    }
    Ok(())
}

pub fn uu_app<'a>() -> App<'a> {
    App::new(uucore::util_name())
        .arg(Arg::new(VERSION).long(VERSION))
        .arg(Arg::new(HELP).long(HELP))
        .setting(AppSettings::InferLongArgs)
}
<commit_msg>printf: add description and version<commit_after>#![allow(dead_code)]
// spell-checker:ignore (change!) each's
// spell-checker:ignore (ToDO) LONGHELP FORMATSTRING templating parameterizing formatstr

use clap::{crate_version, App, AppSettings, Arg};
use uucore::error::{UResult, UUsageError};
use uucore::memo;
use uucore::InvalidEncodingHandling;

const VERSION: &str = "version";
const HELP: &str = "help";
const USAGE: &str = "printf FORMATSTRING [ARGUMENT]...";
const ABOUT: &str = "Print output based off of the format string and proceeding arguments.";
static LONGHELP_LEAD: &str = "printf

 USAGE: printf FORMATSTRING [ARGUMENT]...

 basic anonymous string templating:

 prints format string at least once, repeating as long as there are remaining arguments
 output prints escaped literals in the format string as character literals
 output replaces anonymous fields with the next unused argument, formatted according to the field.

Options:
    --help              display this help and exit
    --version           output version information and exit

";
static LONGHELP_BODY: &str = "
  Prints the , replacing escaped character sequences with character literals
    and substitution field sequences with passed arguments

 literally, with the exception of the below
    escaped character sequences, and the substitution sequences described further down.

  ESCAPE SEQUENCES

  The following escape sequences, organized here in alphabetical order,
  will print the corresponding character literal:

  \"   double quote

  \\\\    backslash

  \\a  alert (BEL)

  \\b  backspace

  \\c  End-of-Input

  \\e  escape

  \\f  form feed

  \\n  new line

  \\r  carriage return

  \\t  horizontal tab

  \\v  vertical tab

  \\NNN byte with value expressed in octal value NNN (1 to 3 digits)
        values greater than 256 will be treated

  \\xHH byte with value expressed in hexadecimal value NN (1 to 2 digits)

  \\uHHHH Unicode (IEC 10646) character with value expressed in hexadecimal value HHHH (4 digits)

  \\uHHHH Unicode character with value expressed in hexadecimal value HHHH (8 digits)

  %% a single %

  SUBSTITUTIONS

  SUBSTITUTION QUICK REFERENCE

  Fields

  %s - string
  %b - string parsed for literals
    second parameter is max length

  %c - char
    no second parameter

  %i or %d - 64-bit integer
  %u - 64 bit unsigned integer
  %x or %X - 64-bit unsigned integer as hex
  %o - 64-bit unsigned integer as octal
    second parameter is min-width, integer
     output below that width is padded with leading zeroes

  %f or %F - decimal floating point value
  %e or %E - scientific notation floating point value
  %g or %G - shorter of specially interpreted decimal or SciNote floating point value.
    second parameter is
      -max places after decimal point for floating point output
      -max number of significant digits for scientific notation output

  parameterizing fields

  examples:

  printf '%4.3i' 7
  has a first parameter of 4
     and a second parameter of 3
  will result in ' 007'

  printf '%.1s' abcde
  has no first parameter
     and a second parameter of 1
  will result in 'a'

  printf '%4c' q
  has a first parameter of 4
     and no second parameter
  will result in  '   q'

  The first parameter of a field is the minimum width to pad the output to
   if the output is less than this absolute value of this width,
   it will be padded with leading spaces, or, if the argument is negative,
   with trailing spaces. the default is zero.

  The second parameter of a field is particular to the output field type.
   defaults can be found in the full substitution help below

  special prefixes to numeric arguments
    0 (e.g. 010) - interpret argument as octal (integer output fields only)
    0x (e.g. 0xABC) - interpret argument as hex (numeric output fields only)
    \' (e.g. \'a) - interpret argument as a character constant

  HOW TO USE SUBSTITUTIONS

  Substitutions are used to pass additional argument(s) into the FORMAT string, to be formatted a
  particular way. E.g.

      printf 'the letter %X comes before the letter %X' 10 11

  will print

     'the letter A comes before the letter B'

  because the substitution field %X means
  'take an integer argument and write it as a hexadecimal number'

  Passing more arguments than are in the format string will cause the format string to be
   repeated for the remaining substitutions

     printf 'it is %i F in %s \n' 22 Portland 25 Boston 27 New York

  will print

     'it is 22 F in Portland
      it is 25 F in Boston
      it is 27 F in Boston
     '
  If a format string is printed but there are less arguments remaining
   than there are substitution fields, substitution fields without
   an argument will default to empty strings, or for numeric fields
   the value 0

  AVAILABLE SUBSTITUTIONS

  This program, like GNU coreutils printf,
  interprets a modified subset of the POSIX C printf spec,
  a quick reference to substitutions is below.

   STRING SUBSTITUTIONS
    All string fields have a 'max width' parameter
    %.3s means 'print no more than three characters of the original input'

   %s - string

   %b - escaped string - the string will be checked for any escaped literals from
         the escaped literal list above, and translate them to literal characters.
         e.g. \\n will be transformed into a newline character.

        One special rule about %b mode is that octal literals are interpreted differently
        In arguments passed by %b, pass octal-interpreted literals must be in the form of \\0NNN
        instead of \\NNN. (Although, for legacy reasons, octal literals in the form of \\NNN will
        still be interpreted and not throw a warning, you will have problems if you use this for a
        literal whose code begins with zero, as it will be viewed as in \\0NNN form.)

   CHAR SUBSTITUTIONS
    The character field does not have a secondary parameter.

   %c - a single character

   INTEGER SUBSTITUTIONS
    All integer fields have a 'pad with zero' parameter
    %.4i means an integer which if it is less than 4 digits in length,
    is padded with leading zeros until it is 4 digits in length.

   %d or %i - 64-bit integer

   %u - 64 bit unsigned integer

   %x or %X - 64 bit unsigned integer printed in Hexadecimal (base 16)
            %X instead of %x means to use uppercase letters for 'a' through 'f'

   %o - 64 bit unsigned integer printed in octal (base 8)

   FLOATING POINT SUBSTITUTIONS

    All floating point fields have a 'max decimal places / max significant digits' parameter
    %.10f means a decimal floating point with 7 decimal places past 0
    %.10e means a scientific notation number with 10 significant digits
    %.10g means the same behavior for decimal and Sci. Note, respectively, and provides the shorter
          of each's output.

    Like with GNU coreutils, the value after the decimal point is these outputs is parsed as a
    double first before being rendered to text. For both implementations do not expect meaningful
    precision past the 18th decimal place. When using a number of decimal places that is 18 or
    higher, you can expect variation in output between GNU coreutils printf and this printf at the
    18th decimal place of +/- 1

   %f - floating point value presented in decimal, truncated and displayed to 6 decimal places by
        default. There is not past-double behavior parity with Coreutils printf, values are not
        estimated or adjusted beyond input values.

   %e or %E - floating point value presented in scientific notation
            7 significant digits by default
            %E means use to use uppercase E for the mantissa.

   %g or %G - floating point value presented in the shorter of decimal and scientific notation
            behaves differently from %f and %E, please see posix printf spec for full details,
            some examples of different behavior:

            Sci Note has 6 significant digits by default
            Trailing zeroes are removed
            Instead of being truncated, digit after last is rounded

   Like other behavior in this utility, the design choices of floating point
    behavior in this utility is selected to reproduce in exact
    the behavior of GNU coreutils' printf from an inputs and outputs standpoint.

  USING PARAMETERS
   Most substitution fields can be parameterized using up to 2 numbers that can
   be passed to the field, between the % sign and the field letter.

   The 1st parameter always indicates the minimum width of output, it is useful for creating
     columnar output. Any output that would be less than this minimum width is padded with
     leading spaces
   The 2nd parameter is proceeded by a dot.
   You do not have to use parameters

  SPECIAL FORMS OF INPUT
   For numeric input, the following additional forms of input are accepted besides decimal:

    Octal (only with integer): if the argument begins with a 0 the proceeding characters
      will be interpreted as octal (base 8) for integer fields

    Hexadecimal: if the argument begins with 0x the proceeding characters will be interpreted
      will be interpreted as hex (base 16) for any numeric fields
      for float fields, hexadecimal input results in a precision
      limit (in converting input past the decimal point) of 10^-15

    Character Constant: if the argument begins with a single quote character, the first byte
      of the next character will be interpreted as an 8-bit unsigned integer. If there are
      additional bytes, they will throw an error (unless the environment variable POSIXLY_CORRECT
      is set)

WRITTEN BY :
  Nathan E. Ross, et al. for the uutils project

MORE INFO :
  https://github.com/uutils/coreutils

COPYRIGHT :
  Copyright 2015 uutils project.
  Licensed under the MIT License, please see LICENSE file for details

";

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let args = args
        .collect_str(InvalidEncodingHandling::Ignore)
        .accept_any();

    if args.len() <= 1 {
        return Err(UUsageError::new(1, "missing operand"));
    }
    let formatstr = &args[1];

    if formatstr == "--help" {
        print!("{} {}", LONGHELP_LEAD, LONGHELP_BODY);
    } else if formatstr == "--version" {
        println!("{} {}", uucore::util_name(), crate_version!());
    } else {
        let printf_args = &args[2..];
        memo::Memo::run_all(formatstr, printf_args);
    }
    Ok(())
}

pub fn uu_app<'a>() -> App<'a> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .override_usage(USAGE)
        .arg(Arg::new(HELP).long(HELP).help("Print help information"))
        .arg(
            Arg::new(VERSION)
                .long(VERSION)
                .help("Print version information"),
        )
        .setting(AppSettings::InferLongArgs)
}
<|endoftext|>


<commit_before>// Discord Dependencies -------------------------------------------------------
use discord::model::UserId;

// Internal Dependencies ------------------------------------------------------
use ::bot::BotConfig;
use ::effect::Effect;
use super::Server;

// Server Greeting Interface --------------------------------------------------
impl Server {

    pub fn get_greeting(
        &self,
        member_id: &UserId,
        bot_config: &BotConfig

    ) -> Option<Vec<&Effect>> {
        if let Some(member) = self.members.get(member_id) {
            if let Some(effect_name) = self.config.greetings.get(&member.nickname) {
                Some(self.map_effects(
                    &[effect_name.to_string()],
                    false,
                    bot_config
                ))

            } else if let Some(effect_name) = self.config.greetings.get("effect") {
                Some(self.map_effects(
                    &[effect_name.to_string()],
                    false,
                    bot_config
                ))

            } else {
                None
            }

        } else {
            None
        }
    }

    pub fn has_greeting(&self, nickname: &str) -> bool {
        self.config.greetings.contains_key(nickname)
    }

    pub fn add_greeting(&mut self, nickname: &str, effect_name: &str) {
        self.config.greetings.insert(nickname.to_string(), effect_name.to_string());
        self.store_config().expect("add_greeting failed to store config.");
    }

    pub fn remove_greeting(&mut self, nickname: &str) {
        self.config.greetings.remove(nickname);
        self.store_config().expect("remove_greeting failed to store config.");
    }

    pub fn list_greetings(&self) -> Vec<(&String, &String)> {
        self.config.greetings.iter().map(|(nickname, effect)| {
            (nickname, effect)

        }).collect()
    }

}

<commit_msg>Fix default greeting.<commit_after>// Discord Dependencies -------------------------------------------------------
use discord::model::UserId;

// Internal Dependencies ------------------------------------------------------
use ::bot::BotConfig;
use ::effect::Effect;
use super::Server;

// Server Greeting Interface --------------------------------------------------
impl Server {

    pub fn get_greeting(
        &self,
        member_id: &UserId,
        bot_config: &BotConfig

    ) -> Option<Vec<&Effect>> {
        if let Some(member) = self.members.get(member_id) {

            // User specific greeting
            if let Some(effect_name) = self.config.greetings.get(&member.nickname) {
                Some(self.map_effects(
                    &[effect_name.to_string()],
                    false,
                    bot_config
                ))

            // Default greeting
            } else if let Some(effect_name) = self.config.greetings.get("default") {
                Some(self.map_effects(
                    &[effect_name.to_string()],
                    false,
                    bot_config
                ))

            } else {
                None
            }

        } else {
            None
        }
    }

    pub fn has_greeting(&self, nickname: &str) -> bool {
        self.config.greetings.contains_key(nickname)
    }

    pub fn add_greeting(&mut self, nickname: &str, effect_name: &str) {
        self.config.greetings.insert(nickname.to_string(), effect_name.to_string());
        self.store_config().expect("add_greeting failed to store config.");
    }

    pub fn remove_greeting(&mut self, nickname: &str) {
        self.config.greetings.remove(nickname);
        self.store_config().expect("remove_greeting failed to store config.");
    }

    pub fn list_greetings(&self) -> Vec<(&String, &String)> {
        self.config.greetings.iter().map(|(nickname, effect)| {
            (nickname, effect)

        }).collect()
    }

}

<|endoftext|>


<commit_before>use std::{
    fmt,
    hash::{Hash, Hasher},
    ops::Deref,
    result::Result as StdResult,
    sync::Arc,
};

use serde::{
    Deserialize, Serialize,
    {de::Deserializer, ser::Serializer},
};

const CUTOFF: usize = std::mem::size_of::<&[u8]>() - 1;

type Inner = [u8; CUTOFF];

/// A buffer that may either be inline or remote and protected
/// by an Arc
#[derive(Clone)]
pub struct IVec(IVecInner);

impl Default for IVec {
    fn default() -> Self {
        Self::from(&[])
    }
}

#[derive(Clone)]
enum IVecInner {
    Inline(u8, Inner),
    Remote(Arc<[u8]>),
}

impl Hash for IVec {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.deref().hash(state);
    }
}

impl Serialize for IVec {
    fn serialize<S: Serializer>(
        &self,
        serializer: S,
    ) -> StdResult<S::Ok, S::Error> {
        serde_bytes::serialize(self.as_ref(), serializer)
    }
}

impl<'de> Deserialize<'de> for IVec {
    fn deserialize<D: Deserializer<'de>>(
        deserializer: D,
    ) -> StdResult<Self, D::Error> {
        let bytes: StdResult<Box<[u8]>, D::Error> =
            serde_bytes::deserialize(deserializer);
        bytes.map(Self::from)
    }
}

const fn is_inline_candidate(length: usize) -> bool {
    length <= CUTOFF
}

impl IVec {
    fn inline(slice: &[u8]) -> Self {
        assert!(is_inline_candidate(slice.len()));

        let mut data = Inner::default();

        unsafe {
            std::ptr::copy_nonoverlapping(
                slice.as_ptr(),
                data.as_mut_ptr(),
                slice.len(),
            );
        }

        Self(IVecInner::Inline(slice.len() as u8, data))
    }

    fn remote(arc: Arc<[u8]>) -> Self {
        Self(IVecInner::Remote(arc))
    }
}

impl From<Box<[u8]>> for IVec {
    fn from(b: Box<[u8]>) -> Self {
        if is_inline_candidate(b.len()) {
            Self::inline(&b)
        } else {
            // rely on the Arc From specialization
            // for Box<T>, which may improve
            // over time
            Self::remote(Arc::from(b))
        }
    }
}

impl From<&[u8]> for IVec {
    fn from(slice: &[u8]) -> Self {
        if is_inline_candidate(slice.len()) {
            Self::inline(slice)
        } else {
            Self::remote(Arc::from(slice))
        }
    }
}

impl From<Arc<[u8]>> for IVec {
    fn from(arc: Arc<[u8]>) -> Self {
        Self::remote(arc)
    }
}

impl From<&str> for IVec {
    fn from(s: &str) -> Self {
        Self::from(s.as_bytes())
    }
}

impl From<&IVec> for IVec {
    fn from(v: &Self) -> Self {
        v.clone()
    }
}

impl From<Vec<u8>> for IVec {
    fn from(v: Vec<u8>) -> Self {
        if is_inline_candidate(v.len()) {
            Self::inline(&v)
        } else {
            // rely on the Arc From specialization
            // for Vec<T>, which may improve
            // over time
            Self::remote(Arc::from(v))
        }
    }
}

impl std::borrow::Borrow<[u8]> for IVec {
    fn borrow(&self) -> &[u8] {
        self.as_ref()
    }
}

impl std::borrow::Borrow<[u8]> for &IVec {
    fn borrow(&self) -> &[u8] {
        self.as_ref()
    }
}

macro_rules! from_array {
    ($($s:expr),*) => {
        $(
            impl From<&[u8; $s]> for IVec {
                fn from(v: &[u8; $s]) -> Self {
                    Self::from(&v[..])
                }
            }
        )*
    }
}

from_array!(
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
    21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32
);

impl Into<Arc<[u8]>> for IVec {
    fn into(self) -> Arc<[u8]> {
        match self.0 {
            IVecInner::Inline(..) => Arc::from(self.as_ref()),
            IVecInner::Remote(arc) => arc,
        }
    }
}

impl Deref for IVec {
    type Target = [u8];

    #[inline]
    fn deref(&self) -> &[u8] {
        self.as_ref()
    }
}

impl AsRef<[u8]> for IVec {
    #[inline]
    fn as_ref(&self) -> &[u8] {
        match &self.0 {
            IVecInner::Inline(sz, buf) => unsafe {
                buf.get_unchecked(..*sz as usize)
            },
            IVecInner::Remote(buf) => buf,
        }
    }
}

impl Ord for IVec {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.as_ref().cmp(other.as_ref())
    }
}

impl PartialOrd for IVec {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl<T: AsRef<[u8]>> PartialEq<T> for IVec {
    fn eq(&self, other: &T) -> bool {
        self.as_ref() == other.as_ref()
    }
}

impl PartialEq<[u8]> for IVec {
    fn eq(&self, other: &[u8]) -> bool {
        self.as_ref() == other
    }
}

impl Eq for IVec {}

impl fmt::Debug for IVec {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.as_ref().fmt(f)
    }
}

#[test]
fn ivec_usage() {
    let iv1 = IVec::from(vec![1, 2, 3]);
    assert_eq!(iv1, vec![1, 2, 3]);
    let iv2 = IVec::from(&[4; 128][..]);
    assert_eq!(iv2, vec![4; 128]);
}
<commit_msg>Bump inline IVec cutoff to 22, which takes the same ammount of memory<commit_after>use std::{
    fmt,
    hash::{Hash, Hasher},
    ops::Deref,
    result::Result as StdResult,
    sync::Arc,
};

use serde::{
    Deserialize, Serialize,
    {de::Deserializer, ser::Serializer},
};

const CUTOFF: usize = 22;

type Inner = [u8; CUTOFF];

/// A buffer that may either be inline or remote and protected
/// by an Arc
#[derive(Clone)]
pub struct IVec(IVecInner);

impl Default for IVec {
    fn default() -> Self {
        Self::from(&[])
    }
}

#[derive(Clone)]
enum IVecInner {
    Inline(u8, Inner),
    Remote(Arc<[u8]>),
}

impl Hash for IVec {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.deref().hash(state);
    }
}

impl Serialize for IVec {
    fn serialize<S: Serializer>(
        &self,
        serializer: S,
    ) -> StdResult<S::Ok, S::Error> {
        serde_bytes::serialize(self.as_ref(), serializer)
    }
}

impl<'de> Deserialize<'de> for IVec {
    fn deserialize<D: Deserializer<'de>>(
        deserializer: D,
    ) -> StdResult<Self, D::Error> {
        let bytes: StdResult<Box<[u8]>, D::Error> =
            serde_bytes::deserialize(deserializer);
        bytes.map(Self::from)
    }
}

const fn is_inline_candidate(length: usize) -> bool {
    length <= CUTOFF
}

impl IVec {
    fn inline(slice: &[u8]) -> Self {
        assert!(is_inline_candidate(slice.len()));

        let mut data = Inner::default();

        unsafe {
            std::ptr::copy_nonoverlapping(
                slice.as_ptr(),
                data.as_mut_ptr(),
                slice.len(),
            );
        }

        Self(IVecInner::Inline(slice.len() as u8, data))
    }

    fn remote(arc: Arc<[u8]>) -> Self {
        Self(IVecInner::Remote(arc))
    }
}

impl From<Box<[u8]>> for IVec {
    fn from(b: Box<[u8]>) -> Self {
        if is_inline_candidate(b.len()) {
            Self::inline(&b)
        } else {
            // rely on the Arc From specialization
            // for Box<T>, which may improve
            // over time
            Self::remote(Arc::from(b))
        }
    }
}

impl From<&[u8]> for IVec {
    fn from(slice: &[u8]) -> Self {
        if is_inline_candidate(slice.len()) {
            Self::inline(slice)
        } else {
            Self::remote(Arc::from(slice))
        }
    }
}

impl From<Arc<[u8]>> for IVec {
    fn from(arc: Arc<[u8]>) -> Self {
        Self::remote(arc)
    }
}

impl From<&str> for IVec {
    fn from(s: &str) -> Self {
        Self::from(s.as_bytes())
    }
}

impl From<&IVec> for IVec {
    fn from(v: &Self) -> Self {
        v.clone()
    }
}

impl From<Vec<u8>> for IVec {
    fn from(v: Vec<u8>) -> Self {
        if is_inline_candidate(v.len()) {
            Self::inline(&v)
        } else {
            // rely on the Arc From specialization
            // for Vec<T>, which may improve
            // over time
            Self::remote(Arc::from(v))
        }
    }
}

impl std::borrow::Borrow<[u8]> for IVec {
    fn borrow(&self) -> &[u8] {
        self.as_ref()
    }
}

impl std::borrow::Borrow<[u8]> for &IVec {
    fn borrow(&self) -> &[u8] {
        self.as_ref()
    }
}

macro_rules! from_array {
    ($($s:expr),*) => {
        $(
            impl From<&[u8; $s]> for IVec {
                fn from(v: &[u8; $s]) -> Self {
                    Self::from(&v[..])
                }
            }
        )*
    }
}

from_array!(
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
    21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32
);

impl Into<Arc<[u8]>> for IVec {
    fn into(self) -> Arc<[u8]> {
        match self.0 {
            IVecInner::Inline(..) => Arc::from(self.as_ref()),
            IVecInner::Remote(arc) => arc,
        }
    }
}

impl Deref for IVec {
    type Target = [u8];

    #[inline]
    fn deref(&self) -> &[u8] {
        self.as_ref()
    }
}

impl AsRef<[u8]> for IVec {
    #[inline]
    fn as_ref(&self) -> &[u8] {
        match &self.0 {
            IVecInner::Inline(sz, buf) => unsafe {
                buf.get_unchecked(..*sz as usize)
            },
            IVecInner::Remote(buf) => buf,
        }
    }
}

impl Ord for IVec {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.as_ref().cmp(other.as_ref())
    }
}

impl PartialOrd for IVec {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl<T: AsRef<[u8]>> PartialEq<T> for IVec {
    fn eq(&self, other: &T) -> bool {
        self.as_ref() == other.as_ref()
    }
}

impl PartialEq<[u8]> for IVec {
    fn eq(&self, other: &[u8]) -> bool {
        self.as_ref() == other
    }
}

impl Eq for IVec {}

impl fmt::Debug for IVec {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.as_ref().fmt(f)
    }
}

#[test]
fn ivec_usage() {
    let iv1 = IVec::from(vec![1, 2, 3]);
    assert_eq!(iv1, vec![1, 2, 3]);
    let iv2 = IVec::from(&[4; 128][..]);
    assert_eq!(iv2, vec![4; 128]);
}
<|endoftext|>


<commit_before>use std::mem::size_of;

use super::*;

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub(crate) struct Node {
    pub(crate) id: PageId,
    pub(crate) data: Data,
    pub(crate) next: Option<PageId>,
    pub(crate) lo: Bound,
    pub(crate) hi: Bound,
}

impl Node {
    #[inline]
    pub(crate) fn size_in_bytes(&self) -> usize {
        let self_sz = size_of::<Node>();
        let lo_sz = self.lo.size_in_bytes();
        let hi_sz = self.hi.size_in_bytes();
        let data_sz = self.data.size_in_bytes();

        self_sz + lo_sz + hi_sz + data_sz
    }

    pub(crate) fn apply(
        &mut self,
        frag: &Frag,
        merge_operator: Option<usize>,
    ) {
        use self::Frag::*;

        match *frag {
            Set(ref k, ref v) => {
                let decoded_k = prefix_decode(self.lo.inner(), k);
                if Bound::Inclusive(decoded_k) < self.hi {
                    self.set_leaf(k.clone(), v.clone());
                } else {
                    panic!("tried to consolidate set at key <= hi")
                }
            }
            Merge(ref k, ref v) => {
                let decoded_k = prefix_decode(self.lo.inner(), k);
                if Bound::Inclusive(decoded_k) < self.hi {
                    let merge_fn_ptr = merge_operator
                        .expect("must have a merge operator set");
                    unsafe {
                        let merge_fn: MergeOperator =
                            std::mem::transmute(merge_fn_ptr);
                        self.merge_leaf(
                            k.clone(),
                            v.clone(),
                            merge_fn,
                        );
                    }
                } else {
                    panic!("tried to consolidate set at key <= hi")
                }
            }
            ChildSplit(ref child_split) => {
                self.child_split(child_split);
            }
            ParentSplit(ref parent_split) => {
                self.parent_split(parent_split);
            }
            Del(ref k) => {
                let decoded_k = prefix_decode(self.lo.inner(), k);
                if Bound::Inclusive(decoded_k) < self.hi {
                    self.del_leaf(k);
                } else {
                    panic!("tried to consolidate del at key <= hi")
                }
            }
            Base(_, _) => {
                panic!("encountered base page in middle of chain")
            }
        }
    }

    pub(crate) fn set_leaf(&mut self, key: Key, val: Value) {
        if let Data::Leaf(ref mut records) = self.data {
            let search =
                records.binary_search_by(|&(ref k, ref _v)| {
                    prefix_cmp(k, &*key)
                });
            if let Ok(idx) = search {
                records.push((key, val));
                records.swap_remove(idx);
            } else {
                records.push((key, val));
                records.sort_unstable_by(|a, b| {
                    prefix_cmp(&*a.0, &*b.0)
                });
            }
        } else {
            panic!("tried to Set a value to an index");
        }
    }

    pub(crate) fn merge_leaf(
        &mut self,
        key: Key,
        val: Value,
        merge_fn: MergeOperator,
    ) {
        if let Data::Leaf(ref mut records) = self.data {
            let search =
                records.binary_search_by(|&(ref k, ref _v)| {
                    prefix_cmp(k, &*key)
                });

            let decoded_k = prefix_decode(self.lo.inner(), &key);
            if let Ok(idx) = search {
                let new = merge_fn(
                    &*decoded_k,
                    Some(&records[idx].1),
                    &val,
                );
                if let Some(new) = new {
                    records.push((key, new));
                    records.swap_remove(idx);
                } else {
                    records.remove(idx);
                }
            } else {
                let new = merge_fn(&*decoded_k, None, &val);
                if let Some(new) = new {
                    records.push((key, new));
                    records.sort_unstable_by(|a, b| {
                        prefix_cmp(&*a.0, &*b.0)
                    });
                }
            }
        } else {
            panic!("tried to Merge a value to an index");
        }
    }

    pub(crate) fn child_split(&mut self, cs: &ChildSplit) {
        self.data.drop_gte(&cs.at, self.lo.inner());
        self.hi = Bound::Exclusive(cs.at.inner().to_vec());
        self.next = Some(cs.to);
    }

    pub(crate) fn parent_split(&mut self, ps: &ParentSplit) {
        if let Data::Index(ref mut ptrs) = self.data {
            let encoded_sep =
                prefix_encode(self.lo.inner(), ps.at.inner());
            ptrs.push((encoded_sep, ps.to));
            ptrs.sort_unstable_by(|a, b| prefix_cmp(&*a.0, &*b.0));
        } else {
            panic!("tried to attach a ParentSplit to a Leaf chain");
        }
    }

    pub(crate) fn del_leaf(&mut self, key: KeyRef<'_>) {
        if let Data::Leaf(ref mut records) = self.data {
            let search =
                records.binary_search_by(|&(ref k, ref _v)| {
                    prefix_cmp(k, &*key)
                });
            if let Ok(idx) = search {
                records.remove(idx);
            }
        } else {
            panic!("tried to attach a Del to an Index chain");
        }
    }

    pub(crate) fn should_split(&self, max_sz: usize) -> bool {
        self.data.len() > 2 && self.size_in_bytes() > max_sz
    }

    pub(crate) fn split(&self, id: PageId) -> Node {
        let (split, right_data) = self.data.split(self.lo.inner());
        Node {
            id,
            data: right_data,
            next: self.next,
            lo: Bound::Inclusive(split),
            hi: self.hi.clone(),
        }
    }
}
<commit_msg>Simplify node key setting<commit_after>use std::mem::size_of;

use super::*;

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub(crate) struct Node {
    pub(crate) id: PageId,
    pub(crate) data: Data,
    pub(crate) next: Option<PageId>,
    pub(crate) lo: Bound,
    pub(crate) hi: Bound,
}

impl Node {
    #[inline]
    pub(crate) fn size_in_bytes(&self) -> usize {
        let self_sz = size_of::<Node>();
        let lo_sz = self.lo.size_in_bytes();
        let hi_sz = self.hi.size_in_bytes();
        let data_sz = self.data.size_in_bytes();

        self_sz + lo_sz + hi_sz + data_sz
    }

    pub(crate) fn apply(
        &mut self,
        frag: &Frag,
        merge_operator: Option<usize>,
    ) {
        use self::Frag::*;

        match *frag {
            Set(ref k, ref v) => {
                let decoded_k = prefix_decode(self.lo.inner(), k);
                if Bound::Inclusive(decoded_k) < self.hi {
                    self.set_leaf(k.clone(), v.clone());
                } else {
                    panic!("tried to consolidate set at key <= hi")
                }
            }
            Merge(ref k, ref v) => {
                let decoded_k = prefix_decode(self.lo.inner(), k);
                if Bound::Inclusive(decoded_k) < self.hi {
                    let merge_fn_ptr = merge_operator
                        .expect("must have a merge operator set");
                    unsafe {
                        let merge_fn: MergeOperator =
                            std::mem::transmute(merge_fn_ptr);
                        self.merge_leaf(
                            k.clone(),
                            v.clone(),
                            merge_fn,
                        );
                    }
                } else {
                    panic!("tried to consolidate set at key <= hi")
                }
            }
            ChildSplit(ref child_split) => {
                self.child_split(child_split);
            }
            ParentSplit(ref parent_split) => {
                self.parent_split(parent_split);
            }
            Del(ref k) => {
                let decoded_k = prefix_decode(self.lo.inner(), k);
                if Bound::Inclusive(decoded_k) < self.hi {
                    self.del_leaf(k);
                } else {
                    panic!("tried to consolidate del at key <= hi")
                }
            }
            Base(_, _) => {
                panic!("encountered base page in middle of chain")
            }
        }
    }

    pub(crate) fn set_leaf(&mut self, key: Key, val: Value) {
        if let Data::Leaf(ref mut records) = self.data {
            let search =
                records.binary_search_by(|&(ref k, ref _v)| {
                    prefix_cmp(k, &*key)
                });
            if let Ok(idx) = search {
                records[idx] = (key, val);
            } else {
                records.push((key, val));
                records.sort_unstable_by(|a, b| {
                    prefix_cmp(&*a.0, &*b.0)
                });
            }
        } else {
            panic!("tried to Set a value to an index");
        }
    }

    pub(crate) fn merge_leaf(
        &mut self,
        key: Key,
        val: Value,
        merge_fn: MergeOperator,
    ) {
        if let Data::Leaf(ref mut records) = self.data {
            let search =
                records.binary_search_by(|&(ref k, ref _v)| {
                    prefix_cmp(k, &*key)
                });

            let decoded_k = prefix_decode(self.lo.inner(), &key);
            if let Ok(idx) = search {
                let new = merge_fn(
                    &*decoded_k,
                    Some(&records[idx].1),
                    &val,
                );
                if let Some(new) = new {
                    records[idx] = (key, new);
                } else {
                    records.remove(idx);
                }
            } else {
                let new = merge_fn(&*decoded_k, None, &val);
                if let Some(new) = new {
                    records.push((key, new));
                    records.sort_unstable_by(|a, b| {
                        prefix_cmp(&*a.0, &*b.0)
                    });
                }
            }
        } else {
            panic!("tried to Merge a value to an index");
        }
    }

    pub(crate) fn child_split(&mut self, cs: &ChildSplit) {
        self.data.drop_gte(&cs.at, self.lo.inner());
        self.hi = Bound::Exclusive(cs.at.inner().to_vec());
        self.next = Some(cs.to);
    }

    pub(crate) fn parent_split(&mut self, ps: &ParentSplit) {
        if let Data::Index(ref mut ptrs) = self.data {
            let encoded_sep =
                prefix_encode(self.lo.inner(), ps.at.inner());
            ptrs.push((encoded_sep, ps.to));
            ptrs.sort_unstable_by(|a, b| prefix_cmp(&*a.0, &*b.0));
        } else {
            panic!("tried to attach a ParentSplit to a Leaf chain");
        }
    }

    pub(crate) fn del_leaf(&mut self, key: KeyRef<'_>) {
        if let Data::Leaf(ref mut records) = self.data {
            let search =
                records.binary_search_by(|&(ref k, ref _v)| {
                    prefix_cmp(k, &*key)
                });
            if let Ok(idx) = search {
                records.remove(idx);
            }
        } else {
            panic!("tried to attach a Del to an Index chain");
        }
    }

    pub(crate) fn should_split(&self, max_sz: usize) -> bool {
        self.data.len() > 2 && self.size_in_bytes() > max_sz
    }

    pub(crate) fn split(&self, id: PageId) -> Node {
        let (split, right_data) = self.data.split(self.lo.inner());
        Node {
            id,
            data: right_data,
            next: self.next,
            lo: Bound::Inclusive(split),
            hi: self.hi.clone(),
        }
    }
}
<|endoftext|>