<commit_before>#![allow(unused)]

use interpreter::ast::*;
use interpreter::tokenizer::*;

use std::collections::HashMap;

pub type Memory = HashMap<Ident, Expr>;

impl Expr {
    fn run(&self, memory: &mut Memory) -> Result<Expr, String> {
        match self.node {
            Node::Ident(ref id) => {
                memory
                    .get(id)
                    .ok_or_else(|| {
                        self.debug.to_string() + ", but " + &id.0 + " has not been defined yet."
                    })
                    .map(|val| val.clone())
            }
            Node::Return(ref expr) => {
                let mut res = self.clone();
                res.node = Node::Return(Box::new(expr.run(memory)?));
                Ok(res)
            }
            Node::Op(ref operator) => operator.run(memory),
            Node::Function(ref function) => function.run(memory, Vec::new()),
            Node::If(ref if_stmt) => if_stmt.run(memory),
            Node::For(ref for_stmt) => for_stmt.run(memory),
            Node::While(ref while_stmt) => while_stmt.run(memory),
            _ => Ok(self.clone()),
        }
    }
}

/// avoids needlessly cloning an Expr if it is already resolved to
/// a primitive value. unfortunately, this requires passing in an
/// external storage variable for this case where the value has not
/// yet been resolved to a primitive, for lifetime reasons.
macro_rules! resolve {
    ($exp:ident, $memory:ident, $store:ident) => (
        match $exp.node {
            Node::Int(_) => $exp,
            Node::Float(_) => $exp,
            Node::Str(_) => $exp,
            Node::Bool(_) => $exp,
            _ => {$store = $exp.run($memory)?; &$store}
        }
    )
}

macro_rules! do_binary_op {
    ($op:tt, $memory:ident, $left:ident, $right:ident, $(($typeA:tt, $typeB:tt -> ($asType:tt:$asTypePrim:tt))),*; $(($typeA2:tt, $typeB2:tt -> ($asType2:tt))),*; _ => $defaultCase:expr) => ({
        let store1;
        let store2;
        let left_res = resolve!($left, $memory, store1);
        let right_res = resolve!($right, $memory, store2);
        match (&left_res.node, &right_res.node) {
            $(
                (&Node::$typeA(val1), &Node::$typeB(val2)) => {
                    Ok(Expr::new(Node::$asType((val1 as $asTypePrim) $op (val2 as $asTypePrim)), DebugInfo::none()))
                },
            )*
            $(
                (&Node::$typeA2(ref val1), &Node::$typeB2(ref val2)) => {
                    Ok(Expr::new(Node::$asType2(val1 $op val2), DebugInfo::none()))
                },
            )*
            _ => $defaultCase,
        }
    })
}

impl Operator {
    fn run(&self, memory: &mut Memory) -> Result<Expr, String> {
        match *self {
            Operator::Assign(ref id, ref expr) => {
                let val = expr.run(memory)?;
                memory.insert(id.clone(), val);
                Ok(Expr::new(Node::Noop, expr.debug.clone()))
            }

            Operator::Call(ref id, ref args) => unimplemented!(),

            Operator::Add(ref left, ref right) => {
                do_binary_op!(+, memory, left, right, (Int, Int -> (Int:i64)), (Float, Float -> (Float:f64)), (Float, Int -> (Float:f64)), (Int, Float -> (Float:f64));; _ => unimplemented!())
            }
            Operator::Sub(ref left, ref right) => {
                do_binary_op!(-, memory, left, right, (Int, Int -> (Int:i64)), (Float, Float -> (Float:f64)), (Float, Int -> (Float:f64)), (Int, Float -> (Float:f64));; _ => unimplemented!())
            }
            Operator::Mul(ref left, ref right) => {
                do_binary_op!(*, memory, left, right, (Int, Int -> (Int:i64)), (Float, Float -> (Float:f64)), (Float, Int -> (Float:f64)), (Int, Float -> (Float:f64));; _ => unimplemented!())
            }
            Operator::Div(ref left, ref right) => {
                do_binary_op!(/, memory, left, right, (Int, Int -> (Int:i64)), (Float, Float -> (Float:f64)), (Float, Int -> (Float:f64)), (Int, Float -> (Float:f64));; _ => unimplemented!())
            }
            Operator::Mod(ref left, ref right) => {
                do_binary_op!(%, memory, left, right, (Int, Int -> (Int:i64));; _ => unimplemented!())
            }
            Operator::And(ref left, ref right) => {
                do_binary_op!(&&, memory, left, right, (Bool, Bool -> (Bool:bool));; _ => Err(
                            left.debug.to_string() + &right.debug.to_string() +
                            ", attempt to use logical AND on something which is not a boolean")?)
            }
            Operator::Or(ref left, ref right) => {
                do_binary_op!(||, memory, left, right, (Bool, Bool -> (Bool:bool));; _ => Err(
                            left.debug.to_string() + &right.debug.to_string() +
                            ", attempt to use logical OR on something which is not a boolean")?)
            }
            Operator::Less(ref left, ref right) => {
                do_binary_op!(<, memory, left, right, (Int, Int -> (Bool:i64)), (Float, Float -> (Bool:f64)), (Float, Int -> (Bool:f64)), (Int, Float -> (Bool:f64)); (Str, Str -> (Bool)); _ => Err(
                            left.debug.to_string() + &right.debug.to_string() +
                            ", attempt to compare two values for equality which cannot be compared")?)
            }
            Operator::LessOrEquals(ref left, ref right) => {
                do_binary_op!(<=, memory, left, right, (Int, Int -> (Bool:i64)), (Float, Float -> (Bool:f64)), (Float, Int -> (Bool:f64)), (Int, Float -> (Bool:f64)); (Str, Str -> (Bool)); _ => Err(
                            left.debug.to_string() + &right.debug.to_string() +
                            ", attempt to compare two values for equality which cannot be compared")?)
            }
            Operator::Greater(ref left, ref right) => {
                do_binary_op!(>, memory, left, right, (Int, Int -> (Bool:i64)), (Float, Float -> (Bool:f64)), (Float, Int -> (Bool:f64)), (Int, Float -> (Bool:f64)); (Str, Str -> (Bool)); _ => Err(
                            left.debug.to_string() + &right.debug.to_string() +
                            ", attempt to compare two values for equality which cannot be compared")?)
            }
            Operator::GreaterOrEquals(ref left, ref right) => {
                do_binary_op!(>=, memory, left, right, (Int, Int -> (Bool:i64)), (Float, Float -> (Bool:f64)), (Float, Int -> (Bool:f64)), (Int, Float -> (Bool:f64)); (Str, Str -> (Bool)); _ => Err(
                            left.debug.to_string() + &right.debug.to_string() +
                            ", attempt to compare two values for equality which cannot be compared")?)
            }
            Operator::Equals(ref left, ref right) => {
                do_binary_op!(==, memory, left, right,; (Int, Int -> (Bool)), (Float, Float -> (Bool)), (Str, Str -> (Bool)), (Bool, Bool -> (Bool)); _ => Err(
                            left.debug.to_string() + &right.debug.to_string() +
                            ", attempt to compare two values for equality which cannot be compared")?)
            }
            Operator::NotEquals(ref left, ref right) => {
                do_binary_op!(!=, memory, left, right,; (Int, Int -> (Bool)), (Float, Float -> (Bool)), (Str, Str -> (Bool)), (Bool, Bool -> (Bool)); _ => Err(
                            left.debug.to_string() + &right.debug.to_string() +
                            ", attempt to compare two values for equality which cannot be compared")?)
            }

            Operator::Negate(ref expr) => {
                let store;
                let expr_res = resolve!(expr, memory, store);
                match &expr_res.node {
                    &Node::Int(val) => Ok(Expr::new(Node::Int(-val), DebugInfo::none())),
                    &Node::Float(val) => Ok(Expr::new(Node::Float(-val), DebugInfo::none())),
                    _ => {
                        Err(
                            expr.debug.to_string() +
                                ", attempt to negate something which is not a number",
                        )?
                    }
                }
            }

            Operator::Not(ref expr) => {
                let store;
                let expr_res = resolve!(expr, memory, store);
                match &expr_res.node {
                    &Node::Bool(val) => Ok(Expr::new(Node::Bool(!val), DebugInfo::none())),
                    _ => {
                        Err(
                            expr.debug.to_string() +
                                ", attempt to use logical NOT on something which is not a boolean",
                        )?
                    }
                }
            }
        }
    }
}

/// get the list of visible identifiers at this scope level
fn get_scope(memory: &mut Memory) -> Vec<Ident> {
    memory.keys().cloned().collect()
}

/// remove any identifiers that were created during the scoped region
fn revert_scope(scope: Vec<Ident>, memory: &mut Memory) {
    memory.retain(|ident, _| scope.contains(ident));
}

fn revert_scope_after<T>(val: T, scope: Vec<Ident>, memory: &mut Memory) -> T {
    revert_scope(scope, memory);
    val
}

fn run_exprs(exprs: &Vec<Expr>, memory: &mut Memory) -> Result<Expr, String> {
    for mut expr in exprs {
        match expr.node {
            Node::Return(_) => expr.run(memory)?,
            _ => expr.run(memory)?,
        };
    }
    Ok(Expr::new(Node::Noop, DebugInfo::none()))
}

impl Function {
    fn run(&self, memory: &mut Memory, args: Vec<Expr>) -> Result<Expr, String> {
        if self.args.len() != args.len() {
            return Err(format!(
                "Function {} called with incorrect number of arguments. Expected {} but found {}.",
                self.name,
                self.args.len(),
                args.len()
            ))?;
        }
        let scope = get_scope(memory);
        for (id, val) in self.args.iter().zip(args) {
            memory.insert(id.clone(), val);
        }
        for mut expr in &self.body {
            let expr = expr.run(memory)?;
            match expr.node {
                Node::Return(_) => return revert_scope_after(Ok(expr), scope, memory),
                _ => {}
            };
        }

        revert_scope(scope, memory);

        Ok(Expr::new(Node::Noop, DebugInfo::none()))
    }
}

impl If {
    fn run(&self, memory: &mut Memory) -> Result<Expr, String> {
        let conditional = self.condition.run(memory)?;
        let conditional = match conditional.node {
            Node::Bool(bool_val) => bool_val,
            _ => Err("")?,
        };

        if conditional {
            run_exprs(&self.true_body, memory)
        } else {
            run_exprs(&self.else_body, memory)
        }
    }
}

impl For {
    fn run(&self, memory: &mut Memory) -> Result<Expr, String> {
        unimplemented!();
    }
}

impl While {
    fn run(&self, memory: &mut Memory) -> Result<Expr, String> {
        unimplemented!();
    }
}

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

    fn create_global(name: &str, memory: &mut Memory) {
        memory.insert(
            Ident(name.to_string()),
            Expr::new(Node::Noop, DebugInfo::none()),
        );
    }

    #[test]
    fn walk_assign() {
        let expr = Expr::parse("$someInt = 35").unwrap();
        let mut memory = &mut HashMap::new();

        // create $someInt outside the scope of the anonymous outer function that
        // Expr::parse returns, that way the effect of the assignment can be seen here.
        create_global("$someInt", memory);

        let result = expr.run(memory).unwrap();
        println!("{:#?}\n memory: {:#?}", result, memory);
    }

    #[test]
    fn walk_assign2() {
        let expr = Expr::parse("$otherInt = 35; $someInt = $otherInt + 2;").unwrap();
        let mut memory = &mut HashMap::new();

        // create $someInt outside the scope of the anonymous outer function that
        // Expr::parse returns, that way the effect of the assignment can be seen here.
        create_global("$someInt", memory);

        let result = expr.run(memory).unwrap();
        println!("{:#?}\n memory: {:#?}", result, memory);
    }

    #[test]
    #[should_panic]
    fn walk_assign_undefined() {
        let expr = Expr::parse("$someInt = 35; $otherInt = $someInt + $whatIsThis;").unwrap();
        let mut memory = &mut HashMap::new();
        let result = expr.run(memory).unwrap();
    }
}
<commit_msg>More progress<commit_after>#![allow(unused)]

use interpreter::ast::*;
use interpreter::tokenizer::*;

use std::collections::HashMap;

pub type Memory = HashMap<Ident, Expr>;

impl Expr {
    fn run(&self, memory: &mut Memory) -> Result<Expr, String> {
        match self.node {
            Node::Ident(ref id) => {
                memory
                    .get(id)
                    .ok_or_else(|| {
                        self.debug.to_string() + ", but " + &id.0 + " has not been defined yet."
                    })
                    .map(|val| val.clone())
            }
            Node::Return(ref expr) => {
                let mut res = self.clone();
                res.node = Node::Return(Box::new(expr.run(memory)?));
                Ok(res)
            }
            Node::Op(ref operator) => operator.run(memory),
            Node::Function(ref function) => function.run(memory, Vec::new()),
            Node::If(ref if_stmt) => if_stmt.run(memory),
            Node::For(ref for_stmt) => for_stmt.run(memory),
            Node::While(ref while_stmt) => while_stmt.run(memory),
            _ => Ok(self.clone()),
        }
    }
}

/// avoids needlessly cloning an Expr if it is already resolved to
/// a primitive value. unfortunately, this requires passing in an
/// external storage variable for this case where the value has not
/// yet been resolved to a primitive, for lifetime reasons.
macro_rules! resolve {
    ($exp:ident, $memory:ident, $store:ident) => (
        match $exp.node {
            Node::Int(_) => $exp,
            Node::Float(_) => $exp,
            Node::Str(_) => $exp,
            Node::Bool(_) => $exp,
            _ => {$store = $exp.run($memory)?; &$store}
        }
    )
}

macro_rules! do_binary_op {
    ($op:tt, $memory:ident, $left:ident, $right:ident, $(($typeA:tt, $typeB:tt -> ($asType:tt:$asTypePrim:tt))),*; $(($typeA2:tt, $typeB2:tt -> ($asType2:tt))),*; _ => $defaultCase:expr) => ({
        let store1;
        let store2;
        let left_res = resolve!($left, $memory, store1);
        let right_res = resolve!($right, $memory, store2);
        match (&left_res.node, &right_res.node) {
            $(
                (&Node::$typeA(val1), &Node::$typeB(val2)) => {
                    Ok(Expr::new(Node::$asType((val1 as $asTypePrim) $op (val2 as $asTypePrim)), DebugInfo::none()))
                },
            )*
            $(
                (&Node::$typeA2(ref val1), &Node::$typeB2(ref val2)) => {
                    Ok(Expr::new(Node::$asType2(val1 $op val2), DebugInfo::none()))
                },
            )*
            _ => $defaultCase,
        }
    })
}

impl Operator {
    fn run(&self, memory: &mut Memory) -> Result<Expr, String> {
        match *self {
            Operator::Assign(ref id, ref expr) => {
                let val = expr.run(memory)?;
                memory.insert(id.clone(), val);
                Ok(Expr::new(Node::Noop, expr.debug.clone()))
            }

            Operator::Call(ref id, ref args) => unimplemented!(),

            Operator::Add(ref left, ref right) => {
                do_binary_op!(+, memory, left, right, (Int, Int -> (Int:i64)), (Float, Float -> (Float:f64)), (Float, Int -> (Float:f64)), (Int, Float -> (Float:f64));; _ => unimplemented!())
            }
            Operator::Sub(ref left, ref right) => {
                do_binary_op!(-, memory, left, right, (Int, Int -> (Int:i64)), (Float, Float -> (Float:f64)), (Float, Int -> (Float:f64)), (Int, Float -> (Float:f64));; _ => unimplemented!())
            }
            Operator::Mul(ref left, ref right) => {
                do_binary_op!(*, memory, left, right, (Int, Int -> (Int:i64)), (Float, Float -> (Float:f64)), (Float, Int -> (Float:f64)), (Int, Float -> (Float:f64));; _ => unimplemented!())
            }
            Operator::Div(ref left, ref right) => {
                do_binary_op!(/, memory, left, right, (Int, Int -> (Int:i64)), (Float, Float -> (Float:f64)), (Float, Int -> (Float:f64)), (Int, Float -> (Float:f64));; _ => unimplemented!())
            }
            Operator::Mod(ref left, ref right) => {
                do_binary_op!(%, memory, left, right, (Int, Int -> (Int:i64));; _ => unimplemented!())
            }
            Operator::And(ref left, ref right) => {
                do_binary_op!(&&, memory, left, right, (Bool, Bool -> (Bool:bool));; _ => Err(
                            left.debug.to_string() + &right.debug.to_string() +
                            ", attempt to use logical AND on something which is not a boolean")?)
            }
            Operator::Or(ref left, ref right) => {
                do_binary_op!(||, memory, left, right, (Bool, Bool -> (Bool:bool));; _ => Err(
                            left.debug.to_string() + &right.debug.to_string() +
                            ", attempt to use logical OR on something which is not a boolean")?)
            }
            Operator::Less(ref left, ref right) => {
                do_binary_op!(<, memory, left, right, (Int, Int -> (Bool:i64)), (Float, Float -> (Bool:f64)), (Float, Int -> (Bool:f64)), (Int, Float -> (Bool:f64)); (Str, Str -> (Bool)); _ => Err(
                            left.debug.to_string() + &right.debug.to_string() +
                            ", attempt to compare two values for equality which cannot be compared")?)
            }
            Operator::LessOrEquals(ref left, ref right) => {
                do_binary_op!(<=, memory, left, right, (Int, Int -> (Bool:i64)), (Float, Float -> (Bool:f64)), (Float, Int -> (Bool:f64)), (Int, Float -> (Bool:f64)); (Str, Str -> (Bool)); _ => Err(
                            left.debug.to_string() + &right.debug.to_string() +
                            ", attempt to compare two values for equality which cannot be compared")?)
            }
            Operator::Greater(ref left, ref right) => {
                do_binary_op!(>, memory, left, right, (Int, Int -> (Bool:i64)), (Float, Float -> (Bool:f64)), (Float, Int -> (Bool:f64)), (Int, Float -> (Bool:f64)); (Str, Str -> (Bool)); _ => Err(
                            left.debug.to_string() + &right.debug.to_string() +
                            ", attempt to compare two values for equality which cannot be compared")?)
            }
            Operator::GreaterOrEquals(ref left, ref right) => {
                do_binary_op!(>=, memory, left, right, (Int, Int -> (Bool:i64)), (Float, Float -> (Bool:f64)), (Float, Int -> (Bool:f64)), (Int, Float -> (Bool:f64)); (Str, Str -> (Bool)); _ => Err(
                            left.debug.to_string() + &right.debug.to_string() +
                            ", attempt to compare two values for equality which cannot be compared")?)
            }
            Operator::Equals(ref left, ref right) => {
                do_binary_op!(==, memory, left, right,; (Int, Int -> (Bool)), (Float, Float -> (Bool)), (Str, Str -> (Bool)), (Bool, Bool -> (Bool)); _ => Err(
                            left.debug.to_string() + &right.debug.to_string() +
                            ", attempt to compare two values for equality which cannot be compared")?)
            }
            Operator::NotEquals(ref left, ref right) => {
                do_binary_op!(!=, memory, left, right,; (Int, Int -> (Bool)), (Float, Float -> (Bool)), (Str, Str -> (Bool)), (Bool, Bool -> (Bool)); _ => Err(
                            left.debug.to_string() + &right.debug.to_string() +
                            ", attempt to compare two values for equality which cannot be compared")?)
            }

            Operator::Negate(ref expr) => {
                let store;
                let expr_res = resolve!(expr, memory, store);
                match &expr_res.node {
                    &Node::Int(val) => Ok(Expr::new(Node::Int(-val), DebugInfo::none())),
                    &Node::Float(val) => Ok(Expr::new(Node::Float(-val), DebugInfo::none())),
                    _ => {
                        Err(
                            expr.debug.to_string() +
                                ", attempt to negate something which is not a number",
                        )?
                    }
                }
            }

            Operator::Not(ref expr) => {
                let store;
                let expr_res = resolve!(expr, memory, store);
                match &expr_res.node {
                    &Node::Bool(val) => Ok(Expr::new(Node::Bool(!val), DebugInfo::none())),
                    _ => {
                        Err(
                            expr.debug.to_string() +
                                ", attempt to use logical NOT on something which is not a boolean",
                        )?
                    }
                }
            }
        }
    }
}

/// get the list of visible identifiers at this scope level
fn get_scope(memory: &mut Memory) -> Vec<Ident> {
    memory.keys().cloned().collect()
}

/// remove any identifiers that were created during the scoped region
fn revert_scope(scope: Vec<Ident>, memory: &mut Memory) {
    memory.retain(|ident, _| scope.contains(ident));
}

fn revert_scope_after<T>(val: T, scope: Vec<Ident>, memory: &mut Memory) -> T {
    revert_scope(scope, memory);
    val
}

fn run_exprs(exprs: &Vec<Expr>, memory: &mut Memory) -> Result<Expr, String> {
    for mut expr in exprs {
        match expr.node {
            Node::Return(_) => expr.run(memory)?,
            _ => expr.run(memory)?,
        };
    }
    Ok(Expr::new(Node::Noop, DebugInfo::none()))
}

impl Function {
    fn run(&self, memory: &mut Memory, args: Vec<Expr>) -> Result<Expr, String> {
        if self.args.len() != args.len() {
            return Err(format!(
                "Function {} called with incorrect number of arguments. Expected {} but found {}.",
                self.name,
                self.args.len(),
                args.len()
            ))?;
        }
        let scope = get_scope(memory);
        for (id, val) in self.args.iter().zip(args) {
            memory.insert(id.clone(), val);
        }
        for mut expr in &self.body {
            match expr.run(memory)?.node {
                Node::Return(ret_expr) => return revert_scope_after(Ok(*ret_expr), scope, memory),
                _ => {}
            };
        }

        revert_scope(scope, memory);

        Ok(Expr::new(Node::Noop, DebugInfo::none()))
    }
}

impl If {
    fn run(&self, memory: &mut Memory) -> Result<Expr, String> {
        let conditional = self.condition.run(memory)?;
        let conditional = match conditional.node {
            Node::Bool(bool_val) => bool_val,
            _ => Err("")?,
        };

        if conditional {
            run_exprs(&self.true_body, memory)
        } else {
            run_exprs(&self.else_body, memory)
        }
    }
}

impl For {
    fn run(&self, memory: &mut Memory) -> Result<Expr, String> {
        unimplemented!();
    }
}

impl While {
    fn run(&self, memory: &mut Memory) -> Result<Expr, String> {
        unimplemented!();
    }
}

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

    fn create_global(name: &str, memory: &mut Memory) {
        memory.insert(
            Ident(name.to_string()),
            Expr::new(Node::Noop, DebugInfo::none()),
        );
    }

    #[test]
    fn walk_assign() {
        let expr = Expr::parse("$someInt = 35").unwrap();
        let mut memory = &mut HashMap::new();

        // create $someInt outside the scope of the anonymous outer function that
        // Expr::parse returns, that way the effect of the assignment can be seen here.
        create_global("$someInt", memory);

        let result = expr.run(memory).unwrap();
        println!("{:#?}\n memory: {:#?}", result, memory);
    }

    #[test]
    fn walk_assign2() {
        let expr = Expr::parse("$otherInt = 35; $someInt = $otherInt + 2;").unwrap();
        let mut memory = &mut HashMap::new();

        // create $someInt outside the scope of the anonymous outer function that
        // Expr::parse returns, that way the effect of the assignment can be seen here.
        create_global("$someInt", memory);

        let result = expr.run(memory).unwrap();
        println!("{:#?}\n memory: {:#?}", result, memory);
    }

    #[test]
    #[should_panic]
    fn walk_assign_undefined() {
        let expr = Expr::parse("$someInt = 35; $otherInt = $someInt + $whatIsThis;").unwrap();
        let mut memory = &mut HashMap::new();
        let result = expr.run(memory).unwrap();
    }
}
<|endoftext|>


<commit_before>//! BookmarkCollection module
//!
//! A BookmarkCollection is nothing more than a simple store entry. One can simply call functions
//! from the libimagentrylink::external::ExternalLinker trait on this to generate external links.
//!
//! The BookmarkCollection type offers helper functions to get all links or such things.
use std::ops::Deref;
use std::ops::DerefMut;

use regex::Regex;

use error::BookmarkError as BE;
use error::BookmarkErrorKind as BEK;
use error::MapErrInto;
use result::Result;
use module_path::ModuleEntryPath;

use libimagstore::store::Store;
use libimagstore::storeid::IntoStoreId;
use libimagstore::store::FileLockEntry;
use libimagentrylink::external::ExternalLinker;
use libimagentrylink::internal::InternalLinker;
use libimagentrylink::internal::Link as StoreLink;
use libimagerror::into::IntoError;
use url::Url;

use link::Link;

pub struct BookmarkCollection<'a> {
    fle: FileLockEntry<'a>,
    store: &'a Store,
}

/// {Internal, External}Linker is implemented as Deref is implemented
impl<'a> Deref for BookmarkCollection<'a> {
    type Target = FileLockEntry<'a>;

    fn deref(&self) -> &FileLockEntry<'a> {
        &self.fle
    }

}

impl<'a> DerefMut for BookmarkCollection<'a> {

    fn deref_mut(&mut self) -> &mut FileLockEntry<'a> {
        &mut self.fle
    }

}

impl<'a> BookmarkCollection<'a> {

    pub fn new(store: &'a Store, name: &str) -> Result<BookmarkCollection<'a>> {
        let id = ModuleEntryPath::new(name).into_storeid();
        store.create(id)
            .map(|fle| {
                BookmarkCollection {
                    fle: fle,
                    store: store,
                }
            })
            .map_err_into(BEK::StoreReadError)
    }

    pub fn get(store: &'a Store, name: &str) -> Result<BookmarkCollection<'a>> {
        let id = ModuleEntryPath::new(name).into_storeid();
        store.get(id)
            .map_err_into(BEK::StoreReadError)
            .and_then(|fle| {
                match fle {
                    None => Err(BEK::CollectionNotFound.into_error()),
                    Some(e) => Ok(BookmarkCollection {
                        fle: e,
                        store: store,
                    }),
                }
            })
    }

    pub fn delete(store: &Store, name: &str) -> Result<()> {
        store.delete(ModuleEntryPath::new(name).into_storeid()).map_err_into(BEK::StoreReadError)
    }

    pub fn links(&self) -> Result<Vec<Url>> {
        self.fle.get_external_links(&self.store).map_err_into(BEK::LinkError)
    }

    pub fn link_entries(&self) -> Result<Vec<StoreLink>> {
        use libimagentrylink::external::is_external_link_storeid;

        self.fle
            .get_internal_links()
            .map(|v| v.into_iter().filter(|id| is_external_link_storeid(id)).collect())
            .map_err_into(BEK::StoreReadError)
    }

    pub fn add_link(&mut self, l: Link) -> Result<()> {
        use link::IntoUrl;

        l.into_url()
            .and_then(|url| self.add_external_link(self.store, url).map_err_into(BEK::LinkingError))
            .map_err_into(BEK::LinkError)
    }

    pub fn get_links_matching(&self, r: Regex) -> Result<Vec<Link>> {
        self.get_external_links(self.store)
            .map_err_into(BEK::LinkError)
            .map(|v| {
                v.into_iter()
                    .map(Url::into_string)
                    .filter(|urlstr| r.is_match(&urlstr[..]))
                    .map(Link::from)
                    .collect()
            })
    }

    pub fn remove_link(&self, l: Link) -> Result<()> {
        unimplemented!()
    }

}

<commit_msg>Remove unneeded import<commit_after>//! BookmarkCollection module
//!
//! A BookmarkCollection is nothing more than a simple store entry. One can simply call functions
//! from the libimagentrylink::external::ExternalLinker trait on this to generate external links.
//!
//! The BookmarkCollection type offers helper functions to get all links or such things.
use std::ops::Deref;
use std::ops::DerefMut;

use regex::Regex;

use error::BookmarkErrorKind as BEK;
use error::MapErrInto;
use result::Result;
use module_path::ModuleEntryPath;

use libimagstore::store::Store;
use libimagstore::storeid::IntoStoreId;
use libimagstore::store::FileLockEntry;
use libimagentrylink::external::ExternalLinker;
use libimagentrylink::internal::InternalLinker;
use libimagentrylink::internal::Link as StoreLink;
use libimagerror::into::IntoError;
use url::Url;

use link::Link;

pub struct BookmarkCollection<'a> {
    fle: FileLockEntry<'a>,
    store: &'a Store,
}

/// {Internal, External}Linker is implemented as Deref is implemented
impl<'a> Deref for BookmarkCollection<'a> {
    type Target = FileLockEntry<'a>;

    fn deref(&self) -> &FileLockEntry<'a> {
        &self.fle
    }

}

impl<'a> DerefMut for BookmarkCollection<'a> {

    fn deref_mut(&mut self) -> &mut FileLockEntry<'a> {
        &mut self.fle
    }

}

impl<'a> BookmarkCollection<'a> {

    pub fn new(store: &'a Store, name: &str) -> Result<BookmarkCollection<'a>> {
        let id = ModuleEntryPath::new(name).into_storeid();
        store.create(id)
            .map(|fle| {
                BookmarkCollection {
                    fle: fle,
                    store: store,
                }
            })
            .map_err_into(BEK::StoreReadError)
    }

    pub fn get(store: &'a Store, name: &str) -> Result<BookmarkCollection<'a>> {
        let id = ModuleEntryPath::new(name).into_storeid();
        store.get(id)
            .map_err_into(BEK::StoreReadError)
            .and_then(|fle| {
                match fle {
                    None => Err(BEK::CollectionNotFound.into_error()),
                    Some(e) => Ok(BookmarkCollection {
                        fle: e,
                        store: store,
                    }),
                }
            })
    }

    pub fn delete(store: &Store, name: &str) -> Result<()> {
        store.delete(ModuleEntryPath::new(name).into_storeid()).map_err_into(BEK::StoreReadError)
    }

    pub fn links(&self) -> Result<Vec<Url>> {
        self.fle.get_external_links(&self.store).map_err_into(BEK::LinkError)
    }

    pub fn link_entries(&self) -> Result<Vec<StoreLink>> {
        use libimagentrylink::external::is_external_link_storeid;

        self.fle
            .get_internal_links()
            .map(|v| v.into_iter().filter(|id| is_external_link_storeid(id)).collect())
            .map_err_into(BEK::StoreReadError)
    }

    pub fn add_link(&mut self, l: Link) -> Result<()> {
        use link::IntoUrl;

        l.into_url()
            .and_then(|url| self.add_external_link(self.store, url).map_err_into(BEK::LinkingError))
            .map_err_into(BEK::LinkError)
    }

    pub fn get_links_matching(&self, r: Regex) -> Result<Vec<Link>> {
        self.get_external_links(self.store)
            .map_err_into(BEK::LinkError)
            .map(|v| {
                v.into_iter()
                    .map(Url::into_string)
                    .filter(|urlstr| r.is_match(&urlstr[..]))
                    .map(Link::from)
                    .collect()
            })
    }

    pub fn remove_link(&self, l: Link) -> Result<()> {
        unimplemented!()
    }

}

<|endoftext|>


<commit_before>//! Array Virtual Table
//! Port of [carray](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/carray.c) C extension.
use std::default::Default;
use std::os::raw::{c_char, c_int, c_void};
use std::rc::Rc;

use ffi;
use types::{ToSql, ToSqlOutput, Value};
use vtab::{self, declare_vtab, Context, IndexInfo, VTab, VTabCursor, Values};
use {Connection, Error, Result};

// http://sqlite.org/bindptr.html

pub(crate) const ARRAY_TYPE: *const c_char = b"rarray\0" as *const u8 as *const c_char;

pub(crate) unsafe extern "C" fn free_array(p: *mut c_void) {
    let _: Array = Rc::from_raw(p as *const Vec<Value>);
}

pub type Array = Rc<Vec<Value>>;

impl ToSql for Array {
    fn to_sql(&self) -> Result<ToSqlOutput> {
        Ok(ToSqlOutput::Array(self.clone()))
    }
}

/// Register the "rarray" module.
pub fn load_module(conn: &Connection) -> Result<()> {
    let aux: Option<()> = None;
    conn.create_module("rarray", &ARRAY_MODULE, aux)
}

eponymous_module!(
    ARRAY_MODULE,
    ArrayTab,
    (),
    ArrayTabCursor,
    None,
    array_connect,
    array_best_index,
    array_disconnect,
    None,
    array_open,
    array_close,
    array_filter,
    array_next,
    array_eof,
    array_column,
    array_rowid
);

// Column numbers
// const CARRAY_COLUMN_VALUE : c_int = 0;
const CARRAY_COLUMN_POINTER: c_int = 1;

/// An instance of the Array virtual table
#[repr(C)]
struct ArrayTab {
    /// Base class. Must be first
    base: ffi::sqlite3_vtab,
}

impl VTab for ArrayTab {
    type Aux = ();
    type Cursor = ArrayTabCursor;

    unsafe fn connect(db: *mut ffi::sqlite3, _aux: *mut (), _args: &[&[u8]]) -> Result<ArrayTab> {
        let vtab = ArrayTab {
            base: Default::default(),
        };
        try!(declare_vtab(db, "CREATE TABLE x(value,pointer hidden)"));
        Ok(vtab)
    }

    fn best_index(&self, info: &mut IndexInfo) -> Result<()> {
        // Index of the pointer= constraint
        let mut ptr_idx = None;
        for (i, constraint) in info.constraints().enumerate() {
            if !constraint.is_usable() {
                continue;
            }
            if constraint.operator() != vtab::IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_EQ {
                continue;
            }
            match constraint.column() {
                CARRAY_COLUMN_POINTER => {
                    ptr_idx = Some(i);
                }
                _ => {}
            }
        }
        if let Some(ptr_idx) = ptr_idx {
            {
                let mut constraint_usage = info.constraint_usage(ptr_idx);
                constraint_usage.set_argv_index(1);
                constraint_usage.set_omit(true);
            }
            info.set_estimated_cost(1f64);
            info.set_estimated_rows(100);
            info.set_idx_num(1);
        } else {
            info.set_estimated_cost(2_147_483_647f64);
            info.set_estimated_rows(2_147_483_647);
            info.set_idx_num(0);
        }
        Ok(())
    }

    fn open(&self) -> Result<ArrayTabCursor> {
        Ok(ArrayTabCursor::new())
    }
}

/// A cursor for the Array virtual table
#[repr(C)]
struct ArrayTabCursor {
    /// Base class. Must be first
    base: ffi::sqlite3_vtab_cursor,
    /// The rowid
    row_id: i64,
    /// Pointer to the array of values ("pointer")
    ptr: Option<Array>,
}

impl ArrayTabCursor {
    fn new() -> ArrayTabCursor {
        ArrayTabCursor {
            base: Default::default(),
            row_id: 0,
            ptr: None,
        }
    }
    fn len(&self) -> i64 {
        match self.ptr {
            Some(ref a) => a.len() as i64,
            _ => 0,
        }
    }
}
impl VTabCursor for ArrayTabCursor {
    type Table = ArrayTab;

    fn vtab(&self) -> &ArrayTab {
        unsafe { &*(self.base.pVtab as *const ArrayTab) }
    }
    fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values) -> Result<()> {
        if idx_num > 0 {
            self.ptr = try!(args.get_array(0));
        } else {
            self.ptr = None;
        }
        self.row_id = 1;
        Ok(())
    }
    fn next(&mut self) -> Result<()> {
        self.row_id += 1;
        Ok(())
    }
    fn eof(&self) -> bool {
        self.row_id > self.len()
    }
    fn column(&self, ctx: &mut Context, i: c_int) -> Result<()> {
        match i {
            CARRAY_COLUMN_POINTER => Ok(()),
            _ => {
                if let Some(ref array) = self.ptr {
                    let value = &array[(self.row_id - 1) as usize];
                    ctx.set_result(&value)
                } else {
                    Ok(())
                }
            }
        }
    }
    fn rowid(&self) -> Result<i64> {
        Ok(self.row_id)
    }
}

#[cfg(test)]
mod test {
    use std::rc::Rc;
    use types::Value;
    use vtab::array;
    use Connection;

    #[test]
    #[cfg_attr(rustfmt, rustfmt_skip)]
    fn test_array_module() {
        let db = Connection::open_in_memory().unwrap();
        array::load_module(&db).unwrap();

        let v = vec![1i64, 2, 3, 4];
        let values = v.into_iter().map(|i| Value::from(i)).collect();
        let ptr = Rc::new(values);
        let mut stmt = db.prepare("SELECT value from rarray(?);").unwrap();

        let rows = stmt.query_map(&[&ptr], |row| row.get::<_, i64>(0)).unwrap();
        assert_eq!(2, Rc::strong_count(&ptr));
        let mut count = 0;
        for (i, value) in rows.enumerate() {
            assert_eq!(i as i64, value.unwrap()-1);
            count += 1;
        }
        assert_eq!(4, count);
        assert_eq!(1, Rc::strong_count(&ptr));
    }
}
<commit_msg>Fix clippy warning<commit_after>//! Array Virtual Table
//! Port of [carray](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/carray.c) C extension.
use std::default::Default;
use std::os::raw::{c_char, c_int, c_void};
use std::rc::Rc;

use ffi;
use types::{ToSql, ToSqlOutput, Value};
use vtab::{self, declare_vtab, Context, IndexInfo, VTab, VTabCursor, Values};
use {Connection, Error, Result};

// http://sqlite.org/bindptr.html

pub(crate) const ARRAY_TYPE: *const c_char = b"rarray\0" as *const u8 as *const c_char;

pub(crate) unsafe extern "C" fn free_array(p: *mut c_void) {
    let _: Array = Rc::from_raw(p as *const Vec<Value>);
}

pub type Array = Rc<Vec<Value>>;

impl ToSql for Array {
    fn to_sql(&self) -> Result<ToSqlOutput> {
        Ok(ToSqlOutput::Array(self.clone()))
    }
}

/// Register the "rarray" module.
pub fn load_module(conn: &Connection) -> Result<()> {
    let aux: Option<()> = None;
    conn.create_module("rarray", &ARRAY_MODULE, aux)
}

eponymous_module!(
    ARRAY_MODULE,
    ArrayTab,
    (),
    ArrayTabCursor,
    None,
    array_connect,
    array_best_index,
    array_disconnect,
    None,
    array_open,
    array_close,
    array_filter,
    array_next,
    array_eof,
    array_column,
    array_rowid
);

// Column numbers
// const CARRAY_COLUMN_VALUE : c_int = 0;
const CARRAY_COLUMN_POINTER: c_int = 1;

/// An instance of the Array virtual table
#[repr(C)]
struct ArrayTab {
    /// Base class. Must be first
    base: ffi::sqlite3_vtab,
}

impl VTab for ArrayTab {
    type Aux = ();
    type Cursor = ArrayTabCursor;

    unsafe fn connect(db: *mut ffi::sqlite3, _aux: *mut (), _args: &[&[u8]]) -> Result<ArrayTab> {
        let vtab = ArrayTab {
            base: Default::default(),
        };
        try!(declare_vtab(db, "CREATE TABLE x(value,pointer hidden)"));
        Ok(vtab)
    }

    fn best_index(&self, info: &mut IndexInfo) -> Result<()> {
        // Index of the pointer= constraint
        let mut ptr_idx = None;
        for (i, constraint) in info.constraints().enumerate() {
            if !constraint.is_usable() {
                continue;
            }
            if constraint.operator() != vtab::IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_EQ {
                continue;
            }
            if let CARRAY_COLUMN_POINTER = constraint.column() {
                ptr_idx = Some(i);
            }
        }
        if let Some(ptr_idx) = ptr_idx {
            {
                let mut constraint_usage = info.constraint_usage(ptr_idx);
                constraint_usage.set_argv_index(1);
                constraint_usage.set_omit(true);
            }
            info.set_estimated_cost(1f64);
            info.set_estimated_rows(100);
            info.set_idx_num(1);
        } else {
            info.set_estimated_cost(2_147_483_647f64);
            info.set_estimated_rows(2_147_483_647);
            info.set_idx_num(0);
        }
        Ok(())
    }

    fn open(&self) -> Result<ArrayTabCursor> {
        Ok(ArrayTabCursor::new())
    }
}

/// A cursor for the Array virtual table
#[repr(C)]
struct ArrayTabCursor {
    /// Base class. Must be first
    base: ffi::sqlite3_vtab_cursor,
    /// The rowid
    row_id: i64,
    /// Pointer to the array of values ("pointer")
    ptr: Option<Array>,
}

impl ArrayTabCursor {
    fn new() -> ArrayTabCursor {
        ArrayTabCursor {
            base: Default::default(),
            row_id: 0,
            ptr: None,
        }
    }
    fn len(&self) -> i64 {
        match self.ptr {
            Some(ref a) => a.len() as i64,
            _ => 0,
        }
    }
}
impl VTabCursor for ArrayTabCursor {
    type Table = ArrayTab;

    fn vtab(&self) -> &ArrayTab {
        unsafe { &*(self.base.pVtab as *const ArrayTab) }
    }
    fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values) -> Result<()> {
        if idx_num > 0 {
            self.ptr = try!(args.get_array(0));
        } else {
            self.ptr = None;
        }
        self.row_id = 1;
        Ok(())
    }
    fn next(&mut self) -> Result<()> {
        self.row_id += 1;
        Ok(())
    }
    fn eof(&self) -> bool {
        self.row_id > self.len()
    }
    fn column(&self, ctx: &mut Context, i: c_int) -> Result<()> {
        match i {
            CARRAY_COLUMN_POINTER => Ok(()),
            _ => {
                if let Some(ref array) = self.ptr {
                    let value = &array[(self.row_id - 1) as usize];
                    ctx.set_result(&value)
                } else {
                    Ok(())
                }
            }
        }
    }
    fn rowid(&self) -> Result<i64> {
        Ok(self.row_id)
    }
}

#[cfg(test)]
mod test {
    use std::rc::Rc;
    use types::Value;
    use vtab::array;
    use Connection;

    #[test]
    #[cfg_attr(rustfmt, rustfmt_skip)]
    fn test_array_module() {
        let db = Connection::open_in_memory().unwrap();
        array::load_module(&db).unwrap();

        let v = vec![1i64, 2, 3, 4];
        let values = v.into_iter().map(|i| Value::from(i)).collect();
        let ptr = Rc::new(values);
        let mut stmt = db.prepare("SELECT value from rarray(?);").unwrap();

        let rows = stmt.query_map(&[&ptr], |row| row.get::<_, i64>(0)).unwrap();
        assert_eq!(2, Rc::strong_count(&ptr));
        let mut count = 0;
        for (i, value) in rows.enumerate() {
            assert_eq!(i as i64, value.unwrap()-1);
            count += 1;
        }
        assert_eq!(4, count);
        assert_eq!(1, Rc::strong_count(&ptr));
    }
}
<|endoftext|>


<commit_before>//! Array Virtual Table.
//!
//! Port of [carray](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/carray.c) C extension.
use std::default::Default;
use std::os::raw::{c_char, c_int, c_void};
use std::rc::Rc;

use crate::ffi;
use crate::types::{ToSql, ToSqlOutput, Value};
use crate::vtab::{
    eponymous_only_module, Context, IndexConstraintOp, IndexInfo, Module, VTab, VTabConnection,
    VTabCursor, Values,
};
use crate::{Connection, Result};

// http://sqlite.org/bindptr.html

pub(crate) const ARRAY_TYPE: *const c_char = b"rarray\0" as *const u8 as *const c_char;

pub(crate) unsafe extern "C" fn free_array(p: *mut c_void) {
    let _: Array = Rc::from_raw(p as *const Vec<Value>);
}

pub type Array = Rc<Vec<Value>>;

impl ToSql for Array {
    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
        Ok(ToSqlOutput::Array(self.clone()))
    }
}

/// Register the "rarray" module.
pub fn load_module(conn: &Connection) -> Result<()> {
    let aux: Option<()> = None;
    conn.create_module("rarray", &ARRAY_MODULE, aux)
}

lazy_static::lazy_static! {
    static ref ARRAY_MODULE: Module<ArrayTab> = eponymous_only_module::<ArrayTab>(1);
}

// Column numbers
// const CARRAY_COLUMN_VALUE : c_int = 0;
const CARRAY_COLUMN_POINTER: c_int = 1;

/// An instance of the Array virtual table
#[repr(C)]
struct ArrayTab {
    /// Base class. Must be first
    base: ffi::sqlite3_vtab,
}

impl VTab for ArrayTab {
    type Aux = ();
    type Cursor = ArrayTabCursor;

    fn connect(
        _: &mut VTabConnection,
        _aux: Option<&()>,
        _args: &[&[u8]],
    ) -> Result<(String, ArrayTab)> {
        let vtab = ArrayTab {
            base: ffi::sqlite3_vtab::default(),
        };
        Ok(("CREATE TABLE x(value,pointer hidden)".to_owned(), vtab))
    }

    fn best_index(&self, info: &mut IndexInfo) -> Result<()> {
        // Index of the pointer= constraint
        let mut ptr_idx = None;
        for (i, constraint) in info.constraints().enumerate() {
            if !constraint.is_usable() {
                continue;
            }
            if constraint.operator() != IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_EQ {
                continue;
            }
            if let CARRAY_COLUMN_POINTER = constraint.column() {
                ptr_idx = Some(i);
            }
        }
        if let Some(ptr_idx) = ptr_idx {
            {
                let mut constraint_usage = info.constraint_usage(ptr_idx);
                constraint_usage.set_argv_index(1);
                constraint_usage.set_omit(true);
            }
            info.set_estimated_cost(1f64);
            info.set_estimated_rows(100);
            info.set_idx_num(1);
        } else {
            info.set_estimated_cost(2_147_483_647f64);
            info.set_estimated_rows(2_147_483_647);
            info.set_idx_num(0);
        }
        Ok(())
    }

    fn open(&self) -> Result<ArrayTabCursor> {
        Ok(ArrayTabCursor::new())
    }
}

/// A cursor for the Array virtual table
#[repr(C)]
struct ArrayTabCursor {
    /// Base class. Must be first
    base: ffi::sqlite3_vtab_cursor,
    /// The rowid
    row_id: i64,
    /// Pointer to the array of values ("pointer")
    ptr: Option<Array>,
}

impl ArrayTabCursor {
    fn new() -> ArrayTabCursor {
        ArrayTabCursor {
            base: ffi::sqlite3_vtab_cursor::default(),
            row_id: 0,
            ptr: None,
        }
    }

    fn len(&self) -> i64 {
        match self.ptr {
            Some(ref a) => a.len() as i64,
            _ => 0,
        }
    }
}
impl VTabCursor for ArrayTabCursor {
    fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values<'_>) -> Result<()> {
        if idx_num > 0 {
            self.ptr = args.get_array(0)?;
        } else {
            self.ptr = None;
        }
        self.row_id = 1;
        Ok(())
    }

    fn next(&mut self) -> Result<()> {
        self.row_id += 1;
        Ok(())
    }

    fn eof(&self) -> bool {
        self.row_id > self.len()
    }

    fn column(&self, ctx: &mut Context, i: c_int) -> Result<()> {
        match i {
            CARRAY_COLUMN_POINTER => Ok(()),
            _ => {
                if let Some(ref array) = self.ptr {
                    let value = &array[(self.row_id - 1) as usize];
                    ctx.set_result(&value)
                } else {
                    Ok(())
                }
            }
        }
    }

    fn rowid(&self) -> Result<i64> {
        Ok(self.row_id)
    }
}

#[cfg(test)]
mod test {
    use crate::types::Value;
    use crate::vtab::array;
    use crate::Connection;
    use std::rc::Rc;

    #[test]
    fn test_array_module() {
        let db = Connection::open_in_memory().unwrap();
        array::load_module(&db).unwrap();

        let v = vec![1i64, 2, 3, 4];
        let values = v.into_iter().map(Value::from).collect();
        let ptr = Rc::new(values);
        {
            let mut stmt = db.prepare("SELECT value from rarray(?);").unwrap();

            let rows = stmt.query_map(&[&ptr], |row| row.get::<_, i64>(0)).unwrap();
            assert_eq!(2, Rc::strong_count(&ptr));
            let mut count = 0;
            for (i, value) in rows.enumerate() {
                assert_eq!(i as i64, value.unwrap() - 1);
                count += 1;
            }
            assert_eq!(4, count);
        }
        assert_eq!(1, Rc::strong_count(&ptr));
    }
}
<commit_msg>Fix failing rarray test (#664)<commit_after>//! Array Virtual Table.
//!
//! Port of [carray](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/carray.c) C extension.
use std::default::Default;
use std::os::raw::{c_char, c_int, c_void};
use std::rc::Rc;

use crate::ffi;
use crate::types::{ToSql, ToSqlOutput, Value};
use crate::vtab::{
    eponymous_only_module, Context, IndexConstraintOp, IndexInfo, Module, VTab, VTabConnection,
    VTabCursor, Values,
};
use crate::{Connection, Result};

// http://sqlite.org/bindptr.html

pub(crate) const ARRAY_TYPE: *const c_char = b"rarray\0" as *const u8 as *const c_char;

pub(crate) unsafe extern "C" fn free_array(p: *mut c_void) {
    let _: Array = Rc::from_raw(p as *const Vec<Value>);
}

pub type Array = Rc<Vec<Value>>;

impl ToSql for Array {
    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
        Ok(ToSqlOutput::Array(self.clone()))
    }
}

/// Register the "rarray" module.
pub fn load_module(conn: &Connection) -> Result<()> {
    let aux: Option<()> = None;
    conn.create_module("rarray", &ARRAY_MODULE, aux)
}

lazy_static::lazy_static! {
    static ref ARRAY_MODULE: Module<ArrayTab> = eponymous_only_module::<ArrayTab>(1);
}

// Column numbers
// const CARRAY_COLUMN_VALUE : c_int = 0;
const CARRAY_COLUMN_POINTER: c_int = 1;

/// An instance of the Array virtual table
#[repr(C)]
struct ArrayTab {
    /// Base class. Must be first
    base: ffi::sqlite3_vtab,
}

impl VTab for ArrayTab {
    type Aux = ();
    type Cursor = ArrayTabCursor;

    fn connect(
        _: &mut VTabConnection,
        _aux: Option<&()>,
        _args: &[&[u8]],
    ) -> Result<(String, ArrayTab)> {
        let vtab = ArrayTab {
            base: ffi::sqlite3_vtab::default(),
        };
        Ok(("CREATE TABLE x(value,pointer hidden)".to_owned(), vtab))
    }

    fn best_index(&self, info: &mut IndexInfo) -> Result<()> {
        // Index of the pointer= constraint
        let mut ptr_idx = None;
        for (i, constraint) in info.constraints().enumerate() {
            if !constraint.is_usable() {
                continue;
            }
            if constraint.operator() != IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_EQ {
                continue;
            }
            if let CARRAY_COLUMN_POINTER = constraint.column() {
                ptr_idx = Some(i);
            }
        }
        if let Some(ptr_idx) = ptr_idx {
            {
                let mut constraint_usage = info.constraint_usage(ptr_idx);
                constraint_usage.set_argv_index(1);
                constraint_usage.set_omit(true);
            }
            info.set_estimated_cost(1f64);
            info.set_estimated_rows(100);
            info.set_idx_num(1);
        } else {
            info.set_estimated_cost(2_147_483_647f64);
            info.set_estimated_rows(2_147_483_647);
            info.set_idx_num(0);
        }
        Ok(())
    }

    fn open(&self) -> Result<ArrayTabCursor> {
        Ok(ArrayTabCursor::new())
    }
}

/// A cursor for the Array virtual table
#[repr(C)]
struct ArrayTabCursor {
    /// Base class. Must be first
    base: ffi::sqlite3_vtab_cursor,
    /// The rowid
    row_id: i64,
    /// Pointer to the array of values ("pointer")
    ptr: Option<Array>,
}

impl ArrayTabCursor {
    fn new() -> ArrayTabCursor {
        ArrayTabCursor {
            base: ffi::sqlite3_vtab_cursor::default(),
            row_id: 0,
            ptr: None,
        }
    }

    fn len(&self) -> i64 {
        match self.ptr {
            Some(ref a) => a.len() as i64,
            _ => 0,
        }
    }
}
impl VTabCursor for ArrayTabCursor {
    fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values<'_>) -> Result<()> {
        if idx_num > 0 {
            self.ptr = args.get_array(0)?;
        } else {
            self.ptr = None;
        }
        self.row_id = 1;
        Ok(())
    }

    fn next(&mut self) -> Result<()> {
        self.row_id += 1;
        Ok(())
    }

    fn eof(&self) -> bool {
        self.row_id > self.len()
    }

    fn column(&self, ctx: &mut Context, i: c_int) -> Result<()> {
        match i {
            CARRAY_COLUMN_POINTER => Ok(()),
            _ => {
                if let Some(ref array) = self.ptr {
                    let value = &array[(self.row_id - 1) as usize];
                    ctx.set_result(&value)
                } else {
                    Ok(())
                }
            }
        }
    }

    fn rowid(&self) -> Result<i64> {
        Ok(self.row_id)
    }
}

#[cfg(test)]
mod test {
    use crate::types::Value;
    use crate::vtab::array;
    use crate::Connection;
    use std::rc::Rc;

    #[test]
    fn test_array_module() {
        let db = Connection::open_in_memory().unwrap();
        array::load_module(&db).unwrap();

        let v = vec![1i64, 2, 3, 4];
        let values: Vec<Value> = v.into_iter().map(Value::from).collect();
        let ptr = Rc::new(values);
        {
            let mut stmt = db.prepare("SELECT value from rarray(?);").unwrap();

            let rows = stmt.query_map(&[&ptr], |row| row.get::<_, i64>(0)).unwrap();
            assert_eq!(2, Rc::strong_count(&ptr));
            let mut count = 0;
            for (i, value) in rows.enumerate() {
                assert_eq!(i as i64, value.unwrap() - 1);
                count += 1;
            }
            assert_eq!(4, count);
        }
        assert_eq!(1, Rc::strong_count(&ptr));
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::cell::Ref;

mod options;

pub use self::options::*;

use {
    Attribute,
    AttributeId,
    AttributeType,
    Document,
    ElementId,
    Name,
    Node,
    NodeEdge,
    NodeType,
    Traverse,
    WriteBuffer,
};

/// An indent counter.
struct Depth {
    /// Current depth.
    value: u32,
    block: Vec<u8>,
}

impl Depth {
    /// Creates a new `Depth`.
    #[inline]
    fn new(indent: Indent) -> Depth {
        Depth {
            value: 0,
            block: Depth::gen_indent(indent),
        }
    }

    fn gen_indent(indent: Indent) -> Vec<u8> {
        match indent {
            Indent::None => Vec::new(),
            Indent::Spaces(n) => {
                let mut v = Vec::with_capacity(n as usize);
                for _ in 0..n {
                    v.push(b' ');
                }
                v
            }
            Indent::Tabs => vec![b'\t'],
        }
    }

    /// Writes an indent to the buffer.
    #[inline]
    fn write_indent(&self, buf: &mut Vec<u8>) {
        for _ in 0..self.value {
            buf.extend_from_slice(&self.block);
        }
    }

    /// Writes a relative indent to the buffer.
    #[inline]
    fn write_indent_with_step(&self, step: i8, buf: &mut Vec<u8>) {
        let v = (self.value as i32 + i32::from(step)) as u32;
        for _ in 0..v {
            buf.extend_from_slice(&self.block);
        }
    }
}

/// Writes a document into the buffer.
pub fn write_dom(doc: &Document, opt: &WriteOptions, out: &mut Vec<u8>) {
    let mut depth = Depth::new(opt.indent);
    let mut attrs_depth = Depth::new(opt.attributes_indent);
    let mut iter = doc.root().traverse();

    attrs_depth.value += 1;

    while let Some(edge) = iter.next() {
        match edge {
            NodeEdge::Start(node) => {
                write_start_edge(
                    &node,
                    &mut iter,
                    &mut depth,
                    &attrs_depth,
                    opt,
                    out,
                )
            }
            NodeEdge::End(node) => {
                write_end_edge(&node, &mut depth, opt.indent, out)
            }
        }
    }
}

/// Writes node's start edge.
fn write_start_edge(
    node: &Node,
    iter: &mut Traverse,
    depth: &mut Depth,
    attrs_depth: &Depth,
    opt: &WriteOptions,
    out: &mut Vec<u8>
) {
    match node.node_type() {
        NodeType::Root => {}
        NodeType::Element => {
            depth.write_indent(out);

            if node.children().any(|c| c.node_type() == NodeType::Text) {
                write_text_elem(iter, depth, attrs_depth, opt, node, out);
                write_newline(opt.indent, out);
                return;
            }

            write_element_start(node, depth, attrs_depth, opt, out);

            if node.has_children() {
                let mut has_text = false;
                if let Some(c) = node.first_child() {
                    if c.node_type() == NodeType::Text {
                        has_text = true;
                    }
                }

                if !has_text {
                    depth.value += 1;
                    write_newline(opt.indent, out);
                }
            }
        }
        NodeType::Cdata => {
            depth.write_indent_with_step(-1, out);
            write_non_element_node(node, out);
            write_newline(opt.indent, out);
        }
        NodeType::Declaration |
        NodeType::Comment => {
            depth.write_indent(out);
            write_non_element_node(node, out);
            write_newline(opt.indent, out);
        }
        NodeType::Text => {
            write_non_element_node(node, out);
        }
    }
}

/// Writes a non element node.
///
/// Specifically: Declaration, Comment, Cdata and Text.
fn write_non_element_node(node: &Node, out: &mut Vec<u8>) {
    match node.node_type() {
        NodeType::Declaration => {
            write_node(b"<?xml ", &node.text(), b"?>", out);
        }
        NodeType::Comment => {
            write_node(b"<!--", &node.text(), b"-->", out);
        }
        NodeType::Cdata => {
            write_node(b"<![CDATA[", &node.text(), b"]]>", out);
        }
        NodeType::Text => {
            write_escaped_text(node.text().as_ref(), out);
        }
        _ => unreachable!(),
    }
}

#[inline]
fn write_node(prefix: &[u8], data: &Ref<String>, suffix: &[u8], out: &mut Vec<u8>) {
    out.extend_from_slice(prefix);
    out.extend_from_slice(data.as_bytes());
    out.extend_from_slice(suffix);
}

/// Writes an element start.
///
/// Order:
/// - `<`
/// - tag name
/// - attributes
/// - closing tag, if a node has children
fn write_element_start(
    node: &Node,
    depth: &Depth,
    attrs_depth: &Depth,
    opt: &WriteOptions,
    out: &mut Vec<u8>
) {
    out.push(b'<');

    write_tag_name(&node.tag_name().unwrap(), out);
    write_attributes(node, depth, attrs_depth, opt, out);

    if node.has_children() {
        out.push(b'>');
    }
}

/// Writes an element tag name.
fn write_tag_name(tag_name: &Name<ElementId>, out: &mut Vec<u8>) {
    match *tag_name {
        Name::Id(ref id) => {
            out.extend_from_slice(id.name().as_bytes());
        }
        Name::Name(ref name) => {
            let n = name.clone();
            out.extend_from_slice(n.as_bytes());
        }
    }
}

/// Writes attributes.
///
/// Order:
/// - 'id'
/// - sorted SVG attributes
/// - unsorted non-SVG attributes
fn write_attributes(
    node: &Node,
    depth: &Depth,
    attrs_depth: &Depth,
    opt: &WriteOptions,
    out: &mut Vec<u8>
) {
    // write 'id'
    if node.has_id() {
        let attr = Attribute::new(AttributeId::Id, node.id().clone());
        write_attribute(&attr, depth, attrs_depth, opt, out);
    }

    let attrs = node.attributes();

    match opt.attributes_order {
        AttributesOrder::AsIs => {
            for attr in attrs.iter() {
                write_attribute(attr, depth, attrs_depth, opt, out);
            }
        }
        AttributesOrder::Alphabetical => {
            // sort attributes
            let mut ids: Vec<AttributeId> = attrs.iter_svg().map(|(aid, _)| aid).collect();
            ids.sort_by_key(|x| *x as usize);

            for aid in &ids {
                let attr = attrs.get(*aid).unwrap();
                write_attribute(attr, depth, attrs_depth, opt, out);
            }

            // write non-SVG attributes
            for attr in attrs.iter() {
                if let Name::Name(_) = attr.name {
                    write_attribute(attr, depth, attrs_depth, opt, out);
                }
            }
        }
        AttributesOrder::Specification => {
            // sort attributes
            let mut ids: Vec<AttributeId> = attrs.iter_svg().map(|(aid, _)| aid).collect();
            ids.sort_by_key(|x| *x as usize);

            let mut ids2: Vec<AttributeId> = Vec::with_capacity(ids.len());

            // collect fill attributes
            for aid in &ids {
                if aid.is_fill() {
                    ids2.push(*aid);
                }
            }

            // collect stroke attributes
            for aid in &ids {
                if aid.is_stroke() {
                    ids2.push(*aid);
                }
            }

            // collect style attributes
            for aid in &ids {
                if aid.is_presentation() && !aid.is_fill() && !aid.is_stroke() {
                    ids2.push(*aid);
                }
            }

            // collect element-specific attributes
            if let Some(eid) = node.tag_id() {
                let elem_ids: &[AttributeId] = match eid {
                    ElementId::Svg => &[
                        AttributeId::X,
                        AttributeId::Y,
                        AttributeId::Width,
                        AttributeId::Height,
                        AttributeId::ViewBox,
                        AttributeId::PreserveAspectRatio,
                        AttributeId::Version,
                        AttributeId::BaseProfile,
                    ],
                    ElementId::Rect => &[
                        AttributeId::Transform,
                        AttributeId::X,
                        AttributeId::Y,
                        AttributeId::Width,
                        AttributeId::Height,
                        AttributeId::Rx,
                        AttributeId::Ry,
                    ],
                    ElementId::Circle => &[
                        AttributeId::Transform,
                        AttributeId::Cx,
                        AttributeId::Cy,
                        AttributeId::R,
                    ],
                    ElementId::Ellipse => &[
                        AttributeId::Transform,
                        AttributeId::Cx,
                        AttributeId::Cy,
                        AttributeId::Rx,
                        AttributeId::Ry,
                    ],
                    ElementId::Line => &[
                        AttributeId::Transform,
                        AttributeId::X1,
                        AttributeId::Y1,
                        AttributeId::X2,
                        AttributeId::Y2,
                    ],
                    ElementId::Polyline | ElementId::Polygon => &[
                        AttributeId::Transform,
                        AttributeId::Points,
                    ],
                    ElementId::Path => &[
                        AttributeId::Transform,
                        AttributeId::D,
                    ],
                    ElementId::Use => &[
                        AttributeId::Transform,
                        AttributeId::X,
                        AttributeId::Y,
                        AttributeId::Width,
                        AttributeId::Height,
                        AttributeId::XlinkHref,
                    ],
                    ElementId::Image => &[
                        AttributeId::PreserveAspectRatio,
                        AttributeId::Transform,
                        AttributeId::X,
                        AttributeId::Y,
                        AttributeId::Width,
                        AttributeId::Height,
                        AttributeId::XlinkHref,
                    ],
                    ElementId::Text => &[
                        AttributeId::Transform,
                        AttributeId::X,
                        AttributeId::Y,
                        AttributeId::Dx,
                        AttributeId::Dy,
                        AttributeId::Rotate,
                    ],
                    ElementId::Tspan => &[
                        AttributeId::X,
                        AttributeId::Y,
                        AttributeId::Dx,
                        AttributeId::Dy,
                        AttributeId::Rotate,
                    ],
                    ElementId::LinearGradient => &[
                        AttributeId::X1,
                        AttributeId::Y1,
                        AttributeId::X2,
                        AttributeId::Y2,
                        AttributeId::GradientUnits,
                        AttributeId::GradientTransform,
                        AttributeId::SpreadMethod,
                        AttributeId::XlinkHref,
                    ],
                    ElementId::RadialGradient => &[
                        AttributeId::Cx,
                        AttributeId::Cy,
                        AttributeId::R,
                        AttributeId::Fx,
                        AttributeId::Fy,
                        AttributeId::GradientUnits,
                        AttributeId::GradientTransform,
                        AttributeId::SpreadMethod,
                        AttributeId::XlinkHref,
                    ],
                    ElementId::Pattern => &[
                        AttributeId::ViewBox,
                        AttributeId::X,
                        AttributeId::Y,
                        AttributeId::Width,
                        AttributeId::Height,
                        AttributeId::PatternUnits,
                        AttributeId::PatternContentUnits,
                        AttributeId::PatternTransform,
                        AttributeId::XlinkHref,
                    ],
                    _ => &[],
                };

                for aid in elem_ids {
                    if ids.contains(aid) {
                        ids2.push(*aid);
                    }
                }
            }

            // write sorted
            for aid in &ids2 {
                let attr = attrs.get(*aid).unwrap();
                write_attribute(attr, depth, attrs_depth, opt, out);
            }

            // write what is left
            for aid in &ids {
                if !ids2.contains(aid) {
                    let attr = attrs.get(*aid).unwrap();
                    write_attribute(attr, depth, attrs_depth, opt, out);
                }
            }

            // write non-SVG attributes
            for attr in attrs.iter() {
                if let Name::Name(_) = attr.name {
                    write_attribute(attr, depth, attrs_depth, opt, out);
                }
            }
        }
    }
}

fn write_attribute(
    attr: &Attribute,
    depth: &Depth,
    attrs_depth: &Depth,
    opt: &WriteOptions,
    out: &mut Vec<u8>
) {
    if !opt.write_hidden_attributes && !attr.visible {
        return;
    }

    if opt.attributes_indent == Indent::None {
        out.push(b' ');
    } else {
        out.push(b'\n');
        depth.write_indent(out);
        attrs_depth.write_indent(out);
    }

    attr.write_buf_opt(opt, out);
}

/// Writes a `text` element node and it's children.
fn write_text_elem(
    iter: &mut Traverse,
    depth: &mut Depth,
    attrs_depth: &Depth,
    opt: &WriteOptions,
    root: &Node,
    out: &mut Vec<u8>,
) {
    for edge in iter {
        if let NodeEdge::End(node) = edge {
            if let NodeType::Element = node.node_type() {
                if node == *root {
                    break;
                }
            }
        }
    }

    _write_text_elem(root, depth, attrs_depth, opt, out);
}

fn _write_text_elem(
    root: &Node,
    depth: &mut Depth,
    attrs_depth: &Depth,
    opt: &WriteOptions,
    out: &mut Vec<u8>,
) {
    write_element_start(root, depth, attrs_depth, opt, out);

    for child in root.children() {
        match child.node_type() {
            NodeType::Element => {
                _write_text_elem(&child, depth, attrs_depth, opt, out);
            }
            NodeType::Text => {
                if child.text().trim().is_empty() {
                    if root.children().count() == 1 {
                        continue;
                    }
                }

                write_escaped_text(child.text().as_ref(), out);
            }
            _ => {
                warn!("'text' element should contain only element and text nodes");
            }
        }
    }

    write_element_end(&root, out);
}

fn write_escaped_text(text: &str, out: &mut Vec<u8>) {
    for c in text.as_bytes() {
        match *c {
            b'&'  => out.extend_from_slice(b"&"),
            b'<'  => out.extend_from_slice(b"<"),
            b'>'  => out.extend_from_slice(b">"),
            _     => out.push(*c),
        }
    }
}

/// Writes an element closing tag.
fn write_element_end(node: &Node, out: &mut Vec<u8>) {
    if node.has_children() {
        out.extend_from_slice(b"</");
        write_tag_name(&node.tag_name().unwrap(), out);
        out.push(b'>');
    } else {
        out.extend_from_slice(b"/>");
    }
}

/// Writes node's end edge.
fn write_end_edge(node: &Node, depth: &mut Depth, indent: Indent, out: &mut Vec<u8>) {
    if let NodeType::Element = node.node_type() {
        if node.has_children() {
            if depth.value > 0 {
                depth.value -= 1;
            }
            depth.write_indent(out);
        }
        write_element_end(node, out);
        write_newline(indent, out);
    }
}

/// Writes a new line.
#[inline]
fn write_newline(indent: Indent, out: &mut Vec<u8>) {
    if indent != Indent::None {
        out.push(b'\n');
    }
}
<commit_msg>Build fixed.<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::cell::Ref;

mod options;

pub use self::options::*;

use {
    Attribute,
    AttributeId,
    AttributeType,
    Document,
    ElementId,
    Name,
    Node,
    NodeEdge,
    NodeType,
    Traverse,
    WriteBuffer,
};

/// An indent counter.
struct Depth {
    /// Current depth.
    value: u32,
    block: Vec<u8>,
}

impl Depth {
    /// Creates a new `Depth`.
    #[inline]
    fn new(indent: Indent) -> Depth {
        Depth {
            value: 0,
            block: Depth::gen_indent(indent),
        }
    }

    fn gen_indent(indent: Indent) -> Vec<u8> {
        match indent {
            Indent::None => Vec::new(),
            Indent::Spaces(n) => {
                let mut v = Vec::with_capacity(n as usize);
                for _ in 0..n {
                    v.push(b' ');
                }
                v
            }
            Indent::Tabs => vec![b'\t'],
        }
    }

    /// Writes an indent to the buffer.
    #[inline]
    fn write_indent(&self, buf: &mut Vec<u8>) {
        for _ in 0..self.value {
            buf.extend_from_slice(&self.block);
        }
    }

    /// Writes a relative indent to the buffer.
    #[inline]
    fn write_indent_with_step(&self, step: i8, buf: &mut Vec<u8>) {
        let v = (self.value as i32 + i32::from(step)) as u32;
        for _ in 0..v {
            buf.extend_from_slice(&self.block);
        }
    }
}

/// Writes a document into the buffer.
pub fn write_dom(doc: &Document, opt: &WriteOptions, out: &mut Vec<u8>) {
    let mut depth = Depth::new(opt.indent);
    let mut attrs_depth = Depth::new(opt.attributes_indent);
    let mut iter = doc.root().traverse();

    attrs_depth.value += 1;

    while let Some(edge) = iter.next() {
        match edge {
            NodeEdge::Start(node) => {
                write_start_edge(
                    &node,
                    &mut iter,
                    &mut depth,
                    &attrs_depth,
                    opt,
                    out,
                )
            }
            NodeEdge::End(node) => {
                write_end_edge(&node, &mut depth, opt.indent, out)
            }
        }
    }
}

/// Writes node's start edge.
fn write_start_edge(
    node: &Node,
    iter: &mut Traverse,
    depth: &mut Depth,
    attrs_depth: &Depth,
    opt: &WriteOptions,
    out: &mut Vec<u8>
) {
    match node.node_type() {
        NodeType::Root => {}
        NodeType::Element => {
            depth.write_indent(out);

            if node.children().any(|c| c.node_type() == NodeType::Text) {
                write_text_elem(iter, depth, attrs_depth, opt, node, out);
                write_newline(opt.indent, out);
                return;
            }

            write_element_start(node, depth, attrs_depth, opt, out);

            if node.has_children() {
                let mut has_text = false;
                if let Some(c) = node.first_child() {
                    if c.node_type() == NodeType::Text {
                        has_text = true;
                    }
                }

                if !has_text {
                    depth.value += 1;
                    write_newline(opt.indent, out);
                }
            }
        }
        NodeType::Cdata => {
            depth.write_indent_with_step(-1, out);
            write_non_element_node(node, out);
            write_newline(opt.indent, out);
        }
        NodeType::Declaration |
        NodeType::Comment => {
            depth.write_indent(out);
            write_non_element_node(node, out);
            write_newline(opt.indent, out);
        }
        NodeType::Text => {
            write_non_element_node(node, out);
        }
    }
}

/// Writes a non element node.
///
/// Specifically: Declaration, Comment, Cdata and Text.
fn write_non_element_node(node: &Node, out: &mut Vec<u8>) {
    match node.node_type() {
        NodeType::Declaration => {
            write_node(b"<?xml ", &node.text(), b"?>", out);
        }
        NodeType::Comment => {
            write_node(b"<!--", &node.text(), b"-->", out);
        }
        NodeType::Cdata => {
            write_node(b"<![CDATA[", &node.text(), b"]]>", out);
        }
        NodeType::Text => {
            write_escaped_text(node.text().as_ref(), out);
        }
        _ => unreachable!(),
    }
}

#[inline]
fn write_node(prefix: &[u8], data: &Ref<String>, suffix: &[u8], out: &mut Vec<u8>) {
    out.extend_from_slice(prefix);
    out.extend_from_slice(data.as_bytes());
    out.extend_from_slice(suffix);
}

/// Writes an element start.
///
/// Order:
/// - `<`
/// - tag name
/// - attributes
/// - closing tag, if a node has children
fn write_element_start(
    node: &Node,
    depth: &Depth,
    attrs_depth: &Depth,
    opt: &WriteOptions,
    out: &mut Vec<u8>
) {
    out.push(b'<');

    write_tag_name(&node.tag_name().unwrap(), out);
    write_attributes(node, depth, attrs_depth, opt, out);

    if node.has_children() {
        out.push(b'>');
    }
}

/// Writes an element tag name.
fn write_tag_name(tag_name: &Name<ElementId>, out: &mut Vec<u8>) {
    match *tag_name {
        Name::Id(ref id) => {
            out.extend_from_slice(id.name().as_bytes());
        }
        Name::Name(ref name) => {
            let n = name.clone();
            out.extend_from_slice(n.as_bytes());
        }
    }
}

/// Writes attributes.
///
/// Order:
/// - 'id'
/// - sorted SVG attributes
/// - unsorted non-SVG attributes
fn write_attributes(
    node: &Node,
    depth: &Depth,
    attrs_depth: &Depth,
    opt: &WriteOptions,
    out: &mut Vec<u8>
) {
    // write 'id'
    if node.has_id() {
        let attr = Attribute::new(AttributeId::Id, node.id().clone());
        write_attribute(&attr, depth, attrs_depth, opt, out);
    }

    let attrs = node.attributes();

    match opt.attributes_order {
        AttributesOrder::AsIs => {
            for attr in attrs.iter() {
                write_attribute(attr, depth, attrs_depth, opt, out);
            }
        }
        AttributesOrder::Alphabetical => {
            // sort attributes
            let mut ids: Vec<AttributeId> = attrs.iter_svg().map(|(aid, _)| aid).collect();
            ids.sort_by_key(|x| *x as usize);

            for aid in &ids {
                let attr = attrs.get(*aid).unwrap();
                write_attribute(attr, depth, attrs_depth, opt, out);
            }

            // write non-SVG attributes
            for attr in attrs.iter() {
                if let Name::Name(_) = attr.name {
                    write_attribute(attr, depth, attrs_depth, opt, out);
                }
            }
        }
        AttributesOrder::Specification => {
            // sort attributes
            let mut ids: Vec<AttributeId> = attrs.iter_svg().map(|(aid, _)| aid).collect();
            ids.sort_by_key(|x| *x as usize);

            let mut ids2: Vec<AttributeId> = Vec::with_capacity(ids.len());

            // collect fill attributes
            for aid in &ids {
                if aid.is_fill() {
                    ids2.push(*aid);
                }
            }

            // collect stroke attributes
            for aid in &ids {
                if aid.is_stroke() {
                    ids2.push(*aid);
                }
            }

            // collect style attributes
            for aid in &ids {
                if aid.is_presentation() && !aid.is_fill() && !aid.is_stroke() {
                    ids2.push(*aid);
                }
            }

            // collect element-specific attributes
            if let Some(eid) = node.tag_id() {
                for aid in attrs_order_by_element(eid) {
                    if ids.contains(aid) {
                        ids2.push(*aid);
                    }
                }
            }

            // write sorted
            for aid in &ids2 {
                let attr = attrs.get(*aid).unwrap();
                write_attribute(attr, depth, attrs_depth, opt, out);
            }

            // write what is left
            for aid in &ids {
                if !ids2.contains(aid) {
                    let attr = attrs.get(*aid).unwrap();
                    write_attribute(attr, depth, attrs_depth, opt, out);
                }
            }

            // write non-SVG attributes
            for attr in attrs.iter() {
                if let Name::Name(_) = attr.name {
                    write_attribute(attr, depth, attrs_depth, opt, out);
                }
            }
        }
    }
}

static SVG_ATTRIBUTES: &'static [AttributeId] = &[
    AttributeId::X,
    AttributeId::Y,
    AttributeId::Width,
    AttributeId::Height,
    AttributeId::ViewBox,
    AttributeId::PreserveAspectRatio,
    AttributeId::Version,
    AttributeId::BaseProfile,
];

static RECT_ATTRIBUTES: &'static [AttributeId] = &[
    AttributeId::Transform,
    AttributeId::X,
    AttributeId::Y,
    AttributeId::Width,
    AttributeId::Height,
    AttributeId::Rx,
    AttributeId::Ry,
];

static CIRCLE_ATTRIBUTES: &'static [AttributeId] = &[
    AttributeId::Transform,
    AttributeId::Cx,
    AttributeId::Cy,
    AttributeId::R,
];

static ELLIPSE_ATTRIBUTES: &'static [AttributeId] = &[
    AttributeId::Transform,
    AttributeId::Cx,
    AttributeId::Cy,
    AttributeId::Rx,
    AttributeId::Ry,
];

static LINE_ATTRIBUTES: &'static [AttributeId] = &[
    AttributeId::Transform,
    AttributeId::X1,
    AttributeId::Y1,
    AttributeId::X2,
    AttributeId::Y2,
];

static POLYLINE_ATTRIBUTES: &'static [AttributeId] = &[
    AttributeId::Transform,
    AttributeId::Points,
];

static PATH_ATTRIBUTES: &'static [AttributeId] = &[
    AttributeId::Transform,
    AttributeId::D,
];

static USE_ATTRIBUTES: &'static [AttributeId] = &[
    AttributeId::Transform,
    AttributeId::X,
    AttributeId::Y,
    AttributeId::Width,
    AttributeId::Height,
    AttributeId::XlinkHref,
];

static IMAGE_ATTRIBUTES: &'static [AttributeId] = &[
    AttributeId::PreserveAspectRatio,
    AttributeId::Transform,
    AttributeId::X,
    AttributeId::Y,
    AttributeId::Width,
    AttributeId::Height,
    AttributeId::XlinkHref,
];

static TEXT_ATTRIBUTES: &'static [AttributeId] = &[
    AttributeId::Transform,
    AttributeId::X,
    AttributeId::Y,
    AttributeId::Dx,
    AttributeId::Dy,
    AttributeId::Rotate,
];

static TSPAN_ATTRIBUTES: &'static [AttributeId] = &[
    AttributeId::X,
    AttributeId::Y,
    AttributeId::Dx,
    AttributeId::Dy,
    AttributeId::Rotate,
];

static LINEAR_GRADIENT_ATTRIBUTES: &'static [AttributeId] = &[
    AttributeId::X1,
    AttributeId::Y1,
    AttributeId::X2,
    AttributeId::Y2,
    AttributeId::GradientUnits,
    AttributeId::GradientTransform,
    AttributeId::SpreadMethod,
    AttributeId::XlinkHref,
];

static RADIAL_GRADIENT_ATTRIBUTES: &'static [AttributeId] = &[
    AttributeId::Cx,
    AttributeId::Cy,
    AttributeId::R,
    AttributeId::Fx,
    AttributeId::Fy,
    AttributeId::GradientUnits,
    AttributeId::GradientTransform,
    AttributeId::SpreadMethod,
    AttributeId::XlinkHref,
];

static PATTERN_ATTRIBUTES: &'static [AttributeId] = &[
    AttributeId::ViewBox,
    AttributeId::X,
    AttributeId::Y,
    AttributeId::Width,
    AttributeId::Height,
    AttributeId::PatternUnits,
    AttributeId::PatternContentUnits,
    AttributeId::PatternTransform,
    AttributeId::XlinkHref,
];

fn attrs_order_by_element(eid: ElementId) -> &'static [AttributeId] {
    match eid {
        ElementId::Svg => SVG_ATTRIBUTES,
        ElementId::Rect => RECT_ATTRIBUTES,
        ElementId::Circle => CIRCLE_ATTRIBUTES,
        ElementId::Ellipse => ELLIPSE_ATTRIBUTES,
        ElementId::Line => LINE_ATTRIBUTES,
        ElementId::Polyline | ElementId::Polygon => POLYLINE_ATTRIBUTES,
        ElementId::Path => PATH_ATTRIBUTES,
        ElementId::Use => USE_ATTRIBUTES,
        ElementId::Image => IMAGE_ATTRIBUTES,
        ElementId::Text => TEXT_ATTRIBUTES,
        ElementId::Tspan => TSPAN_ATTRIBUTES,
        ElementId::LinearGradient => LINEAR_GRADIENT_ATTRIBUTES,
        ElementId::RadialGradient => RADIAL_GRADIENT_ATTRIBUTES,
        ElementId::Pattern => PATTERN_ATTRIBUTES,
        _ => &[],
    }
}

fn write_attribute(
    attr: &Attribute,
    depth: &Depth,
    attrs_depth: &Depth,
    opt: &WriteOptions,
    out: &mut Vec<u8>
) {
    if !opt.write_hidden_attributes && !attr.visible {
        return;
    }

    if opt.attributes_indent == Indent::None {
        out.push(b' ');
    } else {
        out.push(b'\n');
        depth.write_indent(out);
        attrs_depth.write_indent(out);
    }

    attr.write_buf_opt(opt, out);
}

/// Writes a `text` element node and it's children.
fn write_text_elem(
    iter: &mut Traverse,
    depth: &mut Depth,
    attrs_depth: &Depth,
    opt: &WriteOptions,
    root: &Node,
    out: &mut Vec<u8>,
) {
    for edge in iter {
        if let NodeEdge::End(node) = edge {
            if let NodeType::Element = node.node_type() {
                if node == *root {
                    break;
                }
            }
        }
    }

    _write_text_elem(root, depth, attrs_depth, opt, out);
}

fn _write_text_elem(
    root: &Node,
    depth: &mut Depth,
    attrs_depth: &Depth,
    opt: &WriteOptions,
    out: &mut Vec<u8>,
) {
    write_element_start(root, depth, attrs_depth, opt, out);

    for child in root.children() {
        match child.node_type() {
            NodeType::Element => {
                _write_text_elem(&child, depth, attrs_depth, opt, out);
            }
            NodeType::Text => {
                if child.text().trim().is_empty() {
                    if root.children().count() == 1 {
                        continue;
                    }
                }

                write_escaped_text(child.text().as_ref(), out);
            }
            _ => {
                warn!("'text' element should contain only element and text nodes");
            }
        }
    }

    write_element_end(&root, out);
}

fn write_escaped_text(text: &str, out: &mut Vec<u8>) {
    for c in text.as_bytes() {
        match *c {
            b'&'  => out.extend_from_slice(b"&"),
            b'<'  => out.extend_from_slice(b"<"),
            b'>'  => out.extend_from_slice(b">"),
            _     => out.push(*c),
        }
    }
}

/// Writes an element closing tag.
fn write_element_end(node: &Node, out: &mut Vec<u8>) {
    if node.has_children() {
        out.extend_from_slice(b"</");
        write_tag_name(&node.tag_name().unwrap(), out);
        out.push(b'>');
    } else {
        out.extend_from_slice(b"/>");
    }
}

/// Writes node's end edge.
fn write_end_edge(node: &Node, depth: &mut Depth, indent: Indent, out: &mut Vec<u8>) {
    if let NodeType::Element = node.node_type() {
        if node.has_children() {
            if depth.value > 0 {
                depth.value -= 1;
            }
            depth.write_indent(out);
        }
        write_element_end(node, out);
        write_newline(indent, out);
    }
}

/// Writes a new line.
#[inline]
fn write_newline(indent: Indent, out: &mut Vec<u8>) {
    if indent != Indent::None {
        out.push(b'\n');
    }
}
<|endoftext|>


<commit_before>//! `Tombstone` data description.
use super::super::resources::ResourceType;
use super::super::resources::Store;
use crate::data::RoomName;

use super::creep::CreepPartType;

with_base_fields_and_update_struct! {
    /// A tomstone - remnants of a dead creep
    #[derive(Clone, Debug, PartialEq)]
    #[serde(rename_all = "camelCase")]
    pub struct Tombstone {
        /// The user ID of the owner of the creep
        pub user: String,
        /// The body of the creep who died
        pub creep_body: Vec<CreepPartType>,
        /// The ID of the creep who died
        pub creep_id: String,
        /// The name of the creep who died
        pub creep_name: String,
        /// What the creep was saying when they died
        pub creep_saying: Option<String>,
        /// How many ticks-to-live did the creeps have when it died
        pub creep_ticks_to_live: i32,
        /// The time of death of the creep
        pub death_time: u32,
        /// The time at which this structure will decay
        pub decay_time: u32,
        /// The resources and amounts of each resource some game object holds.
        pub store: Store,
        // TODO: what does the tombstone of a power creep look like?
    }

    /// The update structure for a `Tombstone`.
    #[derive(Clone, Debug)]
    #[serde(rename_all = "camelCase")]
    pub struct TombstoneUpdate { ... }
}

impl Tombstone {
    /// Iterates over this structure's resources.
    pub fn resources(&self) -> impl Iterator<Item = (ResourceType, i32)> + '_ {
        self.store.iter()
    }
}

#[cfg(test)]
mod test {
    use serde::Deserialize;

    use super::{ResourceType, Tombstone};

    #[test]
    fn parse_simple_tombstone() {
        let json = json!({
            "_id": "5ce0a11dcd8deb5a978cb216",
            "creepBody": [
                "carry",
                "carry",
                "carry",
                "carry",
                "carry",
                "carry",
                "carry",
                "carry",
                "carry",
                "carry",
                "move",
                "move",
                "move",
                "move",
                "move"
            ],
            "creepId": "5ce08e9f184f823e92616faf",
            "creepName": "HeavyConsolidator6921549",
            "creepSaying": null,
            "creepTicksToLive": 1,
            "deathTime": 6923092,
            "decayTime": 6923167,
            "room": "E8S29",
            "type": "tombstone",
            "user": "5ca80c8f3c33e30c8e85555d",
            "x": 30,
            "y": 31
        });

        let obj = Tombstone::deserialize(json).unwrap();

        assert_eq!(obj.creep_id, "5ce08e9f184f823e92616faf");
        assert_eq!(obj.creep_name, "HeavyConsolidator6921549");
    }

    #[test]
    fn parse_tombstone_with_energy() {
        let json = json!({
            "_id": "5ce0a4839c5bd560bebb03e9",
            "creepBody": [
                "move",
                "move",
                "carry",
                "work"
            ],
            "creepId": "5ce098c5c4e055067fbf182e",
            "creepName": "6922398-0",
            "creepSaying": null,
            "creepTicksToLive": 532,
            "deathTime": 6923377,
            "decayTime": 6923397,
            "energy": 44,
            "room": "E9S31",
            "type": "tombstone",
            "user": "57874d42d0ae911e3bd15bbc",
            "x": 33,
            "y": 23
        });

        let obj = Tombstone::deserialize(json).unwrap();

        assert_eq!(obj.creep_id, "5ce098c5c4e055067fbf182e");
        assert_eq!(obj.creep_name, "6922398-0");
        assert_eq!(obj.store.get(ResourceType::Energy), 44);
    }
}
<commit_msg>Update / fix tombstone tests<commit_after>//! `Tombstone` data description.
use super::super::resources::ResourceType;
use super::super::resources::Store;
use crate::data::RoomName;

use super::creep::CreepPartType;

with_base_fields_and_update_struct! {
    /// A tomstone - remnants of a dead creep
    #[derive(Clone, Debug, PartialEq)]
    #[serde(rename_all = "camelCase")]
    pub struct Tombstone {
        /// The user ID of the owner of the creep
        pub user: String,
        /// The body of the creep who died
        pub creep_body: Vec<CreepPartType>,
        /// The ID of the creep who died
        pub creep_id: String,
        /// The name of the creep who died
        pub creep_name: String,
        /// What the creep was saying when they died
        pub creep_saying: Option<String>,
        /// How many ticks-to-live did the creeps have when it died
        pub creep_ticks_to_live: i32,
        /// The time of death of the creep
        pub death_time: u32,
        /// The time at which this structure will decay
        pub decay_time: u32,
        /// The resources and amounts of each resource some game object holds.
        pub store: Store,
        // TODO: what does the tombstone of a power creep look like?
    }

    /// The update structure for a `Tombstone`.
    #[derive(Clone, Debug)]
    #[serde(rename_all = "camelCase")]
    pub struct TombstoneUpdate { ... }
}

impl Tombstone {
    /// Iterates over this structure's resources.
    pub fn resources(&self) -> impl Iterator<Item = (ResourceType, i32)> + '_ {
        self.store.iter()
    }
}

#[cfg(test)]
mod test {
    use serde::Deserialize;

    use super::{ResourceType, Tombstone};

    #[test]
    fn parse_simple_tombstone() {
        let json = json!({
            "_id": "5f025c6070eb1e39990f1622",
            "creepBody": [
                "move",
                "move",
                "carry",
                "work"
            ],
            "creepId": "5f025b3706ec7d5f6e3cd7a3",
            "creepName": "38445073-0",
            "creepSaying": null,
            "creepTicksToLive": 1457,
            "deathTime": 38445127,
            "decayTime": 38445147,
            "room": "W41N48",
            "store": {
                "energy": 48
            },
            "type": "tombstone",
            "user": "57874d42d0ae911e3bd15bbc",
            "x": 12,
            "y": 24
        });

        let obj = Tombstone::deserialize(json).unwrap();

        assert_eq!(obj.creep_id, "5f025b3706ec7d5f6e3cd7a3");
        assert_eq!(obj.creep_name, "38445073-0");
    }

    #[test]
    fn parse_tombstone_with_energy() {
        let json = json!({
            "_id": "5f023c25557c806c615bf76d",
            "creepBody": [
                "carry",
                "carry",
                "carry",
                "carry",
                "carry",
                "carry",
                "carry",
                "carry",
                "carry",
                "carry",
                "carry",
                "carry",
                "carry",
                "carry",
                "carry",
                "carry",
                "carry",
                "carry",
                "carry",
                "carry",
                "move",
                "move",
                "move",
                "move",
                "move",
                "move",
                "move",
                "move",
                "move",
                "move"
            ],
            "creepId": "5f021a8c0a816c111ff81006",
            "creepName": "W31N48_transport_20200",
            "creepSaying": null,
            "creepTicksToLive": 1,
            "deathTime": 38443608,
            "decayTime": 38443758,
            "room": "W31N48",
            "store": {
                "energy": 1000
            },
            "type": "tombstone",
            "user": "57cd3be0559868c84d297d87",
            "x": 20,
            "y": 33
        });

        let obj = Tombstone::deserialize(json).unwrap();

        assert_eq!(obj.creep_id, "5f021a8c0a816c111ff81006");
        assert_eq!(obj.creep_name, "W31N48_transport_20200");
        assert_eq!(obj.store, store! { Energy: 1000 });
    }
}
<|endoftext|>