<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 engine::Engine;
use engine::EngineError;
use engine::EngineResult;
use engine::ErrorEnum;

use engine::Pool;

use std::cell::RefCell;
use std::path::Path;
use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use std::iter::FromIterator;
use std::rc::Rc;

use super::blockdev::SimDev;
use super::pool::SimPool;
use super::randomization::Randomizer;

#[derive(Debug)]
pub struct SimEngine {
    pub pools: BTreeMap<String, SimPool>,
    rdm: Rc<RefCell<Randomizer>>,
}

impl SimEngine {
    pub fn new() -> SimEngine {
        SimEngine {
            pools: BTreeMap::new(),
            rdm: Rc::new(RefCell::new(Randomizer::new())),
        }
    }
}

impl Engine for SimEngine {
    fn create_pool(&mut self,
                   name: &str,
                   blockdev_paths: &[&Path],
                   raid_level: u16,
                   _force: bool)
                   -> EngineResult<usize> {

        if self.pools.contains_key(name) {
            return Err(EngineError::Stratis(ErrorEnum::AlreadyExists(name.into())));
        }

        let mut devs: Vec<SimDev> =
            blockdev_paths.iter().map(|x| SimDev::new_dev(self.rdm.clone(), x)).collect();

        for dev in devs.iter_mut() {
            dev.update();
        }

        let bad_devs: Vec<&SimDev> = devs.iter().filter(|dev| !dev.usable()).collect();

        if !bad_devs.is_empty() {
            return Err(EngineError::Stratis(ErrorEnum::Busy("some devices are unusable".into())));
        }

        let pool = SimPool::new_pool(self.rdm.clone(), devs.as_slice(), raid_level);

        if self.rdm.borrow_mut().throw_die() {
            return Err(EngineError::Stratis(ErrorEnum::Error("X".into())));
        }

        let num_bdevs = pool.block_devs.len();
        self.pools.insert(name.to_owned(), pool);

        Ok(num_bdevs)
    }

    /// Destroy a pool, if the pool does not exist, return Ok.
    fn destroy_pool(&mut self, name: &str) -> EngineResult<()> {
        let entry = match self.pools.entry(name.into()) {
            Entry::Vacant(_) => return Ok(()),
            Entry::Occupied(entry) => entry,
        };
        if !entry.get().filesystems.is_empty() {
            return Err(EngineError::Stratis(ErrorEnum::Busy("filesystems remaining on pool"
                .into())));
        };
        if !entry.get().block_devs.is_empty() {
            return Err(EngineError::Stratis(ErrorEnum::Busy("devices remaining in pool".into())));
        };
        if !entry.get().cache_devs.is_empty() {
            return Err(EngineError::Stratis(ErrorEnum::Busy("cache devices remaining in pool"
                .into())));
        };
        entry.remove();
        Ok(())
    }

    /// Looks up the pool by its unique name
    fn get_pool(&mut self, name: &str) -> EngineResult<&mut Pool> {
        Ok(try!(self.pools
            .get_mut(name)
            .ok_or(EngineError::Stratis(ErrorEnum::NotFound(name.into())))))
    }

    /// Returns a collection of the Pool objects that belong to this engine
    fn pools(&mut self) -> BTreeMap<&str, &mut Pool> {
        BTreeMap::from_iter(self.pools.iter_mut().map(|x| (x.0 as &str, x.1 as &mut Pool)))
    }

    /// Set properties of the simulator
    fn configure_simulator(&mut self, denominator: u32) -> EngineResult<()> {
        self.rdm.borrow_mut().set_probability(denominator);
        Ok(())
    }
}
<commit_msg>Add some more tests.<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 engine::Engine;
use engine::EngineError;
use engine::EngineResult;
use engine::ErrorEnum;

use engine::Pool;

use std::cell::RefCell;
use std::path::Path;
use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use std::iter::FromIterator;
use std::rc::Rc;

use super::blockdev::SimDev;
use super::pool::SimPool;
use super::randomization::Randomizer;

#[derive(Debug)]
pub struct SimEngine {
    pub pools: BTreeMap<String, SimPool>,
    rdm: Rc<RefCell<Randomizer>>,
}

impl SimEngine {
    pub fn new() -> SimEngine {
        SimEngine {
            pools: BTreeMap::new(),
            rdm: Rc::new(RefCell::new(Randomizer::new())),
        }
    }
}

impl Engine for SimEngine {
    fn create_pool(&mut self,
                   name: &str,
                   blockdev_paths: &[&Path],
                   raid_level: u16,
                   _force: bool)
                   -> EngineResult<usize> {

        if self.pools.contains_key(name) {
            return Err(EngineError::Stratis(ErrorEnum::AlreadyExists(name.into())));
        }

        let mut devs: Vec<SimDev> =
            blockdev_paths.iter().map(|x| SimDev::new_dev(self.rdm.clone(), x)).collect();

        for dev in devs.iter_mut() {
            dev.update();
        }

        let bad_devs: Vec<&SimDev> = devs.iter().filter(|dev| !dev.usable()).collect();

        if !bad_devs.is_empty() {
            return Err(EngineError::Stratis(ErrorEnum::Busy("some devices are unusable".into())));
        }

        let pool = SimPool::new_pool(self.rdm.clone(), devs.as_slice(), raid_level);

        if self.rdm.borrow_mut().throw_die() {
            return Err(EngineError::Stratis(ErrorEnum::Error("X".into())));
        }

        let num_bdevs = pool.block_devs.len();
        self.pools.insert(name.to_owned(), pool);

        Ok(num_bdevs)
    }

    /// Destroy a pool, if the pool does not exist, return Ok.
    fn destroy_pool(&mut self, name: &str) -> EngineResult<()> {
        let entry = match self.pools.entry(name.into()) {
            Entry::Vacant(_) => return Ok(()),
            Entry::Occupied(entry) => entry,
        };
        if !entry.get().filesystems.is_empty() {
            return Err(EngineError::Stratis(ErrorEnum::Busy("filesystems remaining on pool"
                .into())));
        };
        if !entry.get().block_devs.is_empty() {
            return Err(EngineError::Stratis(ErrorEnum::Busy("devices remaining in pool".into())));
        };
        if !entry.get().cache_devs.is_empty() {
            return Err(EngineError::Stratis(ErrorEnum::Busy("cache devices remaining in pool"
                .into())));
        };
        entry.remove();
        Ok(())
    }

    /// Looks up the pool by its unique name
    fn get_pool(&mut self, name: &str) -> EngineResult<&mut Pool> {
        Ok(try!(self.pools
            .get_mut(name)
            .ok_or(EngineError::Stratis(ErrorEnum::NotFound(name.into())))))
    }

    /// Returns a collection of the Pool objects that belong to this engine
    fn pools(&mut self) -> BTreeMap<&str, &mut Pool> {
        BTreeMap::from_iter(self.pools.iter_mut().map(|x| (x.0 as &str, x.1 as &mut Pool)))
    }

    /// Set properties of the simulator
    fn configure_simulator(&mut self, denominator: u32) -> EngineResult<()> {
        self.rdm.borrow_mut().set_probability(denominator);
        Ok(())
    }
}

#[cfg(test)]
mod tests {

    use std::path::Path;

    use quickcheck::QuickCheck;

    use super::SimEngine;

    use engine::Engine;
    use engine::EngineError;
    use engine::ErrorEnum;

    #[test]
    fn prop_configure_simulator_runs() {

        /// Configure simulator should always return Ok.
        fn configure_simulator_runs(denominator: u32) -> bool {
            SimEngine::new().configure_simulator(denominator).is_ok()
        }

        QuickCheck::new().tests(10).quickcheck(configure_simulator_runs as fn(u32) -> bool);
    }

    #[test]
    /// When an engine has no pools, any name lookup should fail
    fn get_pool_err() {
        assert!(match SimEngine::new().get_pool("name") {
            Err(EngineError::Stratis(ErrorEnum::NotFound(_))) => true,
            _ => false,
        });
    }

    #[test]
    /// When an engine has no pools, the thing returned by pools() is empty
    fn pools_empty() {
        assert!(SimEngine::new().pools().is_empty());
    }

    #[test]
    /// When an engine has no pools, destroying any pool must succeed
    fn destroy_pool_empty() {
        assert!(match SimEngine::new().destroy_pool("name") {
            Ok(_) => true,
            _ => false,
        });
    }

    #[test]
    /// Destroying an empty pool should succeed.
    fn destroy_empty_pool() {
        let name = "name";
        let mut engine = SimEngine::new();
        engine.create_pool(name, &vec![], 0, false).unwrap();
        assert!(match engine.destroy_pool(name) {
            Ok(_) => true,
            _ => false,
        });
    }

    #[test]
    /// Destroying a pool with devices should fail
    fn destroy_pool_w_devices() {
        let name = "name";
        let mut engine = SimEngine::new();
        engine.create_pool(name, &vec![Path::new("/s/d")], 0, false).unwrap();
        assert!(match engine.destroy_pool(name) {
            Err(_) => true,
            _ => false,
        });
    }

    #[test]
    #[ignore]
    /// Creating a new pool identical to the previous should succeed
    fn create_new_pool_twice() {
        let name = "name";
        let mut engine = SimEngine::new();
        engine.create_pool(name, &vec![], 0, false).unwrap();
        assert!(match engine.create_pool(name, &vec![], 0, false) {
            Ok(_) => true,
            _ => false,
        });
    }

    #[test]
    /// Creating a new pool with the same name should fail
    fn create_pool_name_collision() {
        let name = "name";
        let mut engine = SimEngine::new();
        engine.create_pool(name, &vec![Path::new("/s/d")], 0, false).unwrap();
        assert!(match engine.create_pool(name, &vec![], 0, false) {
            Err(_) => true,
            _ => false,
        });
    }

    #[test]
    /// Creating a pool with duplicate devices should succeed
    fn create_pool_duplicate_devices() {
        let path = "/s/d";
        let mut engine = SimEngine::new();
        let devices = vec![Path::new(path), Path::new(path)];
        assert!(match engine.create_pool("name", &devices, 0, false) {
            Ok(_) => true,
            _ => false,
        });
    }

    #[test]
    #[ignore]
    /// Creating a pool with an impossible raid level should fail
    fn create_pool_max_u16_raid() {
        let mut engine = SimEngine::new();
        assert!(match engine.create_pool("name", &vec![], u16::max_value(), false) {
            Err(_) => true,
            _ => false,
        });
    }

}
<|endoftext|>


<commit_before>//! Graph visitor algorithms.
//!

use std::collections::{
    HashSet,
    VecDeque,
};
use std::hash::Hash;
use fb::FixedBitSet;

use super::{
    graphmap,
    graph,
    EdgeType,
    EdgeDirection,
    Graph,
    GraphMap,
};

use graph::{
    IndexType,
};

pub trait Graphlike {
    type NodeId: Clone;
}

/// A graph trait for accessing the neighbors iterator
pub trait NeighborIter<'a> : Graphlike {
    type Iter: Iterator<Item=Self::NodeId>;
    fn neighbors(&'a self, n: Self::NodeId) -> Self::Iter;
}

impl<'a, N, E, Ty, Ix> NeighborIter<'a> for Graph<N, E, Ty, Ix> where
    Ty: EdgeType,
    Ix: IndexType,
{
    type Iter = graph::Neighbors<'a, E, Ix>;
    fn neighbors(&'a self, n: graph::NodeIndex<Ix>) -> graph::Neighbors<'a, E, Ix>
    {
        Graph::neighbors(self, n)
    }
}

impl<'a, N, E> NeighborIter<'a> for GraphMap<N, E>
where N: Copy + Clone + Ord + Hash + Eq
{
    type Iter = graphmap::Neighbors<'a, N>;
    fn neighbors(&'a self, n: N) -> graphmap::Neighbors<'a, N>
    {
        GraphMap::neighbors(self, n)
    }
}

/// Wrapper type for walking the graph as if it is undirected
pub struct AsUndirected<G>(pub G);
/// Wrapper type for walking edges the other way
pub struct Reversed<G>(pub G);

impl<'a, 'b, N, E, Ty, Ix> NeighborIter<'a> for AsUndirected<&'b Graph<N, E, Ty, Ix>> where
    Ty: EdgeType,
    Ix: IndexType,
{
    type Iter = graph::Neighbors<'a, E, Ix>;

    fn neighbors(&'a self, n: graph::NodeIndex<Ix>) -> graph::Neighbors<'a, E, Ix>
    {
        Graph::neighbors_undirected(self.0, n)
    }
}

impl<'a, 'b, N, E, Ty, Ix> NeighborIter<'a> for Reversed<&'b Graph<N, E, Ty, Ix>> where
    Ty: EdgeType,
    Ix: IndexType,
{
    type Iter = graph::Neighbors<'a, E, Ix>;
    fn neighbors(&'a self, n: graph::NodeIndex<Ix>) -> graph::Neighbors<'a, E, Ix>
    {
        Graph::neighbors_directed(self.0, n, EdgeDirection::Incoming)
    }
}

pub trait VisitMap<N> {
    /// Return **true** if the value is not already present.
    fn visit(&mut self, N) -> bool;
    fn is_visited(&self, &N) -> bool;
}

impl<Ix> VisitMap<graph::NodeIndex<Ix>> for FixedBitSet where
    Ix: IndexType,
{
    fn visit(&mut self, x: graph::NodeIndex<Ix>) -> bool {
        let present = self.contains(x.index());
        self.insert(x.index());
        !present
    }
    fn is_visited(&self, x: &graph::NodeIndex<Ix>) -> bool {
        self.contains(x.index())
    }
}

impl<N: Eq + Hash> VisitMap<N> for HashSet<N> {
    fn visit(&mut self, x: N) -> bool {
        self.insert(x)
    }
    fn is_visited(&self, x: &N) -> bool {
        self.contains(x)
    }
}

/// Trait for GraphMap that knows which datastructure is the best for its visitor map
pub trait Visitable : Graphlike {
    type Map: VisitMap<Self::NodeId>;
    fn visit_map(&self) -> Self::Map;
}

impl<N, E, Ty, Ix> Graphlike for Graph<N, E, Ty, Ix> where
    Ix: IndexType,
{
    type NodeId = graph::NodeIndex<Ix>;
}

impl<N, E, Ty, Ix> Visitable for Graph<N, E, Ty, Ix> where
    Ty: EdgeType,
    Ix: IndexType,
{
    type Map = FixedBitSet;
    fn visit_map(&self) -> FixedBitSet { FixedBitSet::with_capacity(self.node_count()) }
}

impl<N: Clone, E> Graphlike for GraphMap<N, E>
{
    type NodeId = N;
}

impl<N, E> Visitable for GraphMap<N, E>
    where N: Copy + Clone + Ord + Eq + Hash
{
    type Map = HashSet<N>;
    fn visit_map(&self) -> HashSet<N> { HashSet::with_capacity(self.node_count()) }
}

impl<'a, V: Graphlike> Graphlike for AsUndirected<&'a V>
{
    type NodeId = V::NodeId;
}

impl<'a, V: Graphlike> Graphlike for Reversed<&'a V>
{
    type NodeId = V::NodeId;
}

impl<'a, V: Visitable> Visitable for AsUndirected<&'a V>
{
    type Map = V::Map;
    fn visit_map(&self) -> V::Map {
        self.0.visit_map()
    }
}

impl<'a, V: Visitable> Visitable for Reversed<&'a V>
{
    type Map = V::Map;
    fn visit_map(&self) -> V::Map {
        self.0.visit_map()
    }
}

/// Create or access the adjacency matrix of a graph
pub trait GetAdjacencyMatrix : Graphlike {
    type AdjMatrix;
    fn adjacency_matrix(&self) -> Self::AdjMatrix;
    fn is_adjacent(&self, matrix: &Self::AdjMatrix, a: Self::NodeId, b: Self::NodeId) -> bool;
}

impl<N, E> GetAdjacencyMatrix for GraphMap<N, E>
    where N: Copy + Clone + Ord + Eq + Hash
{
    type AdjMatrix = ();
    #[inline]
    fn adjacency_matrix(&self) { }
    #[inline]
    fn is_adjacent(&self, _: &(), a: N, b: N) -> bool {
        self.contains_edge(a, b)
    }
}

/// A depth first search (DFS) of a graph.
///
/// Using a **Dfs** you can run a traversal over a graph while still retaining
/// mutable access to it, if you use it like the following example:
///
/// ```
/// use petgraph::{Graph, Dfs};
///
/// let mut graph = Graph::<_,()>::new();
/// let a = graph.add_node(0);
///
/// let mut dfs = Dfs::new(&graph, a);
/// while let Some(nx) = dfs.next(&graph) {
///     // we can access `graph` mutably here still
///     graph[nx] += 1;
/// }
///
/// assert_eq!(graph[a], 1);
/// ```
///
/// **Note:** The algorithm may not behave correctly if nodes are removed
/// during iteration. It may not necessarily visit added nodes or edges.
#[derive(Clone, Debug)]
pub struct Dfs<N, VM> {
    pub stack: Vec<N>,
    pub discovered: VM,
}

impl<G: Visitable> Dfs<G::NodeId, G::Map>
{
    /// Create a new **Dfs**, using the graph's visitor map, and put **start**
    /// in the stack of nodes to visit.
    pub fn new(graph: &G, start: G::NodeId) -> Self
    {
        let mut dfs = Dfs::empty(graph);
        dfs.move_to(start);
        dfs
    }

    /// Create a new **Dfs** using the graph's visitor map, and no stack.
    pub fn empty(graph: &G) -> Self
    {
        Dfs {
            stack: Vec::new(),
            discovered: graph.visit_map(),
        }
    }
}

impl<N, VM> Dfs<N, VM> where
    N: Clone,
    VM: VisitMap<N>
{
    /// Keep the discovered map, but clear the visit stack and restart
    /// the dfs from a particular node.
    pub fn move_to(&mut self, start: N)
    {
        self.discovered.visit(start.clone());
        self.stack.clear();
        self.stack.push(start);
    }
}

impl<N, VM> Dfs<N, VM> where
    N: Clone,
    VM: VisitMap<N>
{
    /// Return the next node in the dfs, or **None** if the traversal is done.
    pub fn next<'a, G>(&mut self, graph: &'a G) -> Option<N> where
        G: Graphlike<NodeId=N>,
        G: for<'b> NeighborIter<'b>,
    {
        while let Some(node) = self.stack.pop() {
            for succ in graph.neighbors(node.clone()) {
                if self.discovered.visit(succ.clone()) {
                    self.stack.push(succ);
                }
            }

            return Some(node);
        }
        None
    }
}

/// A breadth first search (BFS) of a graph.
///
/// Using a **Bfs** you can run a traversal over a graph while still retaining
/// mutable access to it, if you use it like the following example:
///
/// ```
/// use petgraph::{Graph, Bfs};
///
/// let mut graph = Graph::<_,()>::new();
/// let a = graph.add_node(0);
///
/// let mut bfs = Bfs::new(&graph, a);
/// while let Some(nx) = bfs.next(&graph) {
///     // we can access `graph` mutably here still
///     graph[nx] += 1;
/// }
///
/// assert_eq!(graph[a], 1);
/// ```
///
/// **Note:** The algorithm may not behave correctly if nodes are removed
/// during iteration. It may not necessarily visit added nodes or edges.
#[derive(Clone)]
pub struct Bfs<N, VM> {
    pub stack: VecDeque<N>,
    pub discovered: VM,
}

impl<G: Visitable> Bfs<G::NodeId, G::Map> where
    G::NodeId: Clone,
{
    /// Create a new **Bfs**, using the graph's visitor map, and put **start**
    /// in the stack of nodes to visit.
    pub fn new(graph: &G, start: G::NodeId) -> Self
    {
        let mut discovered = graph.visit_map();
        discovered.visit(start.clone());
        let mut stack = VecDeque::new();
        stack.push_front(start.clone());
        Bfs {
            stack: stack,
            discovered: discovered,
        }
    }
}

impl<N, VM> Bfs<N, VM> where
    N: Clone,
    VM: VisitMap<N>
{
    /// Return the next node in the dfs, or **None** if the traversal is done.
    pub fn next<'a, G>(&mut self, graph: &'a G) -> Option<N> where
        G: Graphlike<NodeId=N>,
        G: for<'b> NeighborIter<'b>,
    {
        while let Some(node) = self.stack.pop_front() {
            for succ in graph.neighbors(node.clone()) {
                if self.discovered.visit(succ.clone()) {
                    self.stack.push_back(succ);
                }
            }

            return Some(node);
        }
        None
    }

}

/*
pub struct Visitor<G> where
    G: Visitable,
{
    stack: Vec<<G as Graphlike>::NodeId>,
    discovered: <G as Visitable>::Map,
}

pub fn visitor<G>(graph: &G, start: <G as Graphlike>::NodeId) -> Visitor<G> where
    G: Visitable
{
    Visitor{
        stack: vec![start],
        discovered: graph.visit_map(),
    }
}
*/

<commit_msg>Update docs<commit_after>//! Graph visitor algorithms.
//!

use std::collections::{
    HashSet,
    VecDeque,
};
use std::hash::Hash;
use fb::FixedBitSet;

use super::{
    graphmap,
    graph,
    EdgeType,
    EdgeDirection,
    Graph,
    GraphMap,
};

use graph::{
    IndexType,
};

/// Base trait for graphs that defines the node identifier.
pub trait Graphlike {
    type NodeId: Clone;
}

/// A graph trait for accessing the neighbors iterator
pub trait NeighborIter<'a> : Graphlike {
    type Iter: Iterator<Item=Self::NodeId>;

    /// Return an iterator that visits all neighbors of the node **n**.
    fn neighbors(&'a self, n: Self::NodeId) -> Self::Iter;
}

impl<'a, N, E, Ty, Ix> NeighborIter<'a> for Graph<N, E, Ty, Ix> where
    Ty: EdgeType,
    Ix: IndexType,
{
    type Iter = graph::Neighbors<'a, E, Ix>;
    fn neighbors(&'a self, n: graph::NodeIndex<Ix>) -> graph::Neighbors<'a, E, Ix>
    {
        Graph::neighbors(self, n)
    }
}

impl<'a, N, E> NeighborIter<'a> for GraphMap<N, E>
where N: Copy + Clone + Ord + Hash + Eq
{
    type Iter = graphmap::Neighbors<'a, N>;
    fn neighbors(&'a self, n: N) -> graphmap::Neighbors<'a, N>
    {
        GraphMap::neighbors(self, n)
    }
}

/// Wrapper type for walking the graph as if it is undirected
pub struct AsUndirected<G>(pub G);
/// Wrapper type for walking edges the other way
pub struct Reversed<G>(pub G);

impl<'a, 'b, N, E, Ty, Ix> NeighborIter<'a> for AsUndirected<&'b Graph<N, E, Ty, Ix>> where
    Ty: EdgeType,
    Ix: IndexType,
{
    type Iter = graph::Neighbors<'a, E, Ix>;

    fn neighbors(&'a self, n: graph::NodeIndex<Ix>) -> graph::Neighbors<'a, E, Ix>
    {
        Graph::neighbors_undirected(self.0, n)
    }
}

impl<'a, 'b, N, E, Ty, Ix> NeighborIter<'a> for Reversed<&'b Graph<N, E, Ty, Ix>> where
    Ty: EdgeType,
    Ix: IndexType,
{
    type Iter = graph::Neighbors<'a, E, Ix>;
    fn neighbors(&'a self, n: graph::NodeIndex<Ix>) -> graph::Neighbors<'a, E, Ix>
    {
        Graph::neighbors_directed(self.0, n, EdgeDirection::Incoming)
    }
}

pub trait VisitMap<N> {
    /// Return **true** if the value is not already present.
    fn visit(&mut self, N) -> bool;
    fn is_visited(&self, &N) -> bool;
}

impl<Ix> VisitMap<graph::NodeIndex<Ix>> for FixedBitSet where
    Ix: IndexType,
{
    fn visit(&mut self, x: graph::NodeIndex<Ix>) -> bool {
        let present = self.contains(x.index());
        self.insert(x.index());
        !present
    }
    fn is_visited(&self, x: &graph::NodeIndex<Ix>) -> bool {
        self.contains(x.index())
    }
}

impl<N: Eq + Hash> VisitMap<N> for HashSet<N> {
    fn visit(&mut self, x: N) -> bool {
        self.insert(x)
    }
    fn is_visited(&self, x: &N) -> bool {
        self.contains(x)
    }
}

/// Trait for GraphMap that knows which datastructure is the best for its visitor map
pub trait Visitable : Graphlike {
    type Map: VisitMap<Self::NodeId>;
    fn visit_map(&self) -> Self::Map;
}

impl<N, E, Ty, Ix> Graphlike for Graph<N, E, Ty, Ix> where
    Ix: IndexType,
{
    type NodeId = graph::NodeIndex<Ix>;
}

impl<N, E, Ty, Ix> Visitable for Graph<N, E, Ty, Ix> where
    Ty: EdgeType,
    Ix: IndexType,
{
    type Map = FixedBitSet;
    fn visit_map(&self) -> FixedBitSet { FixedBitSet::with_capacity(self.node_count()) }
}

impl<N: Clone, E> Graphlike for GraphMap<N, E>
{
    type NodeId = N;
}

impl<N, E> Visitable for GraphMap<N, E>
    where N: Copy + Clone + Ord + Eq + Hash
{
    type Map = HashSet<N>;
    fn visit_map(&self) -> HashSet<N> { HashSet::with_capacity(self.node_count()) }
}

impl<'a, V: Graphlike> Graphlike for AsUndirected<&'a V>
{
    type NodeId = V::NodeId;
}

impl<'a, V: Graphlike> Graphlike for Reversed<&'a V>
{
    type NodeId = V::NodeId;
}

impl<'a, V: Visitable> Visitable for AsUndirected<&'a V>
{
    type Map = V::Map;
    fn visit_map(&self) -> V::Map {
        self.0.visit_map()
    }
}

impl<'a, V: Visitable> Visitable for Reversed<&'a V>
{
    type Map = V::Map;
    fn visit_map(&self) -> V::Map {
        self.0.visit_map()
    }
}

/// Create or access the adjacency matrix of a graph
pub trait GetAdjacencyMatrix : Graphlike {
    type AdjMatrix;
    fn adjacency_matrix(&self) -> Self::AdjMatrix;
    fn is_adjacent(&self, matrix: &Self::AdjMatrix, a: Self::NodeId, b: Self::NodeId) -> bool;
}

impl<N, E> GetAdjacencyMatrix for GraphMap<N, E>
    where N: Copy + Clone + Ord + Eq + Hash
{
    type AdjMatrix = ();
    #[inline]
    fn adjacency_matrix(&self) { }
    #[inline]
    fn is_adjacent(&self, _: &(), a: N, b: N) -> bool {
        self.contains_edge(a, b)
    }
}

/// A depth first search (DFS) of a graph.
///
/// Using a **Dfs** you can run a traversal over a graph while still retaining
/// mutable access to it, if you use it like the following example:
///
/// ```
/// use petgraph::{Graph, Dfs};
///
/// let mut graph = Graph::<_,()>::new();
/// let a = graph.add_node(0);
///
/// let mut dfs = Dfs::new(&graph, a);
/// while let Some(nx) = dfs.next(&graph) {
///     // we can access `graph` mutably here still
///     graph[nx] += 1;
/// }
///
/// assert_eq!(graph[a], 1);
/// ```
///
/// **Note:** The algorithm may not behave correctly if nodes are removed
/// during iteration. It may not necessarily visit added nodes or edges.
#[derive(Clone, Debug)]
pub struct Dfs<N, VM> {
    pub stack: Vec<N>,
    pub discovered: VM,
}

impl<G: Visitable> Dfs<G::NodeId, G::Map>
{
    /// Create a new **Dfs**, using the graph's visitor map, and put **start**
    /// in the stack of nodes to visit.
    pub fn new(graph: &G, start: G::NodeId) -> Self
    {
        let mut dfs = Dfs::empty(graph);
        dfs.move_to(start);
        dfs
    }

    /// Create a new **Dfs** using the graph's visitor map, and no stack.
    pub fn empty(graph: &G) -> Self
    {
        Dfs {
            stack: Vec::new(),
            discovered: graph.visit_map(),
        }
    }
}

impl<N, VM> Dfs<N, VM> where
    N: Clone,
    VM: VisitMap<N>
{
    /// Keep the discovered map, but clear the visit stack and restart
    /// the dfs from a particular node.
    pub fn move_to(&mut self, start: N)
    {
        self.discovered.visit(start.clone());
        self.stack.clear();
        self.stack.push(start);
    }
}

impl<N, VM> Dfs<N, VM> where
    N: Clone,
    VM: VisitMap<N>
{
    /// Return the next node in the dfs, or **None** if the traversal is done.
    pub fn next<'a, G>(&mut self, graph: &'a G) -> Option<N> where
        G: Graphlike<NodeId=N>,
        G: for<'b> NeighborIter<'b>,
    {
        while let Some(node) = self.stack.pop() {
            for succ in graph.neighbors(node.clone()) {
                if self.discovered.visit(succ.clone()) {
                    self.stack.push(succ);
                }
            }

            return Some(node);
        }
        None
    }
}

/// A breadth first search (BFS) of a graph.
///
/// Using a **Bfs** you can run a traversal over a graph while still retaining
/// mutable access to it, if you use it like the following example:
///
/// ```
/// use petgraph::{Graph, Bfs};
///
/// let mut graph = Graph::<_,()>::new();
/// let a = graph.add_node(0);
///
/// let mut bfs = Bfs::new(&graph, a);
/// while let Some(nx) = bfs.next(&graph) {
///     // we can access `graph` mutably here still
///     graph[nx] += 1;
/// }
///
/// assert_eq!(graph[a], 1);
/// ```
///
/// **Note:** The algorithm may not behave correctly if nodes are removed
/// during iteration. It may not necessarily visit added nodes or edges.
#[derive(Clone)]
pub struct Bfs<N, VM> {
    pub stack: VecDeque<N>,
    pub discovered: VM,
}

impl<G: Visitable> Bfs<G::NodeId, G::Map> where
    G::NodeId: Clone,
{
    /// Create a new **Bfs**, using the graph's visitor map, and put **start**
    /// in the stack of nodes to visit.
    pub fn new(graph: &G, start: G::NodeId) -> Self
    {
        let mut discovered = graph.visit_map();
        discovered.visit(start.clone());
        let mut stack = VecDeque::new();
        stack.push_front(start.clone());
        Bfs {
            stack: stack,
            discovered: discovered,
        }
    }
}

impl<N, VM> Bfs<N, VM> where
    N: Clone,
    VM: VisitMap<N>
{
    /// Return the next node in the dfs, or **None** if the traversal is done.
    pub fn next<'a, G>(&mut self, graph: &'a G) -> Option<N> where
        G: Graphlike<NodeId=N>,
        G: for<'b> NeighborIter<'b>,
    {
        while let Some(node) = self.stack.pop_front() {
            for succ in graph.neighbors(node.clone()) {
                if self.discovered.visit(succ.clone()) {
                    self.stack.push_back(succ);
                }
            }

            return Some(node);
        }
        None
    }

}

/*
pub struct Visitor<G> where
    G: Visitable,
{
    stack: Vec<<G as Graphlike>::NodeId>,
    discovered: <G as Visitable>::Map,
}

pub fn visitor<G>(graph: &G, start: <G as Graphlike>::NodeId) -> Visitor<G> where
    G: Visitable
{
    Visitor{
        stack: vec![start],
        discovered: graph.visit_map(),
    }
}
*/

<|endoftext|>


<commit_before>use std::io::{stdout, Error, LineWriter, Write};
use vm::bytecode::{disassemble_instruction, BinaryOp, Chunk, OpCode, Value};

#[derive(Debug)]
pub enum RuntimeError {
    TracingError(Error),
    StackUnderflow,
    // The following out of bound errors have been found using
    // property based testing.
    // Unless I can statically determine that a chunk it is well-formed
    // they should be there, with a runtime penalty of bound checking
    // at every instruction
    InstructionOutOfBound,
    ValueOutOfBound,
}

struct Vm<'a> {
    chunk: &'a Chunk,
    program_counter: usize,
    stack: Vec<Value>,
}

impl<'a> Vm<'a> {
    fn new(chunk: &'a Chunk) -> Vm<'a> {
        Vm {
            chunk: chunk,
            program_counter: 0,
            stack: vec![],
        }
    }

    fn pop(&mut self) -> Result<Value, RuntimeError> {
        if let Some(value) = self.stack.pop() {
            Ok(value)
        } else {
            Err(RuntimeError::StackUnderflow)
        }
    }

    /// Interprets the next instruction.
    /// The execution of this function have some side effects including:
    ///  * update of the program counter to have it point to the next
    ///    instruction
    ///  * mutate the state of the stack
    ///
    /// This function true if there are other instructions left to execute
    /// or false if we're done interpreting the chunk.
    fn interpret_next(&mut self) -> Result<bool, RuntimeError> {
        self.program_counter += 1;
        if self.program_counter >= self.chunk.instruction_count(){
            return Err(RuntimeError::InstructionOutOfBound)
        }
        match self.chunk.get(self.program_counter - 1) {
            OpCode::Return => {
                let value = self.stack.pop();
                println!{"{:?}", value};
                // Temporarily changed the meaning
                return Ok(false);
            }
            OpCode::Constant(offset) =>{
                if offset >= self.chunk.values_count(){
                    return Err(RuntimeError::ValueOutOfBound)
                }
                self.stack.push(self.chunk.get_value(offset))
            },
            OpCode::Negate => {
                let op = try!(self.pop());
                self.stack.push(-op);
            }
            OpCode::Binary(ref operator) => {
                // Note the order!
                // Op2 is the topmost element of the stack,
                // Op1 is the second topmost element
                let op2 = try!(self.pop());
                let op1 = try!(self.pop());
                self.stack.push(match operator {
                    &BinaryOp::Add => op1 + op2,
                    &BinaryOp::Subtract => op1 - op2,
                    &BinaryOp::Multiply => op1 * op2,
                    &BinaryOp::Divide => op1 / op2,
                })
            }
        };
        Ok(true)
    }
    fn trace<T>(&mut self, out: &mut LineWriter<T>) -> Result<(), Error>
    where
        T: Write,
    {
        try!(write!(out, "Stack: "));
        for value in self.stack.iter() {
            try!(write!(out, "[ {} ]", value));
        }
        try!(writeln!(out));
        disassemble_instruction(&self.chunk.get(self.program_counter), self.chunk, out)
    }
}

pub fn interpret(chunk: &Chunk) -> Result<(), RuntimeError> {
    let mut vm = Vm::new(chunk);
    while try!{vm.interpret_next()} {}
    Ok(())
}

pub fn trace(chunk: &Chunk) -> Result<(), RuntimeError> {
    let mut vm = Vm::new(chunk);
    // Destination of the trace output
    let stdout = stdout();
    let handle = stdout.lock();
    let mut writer = LineWriter::new(handle);
    while {
        try!{vm.trace(&mut writer).map_err(RuntimeError::TracingError)};
        try!{vm.interpret_next()}
    } {}
    Ok(())
}

#[cfg(test)]
mod tests {
    use vm::*;
    use vm::bytecode::*;
    use proptest::prelude::*;
    use proptest::collection::*;
    use proptest::num::*;

    fn arb_constants(max_constants : usize) -> VecStrategy<f64::Any>{
        prop::collection::vec(any::<Value>(), 0..max_constants)
    }

    fn arb_instruction(max_offset : usize) -> BoxedStrategy<OpCode> {
        (0..max_offset)
        .prop_map(|offset| OpCode::Constant(offset))
        .boxed()
    }

    fn arb_instructions(max_offset : usize, max_instructions : usize) -> VecStrategy<BoxedStrategy<OpCode>>{
        prop::collection::vec(arb_instruction(max_offset), 0..max_instructions)
    }

    prop_compose!{
        fn arb_chunk(max_offset : usize, max_instructions : usize)
            (constants in arb_constants(max_offset),
             instructions in arb_instructions(max_offset, max_instructions)) -> Chunk{
            let mut chunk = Chunk::new();
            for constant in constants{
                let _offset = chunk.add_constant(constant);
            }
            let mut line = 0;
            for instruction in instructions{
                chunk.add_instruction(instruction, line);
                line = line + 1;
            }
            chunk
        }
    }

    proptest! {
    #[test]
    fn doesnt_crash(ref chunk in arb_chunk(10, 20)) {
        let _ = vm::interpret(chunk);
    }
    }
}<commit_msg>Property based testing for all the operations implemented so far<commit_after>use std::io::{stdout, Error, LineWriter, Write};
use vm::bytecode::{disassemble_instruction, BinaryOp, Chunk, OpCode, Value};

#[derive(Debug)]
pub enum RuntimeError {
    TracingError(Error),
    StackUnderflow,
    // The following out of bound errors have been found using
    // property based testing.
    // Unless I can statically determine that a chunk it is well-formed
    // they should be there, with a runtime penalty of bound checking
    // at every instruction
    InstructionOutOfBound,
    ValueOutOfBound,
}

struct Vm<'a> {
    chunk: &'a Chunk,
    program_counter: usize,
    stack: Vec<Value>,
}

impl<'a> Vm<'a> {
    fn new(chunk: &'a Chunk) -> Vm<'a> {
        Vm {
            chunk: chunk,
            program_counter: 0,
            stack: vec![],
        }
    }

    fn pop(&mut self) -> Result<Value, RuntimeError> {
        if let Some(value) = self.stack.pop() {
            Ok(value)
        } else {
            Err(RuntimeError::StackUnderflow)
        }
    }

    /// Interprets the next instruction.
    /// The execution of this function have some side effects including:
    ///  * update of the program counter to have it point to the next
    ///    instruction
    ///  * mutate the state of the stack
    ///
    /// This function true if there are other instructions left to execute
    /// or false if we're done interpreting the chunk.
    fn interpret_next(&mut self) -> Result<bool, RuntimeError> {
        self.program_counter += 1;
        if self.program_counter >= self.chunk.instruction_count(){
            return Err(RuntimeError::InstructionOutOfBound)
        }
        match self.chunk.get(self.program_counter - 1) {
            OpCode::Return => {
                let value = self.stack.pop();
                println!{"{:?}", value};
                // Temporarily changed the meaning
                return Ok(false);
            }
            OpCode::Constant(offset) =>{
                if offset >= self.chunk.values_count(){
                    return Err(RuntimeError::ValueOutOfBound)
                }
                self.stack.push(self.chunk.get_value(offset))
            },
            OpCode::Negate => {
                let op = try!(self.pop());
                self.stack.push(-op);
            }
            OpCode::Binary(ref operator) => {
                // Note the order!
                // Op2 is the topmost element of the stack,
                // Op1 is the second topmost element
                let op2 = try!(self.pop());
                let op1 = try!(self.pop());
                self.stack.push(match operator {
                    &BinaryOp::Add => op1 + op2,
                    &BinaryOp::Subtract => op1 - op2,
                    &BinaryOp::Multiply => op1 * op2,
                    &BinaryOp::Divide => op1 / op2,
                })
            }
        };
        Ok(true)
    }
    fn trace<T>(&mut self, out: &mut LineWriter<T>) -> Result<(), Error>
    where
        T: Write,
    {
        try!(write!(out, "Stack: "));
        for value in self.stack.iter() {
            try!(write!(out, "[ {} ]", value));
        }
        try!(writeln!(out));
        disassemble_instruction(&self.chunk.get(self.program_counter), self.chunk, out)
    }
}

pub fn interpret(chunk: &Chunk) -> Result<(), RuntimeError> {
    let mut vm = Vm::new(chunk);
    while try!{vm.interpret_next()} {}
    Ok(())
}

pub fn trace(chunk: &Chunk) -> Result<(), RuntimeError> {
    let mut vm = Vm::new(chunk);
    // Destination of the trace output
    let stdout = stdout();
    let handle = stdout.lock();
    let mut writer = LineWriter::new(handle);
    while {
        try!{vm.trace(&mut writer).map_err(RuntimeError::TracingError)};
        try!{vm.interpret_next()}
    } {}
    Ok(())
}

#[cfg(test)]
mod tests {
    use vm::*;
    use vm::bytecode::*;
    use proptest::strategy::*;
    use proptest::prelude::*;
    use proptest::collection::*;
    use proptest::num::*;

    fn arb_constants(max_constants : usize) -> VecStrategy<f64::Any>{
        prop::collection::vec(any::<Value>(), 0..max_constants)
    }

    fn arb_instruction(max_offset: usize) -> BoxedStrategy<OpCode> {
        prop_oneof![
        (0..max_offset).prop_map(OpCode::Constant),
        Just(OpCode::Return),
        Just(OpCode::Negate),
        prop_oneof![
                Just(BinaryOp::Add),
                Just(BinaryOp::Subtract),
                Just(BinaryOp::Multiply),
                Just(BinaryOp::Divide),
            ].prop_map(OpCode::Binary)
        ]
        .boxed()
    }

    fn arb_instructions(max_offset : usize, max_instructions : usize) -> VecStrategy<BoxedStrategy<OpCode>>{
        prop::collection::vec(arb_instruction(max_offset), 0..max_instructions)
    }

    prop_compose!{
        fn arb_chunk(max_offset : usize, max_instructions : usize)
            (constants in arb_constants(max_offset),
             instructions in arb_instructions(max_offset, max_instructions)) -> Chunk{
            let mut chunk = Chunk::new();
            for constant in constants{
                let _offset = chunk.add_constant(constant);
            }
            let mut line = 0;
            for instruction in instructions{
                chunk.add_instruction(instruction, line);
                line = line + 1;
            }
            chunk
        }
    }

    proptest! {
    #[test]
    fn doesnt_crash(ref chunk in arb_chunk(10, 20)) {
        let _ = vm::interpret(chunk);
    }
    }
}<|endoftext|>


<commit_before>#[doc(
    brief = "Attribute parsing",
    desc =
    "The attribute parser provides methods for pulling documentation out of \
     an AST's attributes."
)];

import rustc::syntax::ast;
import rustc::front::attr;
import core::tuple;

export crate_attrs, mod_attrs, fn_attrs, arg_attrs,
       const_attrs, enum_attrs, variant_attrs, res_attrs;
export parse_crate, parse_mod, parse_fn, parse_const,
       parse_enum, parse_variant, parse_res;

type crate_attrs = {
    name: option<str>
};

type mod_attrs = {
    brief: option<str>,
    desc: option<str>
};

type fn_attrs = {
    brief: option<str>,
    desc: option<str>,
    args: [arg_attrs],
    return: option<str>,
    failure: option<str>
};

type arg_attrs = {
    name: str,
    desc: str
};

type const_attrs = {
    brief: option<str>,
    desc: option<str>
};

type enum_attrs = {
    brief: option<str>,
    desc: option<str>
};

type variant_attrs = {
    desc: option<str>
};

type res_attrs = {
    brief: option<str>,
    desc: option<str>,
    args: [arg_attrs]
};

#[cfg(test)]
mod test {

    fn parse_attributes(source: str) -> [ast::attribute] {
        import rustc::syntax::parse::parser;
        // FIXME: Uncommenting this results in rustc bugs
        //import rustc::syntax::codemap;
        import rustc::driver::diagnostic;

        let cm = rustc::syntax::codemap::new_codemap();
        let handler = diagnostic::mk_handler(none);
        let parse_sess = @{
            cm: cm,
            mutable next_id: 0,
            span_diagnostic: diagnostic::mk_span_handler(handler, cm),
            mutable chpos: 0u,
            mutable byte_pos: 0u
        };
        let parser = parser::new_parser_from_source_str(
            parse_sess, [], "-", @source);

        parser::parse_outer_attributes(parser)
    }
}

fn doc_meta(
    attrs: [ast::attribute]
) -> option<@ast::meta_item> {

    #[doc =
      "Given a vec of attributes, extract the meta_items contained in the \
       doc attribute"];

    let doc_attrs = attr::find_attrs_by_name(attrs, "doc");
    let doc_metas = attr::attr_metas(doc_attrs);
    if vec::is_not_empty(doc_metas) {
        if vec::len(doc_metas) != 1u {
            #warn("ignoring %u doc attributes", vec::len(doc_metas) - 1u);
        }
        some(doc_metas[0])
    } else {
        none
    }
}

fn parse_crate(attrs: [ast::attribute]) -> crate_attrs {
    let link_metas = attr::find_linkage_metas(attrs);

    {
        name: attr::meta_item_value_from_list(link_metas, "name")
    }
}

#[test]
fn should_extract_crate_name_from_link_attribute() {
    let source = "#[link(name = \"snuggles\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_crate(attrs);
    assert attrs.name == some("snuggles");
}

#[test]
fn should_not_extract_crate_name_if_no_link_attribute() {
    let source = "";
    let attrs = test::parse_attributes(source);
    let attrs = parse_crate(attrs);
    assert attrs.name == none;
}

#[test]
fn should_not_extract_crate_name_if_no_name_value_in_link_attribute() {
    let source = "#[link(whatever)]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_crate(attrs);
    assert attrs.name == none;
}

fn parse_basic(
    attrs: [ast::attribute]
) -> {
    brief: option<str>,
    desc: option<str>
} {
    parse_short_doc_or(
        attrs,
        {|desc|
            {
                brief: none,
                desc: desc
            }
        },
        {|_items, brief, desc|
            {
                brief: brief,
                desc: desc
            }
        }
    )
}

fn parse_mod(attrs: [ast::attribute]) -> mod_attrs {
    parse_basic(attrs)
}

#[test]
fn parse_mod_should_handle_undocumented_mods() {
    let source = "";
    let attrs = test::parse_attributes(source);
    let attrs = parse_mod(attrs);
    assert attrs.brief == none;
    assert attrs.desc == none;
}

#[test]
fn parse_mod_should_parse_simple_doc_attributes() {
    let source = "#[doc = \"basic\"]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_mod(attrs);
    assert attrs.desc == some("basic");
}

#[test]
fn parse_mod_should_parse_the_brief_description() {
    let source = "#[doc(brief = \"short\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_mod(attrs);
    assert attrs.brief == some("short");
}

#[test]
fn parse_mod_should_parse_the_long_description() {
    let source = "#[doc(desc = \"description\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_mod(attrs);
    assert attrs.desc == some("description");
}

fn parse_short_doc_or<T>(
    attrs: [ast::attribute],
    handle_short: fn&(
        short_desc: option<str>
    ) -> T,
    parse_long: fn&(
        doc_items: [@ast::meta_item],
        brief: option<str>,
        desc: option<str>
    ) -> T
) -> T {
    alt doc_meta(attrs) {
      some(meta) {
        alt attr::get_meta_item_value_str(meta) {
          some(desc) { handle_short(some(desc)) }
          none {
            alt attr::get_meta_item_list(meta) {
              some(list) {
                let brief = attr::meta_item_value_from_list(list, "brief");
                let desc = attr::meta_item_value_from_list(list, "desc");
                parse_long(list, brief, desc)
              }
              none {
                handle_short(none)
              }
            }
          }
        }
      }
      none {
        handle_short(none)
      }
    }
}

fn parse_fn(
    attrs: [ast::attribute]
) -> fn_attrs {

    parse_short_doc_or(
        attrs,
        {|desc|
            {
                brief: none,
                desc: desc,
                args: [],
                return: none,
                failure: none
            }
        },
        parse_fn_long_doc
    )
}

fn parse_fn_long_doc(
    items: [@ast::meta_item],
    brief: option<str>,
    desc: option<str>
) -> fn_attrs {
    let return = attr::meta_item_value_from_list(items, "return");
    let failure = attr::meta_item_value_from_list(items, "failure");
    let args = parse_args(items);

    {
        brief: brief,
        desc: desc,
        args: args,
        return: return,
        failure: failure
    }
}

fn parse_args(items: [@ast::meta_item]) -> [arg_attrs] {
    alt attr::meta_item_list_from_list(items, "args") {
      some(items) {
        vec::filter_map(items) {|item|
            option::map(attr::name_value_str_pair(item)) { |pair|
                {
                    name: tuple::first(pair),
                    desc: tuple::second(pair)
                }
            }
        }
      }
      none { [] }
    }
}

#[test]
fn parse_fn_should_handle_undocumented_functions() {
    let source = "";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.brief == none;
    assert attrs.desc == none;
    assert attrs.return == none;
    assert vec::len(attrs.args) == 0u;
}

#[test]
fn parse_fn_should_parse_simple_doc_attributes() {
    let source = "#[doc = \"basic\"]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.desc == some("basic");
}

#[test]
fn parse_fn_should_parse_the_brief_description() {
    let source = "#[doc(brief = \"short\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.brief == some("short");
}

#[test]
fn parse_fn_should_parse_the_long_description() {
    let source = "#[doc(desc = \"description\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.desc == some("description");
}

#[test]
fn parse_fn_should_parse_the_return_value_description() {
    let source = "#[doc(return = \"return value\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.return == some("return value");
}

#[test]
fn parse_fn_should_parse_the_argument_descriptions() {
    let source = "#[doc(args(a = \"arg a\", b = \"arg b\"))]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.args[0] == {name: "a", desc: "arg a"};
    assert attrs.args[1] == {name: "b", desc: "arg b"};
}

#[test]
fn parse_fn_should_parse_failure_conditions() {
    let source = "#[doc(failure = \"it's the fail\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.failure == some("it's the fail");
}

fn parse_const(attrs: [ast::attribute]) -> const_attrs {
    parse_basic(attrs)
}

#[test]
fn should_parse_const_short_doc() {
    let source = "#[doc = \"description\"]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_const(attrs);
    assert attrs.desc == some("description");
}

#[test]
fn should_parse_const_long_doc() {
    let source = "#[doc(brief = \"a\", desc = \"b\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_const(attrs);
    assert attrs.brief == some("a");
    assert attrs.desc == some("b");
}

fn parse_enum(attrs: [ast::attribute]) -> enum_attrs {
    parse_basic(attrs)
}

#[test]
fn should_parse_enum_short_doc() {
    let source = "#[doc = \"description\"]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_enum(attrs);
    assert attrs.desc == some("description");
}

#[test]
fn should_parse_enum_long_doc() {
    let source = "#[doc(brief = \"a\", desc = \"b\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_enum(attrs);
    assert attrs.brief == some("a");
    assert attrs.desc == some("b");
}

fn parse_variant(attrs: [ast::attribute]) -> variant_attrs {
    parse_short_doc_or(
        attrs,
        {|desc|
            {
                desc: desc
            }
        },
        {|_items, brief, desc|
            if option::is_some(brief) && option::is_some(desc) {
                // FIXME: Warn about dropping brief description
            }

            {
                // Prefer desc over brief
                desc: option::maybe(brief, desc, {|s| some(s) })
            }
        }
    )
}

#[test]
fn should_parse_variant_short_doc() {
    let source = "#[doc = \"a\"]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_variant(attrs);
    assert attrs.desc == some("a");
}

#[test]
fn should_parse_variant_brief_doc() {
    let source = "#[doc(brief = \"a\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_variant(attrs);
    assert attrs.desc == some("a");
}

#[test]
fn should_parse_variant_long_doc() {
    let source = "#[doc(desc = \"a\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_variant(attrs);
    assert attrs.desc == some("a");
}

fn parse_res(
    attrs: [ast::attribute]
) -> res_attrs {

    parse_short_doc_or(
        attrs,
        {|desc|
            {
                brief: none,
                desc: desc,
                args: []
            }
        },
        parse_res_long_doc
    )
}

fn parse_res_long_doc(
    items: [@ast::meta_item],
    brief: option<str>,
    desc: option<str>
) -> res_attrs {
    {
        brief: brief,
        desc: desc,
        args: parse_args(items)
    }
}

#[test]
fn should_parse_resource_short_desc() {
    let source = "#[doc = \"a\"]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_res(attrs);
    assert attrs.desc == some("a");
}

#[test]
fn should_parse_resource_long_desc() {
    let source = "#[doc(brief = \"a\", desc = \"b\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_res(attrs);
    assert attrs.brief == some("a");
    assert attrs.desc == some("b");
}

#[test]
fn shoulde_parse_resource_arg() {
    let source = "#[doc(args(a = \"b\"))]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_res(attrs);
    assert attrs.args[0].name == "a";
    assert attrs.args[0].desc == "b";
}<commit_msg>rustdoc: Add attribute parsing for ifaces and methods<commit_after>#[doc(
    brief = "Attribute parsing",
    desc =
    "The attribute parser provides methods for pulling documentation out of \
     an AST's attributes."
)];

import rustc::syntax::ast;
import rustc::front::attr;
import core::tuple;

export crate_attrs, mod_attrs, fn_attrs, arg_attrs,
       const_attrs, enum_attrs, variant_attrs, res_attrs,
       iface_attrs, method_attrs;
export parse_crate, parse_mod, parse_fn, parse_const,
       parse_enum, parse_variant, parse_res,
       parse_iface, parse_method;

type crate_attrs = {
    name: option<str>
};

type mod_attrs = {
    brief: option<str>,
    desc: option<str>
};

type fn_attrs = {
    brief: option<str>,
    desc: option<str>,
    args: [arg_attrs],
    return: option<str>,
    failure: option<str>
};

type arg_attrs = {
    name: str,
    desc: str
};

type const_attrs = {
    brief: option<str>,
    desc: option<str>
};

type enum_attrs = {
    brief: option<str>,
    desc: option<str>
};

type variant_attrs = {
    desc: option<str>
};

type res_attrs = {
    brief: option<str>,
    desc: option<str>,
    args: [arg_attrs]
};

type iface_attrs = {
    brief: option<str>,
    desc: option<str>
};

type method_attrs = fn_attrs;

#[cfg(test)]
mod test {

    fn parse_attributes(source: str) -> [ast::attribute] {
        import rustc::syntax::parse::parser;
        // FIXME: Uncommenting this results in rustc bugs
        //import rustc::syntax::codemap;
        import rustc::driver::diagnostic;

        let cm = rustc::syntax::codemap::new_codemap();
        let handler = diagnostic::mk_handler(none);
        let parse_sess = @{
            cm: cm,
            mutable next_id: 0,
            span_diagnostic: diagnostic::mk_span_handler(handler, cm),
            mutable chpos: 0u,
            mutable byte_pos: 0u
        };
        let parser = parser::new_parser_from_source_str(
            parse_sess, [], "-", @source);

        parser::parse_outer_attributes(parser)
    }
}

fn doc_meta(
    attrs: [ast::attribute]
) -> option<@ast::meta_item> {

    #[doc =
      "Given a vec of attributes, extract the meta_items contained in the \
       doc attribute"];

    let doc_attrs = attr::find_attrs_by_name(attrs, "doc");
    let doc_metas = attr::attr_metas(doc_attrs);
    if vec::is_not_empty(doc_metas) {
        if vec::len(doc_metas) != 1u {
            #warn("ignoring %u doc attributes", vec::len(doc_metas) - 1u);
        }
        some(doc_metas[0])
    } else {
        none
    }
}

fn parse_crate(attrs: [ast::attribute]) -> crate_attrs {
    let link_metas = attr::find_linkage_metas(attrs);

    {
        name: attr::meta_item_value_from_list(link_metas, "name")
    }
}

#[test]
fn should_extract_crate_name_from_link_attribute() {
    let source = "#[link(name = \"snuggles\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_crate(attrs);
    assert attrs.name == some("snuggles");
}

#[test]
fn should_not_extract_crate_name_if_no_link_attribute() {
    let source = "";
    let attrs = test::parse_attributes(source);
    let attrs = parse_crate(attrs);
    assert attrs.name == none;
}

#[test]
fn should_not_extract_crate_name_if_no_name_value_in_link_attribute() {
    let source = "#[link(whatever)]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_crate(attrs);
    assert attrs.name == none;
}

fn parse_basic(
    attrs: [ast::attribute]
) -> {
    brief: option<str>,
    desc: option<str>
} {
    parse_short_doc_or(
        attrs,
        {|desc|
            {
                brief: none,
                desc: desc
            }
        },
        {|_items, brief, desc|
            {
                brief: brief,
                desc: desc
            }
        }
    )
}

fn parse_mod(attrs: [ast::attribute]) -> mod_attrs {
    parse_basic(attrs)
}

#[test]
fn parse_mod_should_handle_undocumented_mods() {
    let source = "";
    let attrs = test::parse_attributes(source);
    let attrs = parse_mod(attrs);
    assert attrs.brief == none;
    assert attrs.desc == none;
}

#[test]
fn parse_mod_should_parse_simple_doc_attributes() {
    let source = "#[doc = \"basic\"]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_mod(attrs);
    assert attrs.desc == some("basic");
}

#[test]
fn parse_mod_should_parse_the_brief_description() {
    let source = "#[doc(brief = \"short\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_mod(attrs);
    assert attrs.brief == some("short");
}

#[test]
fn parse_mod_should_parse_the_long_description() {
    let source = "#[doc(desc = \"description\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_mod(attrs);
    assert attrs.desc == some("description");
}

fn parse_short_doc_or<T>(
    attrs: [ast::attribute],
    handle_short: fn&(
        short_desc: option<str>
    ) -> T,
    parse_long: fn&(
        doc_items: [@ast::meta_item],
        brief: option<str>,
        desc: option<str>
    ) -> T
) -> T {
    alt doc_meta(attrs) {
      some(meta) {
        alt attr::get_meta_item_value_str(meta) {
          some(desc) { handle_short(some(desc)) }
          none {
            alt attr::get_meta_item_list(meta) {
              some(list) {
                let brief = attr::meta_item_value_from_list(list, "brief");
                let desc = attr::meta_item_value_from_list(list, "desc");
                parse_long(list, brief, desc)
              }
              none {
                handle_short(none)
              }
            }
          }
        }
      }
      none {
        handle_short(none)
      }
    }
}

fn parse_fn(
    attrs: [ast::attribute]
) -> fn_attrs {

    parse_short_doc_or(
        attrs,
        {|desc|
            {
                brief: none,
                desc: desc,
                args: [],
                return: none,
                failure: none
            }
        },
        parse_fn_long_doc
    )
}

fn parse_fn_long_doc(
    items: [@ast::meta_item],
    brief: option<str>,
    desc: option<str>
) -> fn_attrs {
    let return = attr::meta_item_value_from_list(items, "return");
    let failure = attr::meta_item_value_from_list(items, "failure");
    let args = parse_args(items);

    {
        brief: brief,
        desc: desc,
        args: args,
        return: return,
        failure: failure
    }
}

fn parse_args(items: [@ast::meta_item]) -> [arg_attrs] {
    alt attr::meta_item_list_from_list(items, "args") {
      some(items) {
        vec::filter_map(items) {|item|
            option::map(attr::name_value_str_pair(item)) { |pair|
                {
                    name: tuple::first(pair),
                    desc: tuple::second(pair)
                }
            }
        }
      }
      none { [] }
    }
}

#[test]
fn parse_fn_should_handle_undocumented_functions() {
    let source = "";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.brief == none;
    assert attrs.desc == none;
    assert attrs.return == none;
    assert vec::len(attrs.args) == 0u;
}

#[test]
fn parse_fn_should_parse_simple_doc_attributes() {
    let source = "#[doc = \"basic\"]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.desc == some("basic");
}

#[test]
fn parse_fn_should_parse_the_brief_description() {
    let source = "#[doc(brief = \"short\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.brief == some("short");
}

#[test]
fn parse_fn_should_parse_the_long_description() {
    let source = "#[doc(desc = \"description\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.desc == some("description");
}

#[test]
fn parse_fn_should_parse_the_return_value_description() {
    let source = "#[doc(return = \"return value\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.return == some("return value");
}

#[test]
fn parse_fn_should_parse_the_argument_descriptions() {
    let source = "#[doc(args(a = \"arg a\", b = \"arg b\"))]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.args[0] == {name: "a", desc: "arg a"};
    assert attrs.args[1] == {name: "b", desc: "arg b"};
}

#[test]
fn parse_fn_should_parse_failure_conditions() {
    let source = "#[doc(failure = \"it's the fail\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.failure == some("it's the fail");
}

fn parse_const(attrs: [ast::attribute]) -> const_attrs {
    parse_basic(attrs)
}

#[test]
fn should_parse_const_short_doc() {
    let source = "#[doc = \"description\"]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_const(attrs);
    assert attrs.desc == some("description");
}

#[test]
fn should_parse_const_long_doc() {
    let source = "#[doc(brief = \"a\", desc = \"b\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_const(attrs);
    assert attrs.brief == some("a");
    assert attrs.desc == some("b");
}

fn parse_enum(attrs: [ast::attribute]) -> enum_attrs {
    parse_basic(attrs)
}

#[test]
fn should_parse_enum_short_doc() {
    let source = "#[doc = \"description\"]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_enum(attrs);
    assert attrs.desc == some("description");
}

#[test]
fn should_parse_enum_long_doc() {
    let source = "#[doc(brief = \"a\", desc = \"b\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_enum(attrs);
    assert attrs.brief == some("a");
    assert attrs.desc == some("b");
}

fn parse_variant(attrs: [ast::attribute]) -> variant_attrs {
    parse_short_doc_or(
        attrs,
        {|desc|
            {
                desc: desc
            }
        },
        {|_items, brief, desc|
            if option::is_some(brief) && option::is_some(desc) {
                // FIXME: Warn about dropping brief description
            }

            {
                // Prefer desc over brief
                desc: option::maybe(brief, desc, {|s| some(s) })
            }
        }
    )
}

#[test]
fn should_parse_variant_short_doc() {
    let source = "#[doc = \"a\"]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_variant(attrs);
    assert attrs.desc == some("a");
}

#[test]
fn should_parse_variant_brief_doc() {
    let source = "#[doc(brief = \"a\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_variant(attrs);
    assert attrs.desc == some("a");
}

#[test]
fn should_parse_variant_long_doc() {
    let source = "#[doc(desc = \"a\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_variant(attrs);
    assert attrs.desc == some("a");
}

fn parse_res(
    attrs: [ast::attribute]
) -> res_attrs {

    parse_short_doc_or(
        attrs,
        {|desc|
            {
                brief: none,
                desc: desc,
                args: []
            }
        },
        parse_res_long_doc
    )
}

fn parse_res_long_doc(
    items: [@ast::meta_item],
    brief: option<str>,
    desc: option<str>
) -> res_attrs {
    {
        brief: brief,
        desc: desc,
        args: parse_args(items)
    }
}

#[test]
fn should_parse_resource_short_desc() {
    let source = "#[doc = \"a\"]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_res(attrs);
    assert attrs.desc == some("a");
}

#[test]
fn should_parse_resource_long_desc() {
    let source = "#[doc(brief = \"a\", desc = \"b\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_res(attrs);
    assert attrs.brief == some("a");
    assert attrs.desc == some("b");
}

#[test]
fn shoulde_parse_resource_arg() {
    let source = "#[doc(args(a = \"b\"))]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_res(attrs);
    assert attrs.args[0].name == "a";
    assert attrs.args[0].desc == "b";
}

fn parse_iface(attrs: [ast::attribute]) -> iface_attrs {
    parse_basic(attrs)
}

fn parse_method(attrs: [ast::attribute]) -> method_attrs {
    parse_fn(attrs)
}
<|endoftext|>


<commit_before>/*!
Functionality for declaring Objective-C classes.

Classes can be declared using the `ClassDecl` struct. Instance variables and
methods can then be added before the class is ultimately registered.

# Example

The following example demonstrates declaring a class named `MyNumber` that has
one ivar, a `u32` named `_number` and a `number` method that returns it:

```
# #[macro_use] extern crate objc;
# use objc::declare::ClassDecl;
# use objc::runtime::{Class, Object, Sel};
# fn main() {
let superclass = Class::get("NSObject").unwrap();
let mut decl = ClassDecl::new(superclass, "MyNumber").unwrap();

// Add an instance variable
decl.add_ivar::<u32>("_number");

// Add an ObjC method for getting the number
extern fn my_number_get(this: &Object, _cmd: Sel) -> u32 {
    unsafe { *this.get_ivar("_number") }
}
unsafe {
    decl.add_method(sel!(number),
        my_number_get as extern fn(&Object, Sel) -> u32);
}

decl.register();
# }
```
*/

use std::error::Error;
use std::ffi::CString;
use std::fmt;
use std::mem;
use libc::size_t;

use {Encode, Encoding, Message};
use runtime::{Class, Imp, NO, Object, Sel, self};

/// An error returned from `IntoMethodImp::into_imp` to indicate that a
/// selector and function accept unequal numbers of arguments.
#[derive(Clone, PartialEq, Debug)]
pub struct UnequalArgsError {
    sel_args: usize,
    fn_args: usize,
}

impl fmt::Display for UnequalArgsError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Selector accepts {} arguments, but function accepts {}",
            self.sel_args, self.fn_args)
    }
}

impl Error for UnequalArgsError {
    fn description(&self) -> &str {
        "Selector and function accept unequal numbers of arguments"
    }
}

/// Types that can be used as the implementation of an Objective-C method.
pub trait IntoMethodImp {
    /// The callee type of the method.
    type Callee: Message;
    /// The return type of the method.
    type Ret: Encode;

    /// Returns the type encodings of Self's arguments.
    fn argument_encodings() -> Box<[Encoding]>;

    /// Consumes self to create a method implementation for the given selector.
    ///
    /// Returns an error if self and the selector do not accept the same number
    /// of arguments.
    fn into_imp(self, sel: Sel) -> Result<Imp, UnequalArgsError>;
}

macro_rules! count_idents {
    () => (0);
    ($a:ident) => (1);
    ($a:ident, $($b:ident),+) => (1 + count_idents!($($b),*));
}

macro_rules! method_decl_impl {
    (-$s:ident, $sp:ty, $($t:ident),*) => (
        impl<$s, R $(, $t)*> IntoMethodImp for extern fn($sp, Sel $(, $t)*) -> R
                where $s: Message, R: Encode $(, $t: Encode)* {
            type Callee = $s;
            type Ret = R;

            fn argument_encodings() -> Box<[Encoding]> {
                Box::new([
                    <*mut Object>::encode(),
                    Sel::encode(),
                    $($t::encode()),*
                ])
            }

            fn into_imp(self, sel: Sel) -> Result<Imp, UnequalArgsError> {
                // Add 2 to the arguments for self and _cmd
                let fn_args = 2 + count_idents!($($t),*);
                let sel_args = 2 + sel.name().chars().filter(|&c| c == ':').count();
                if sel_args == fn_args {
                    unsafe { Ok(mem::transmute(self)) }
                } else {
                    Err(UnequalArgsError { sel_args: sel_args, fn_args: fn_args })
                }
            }
        }
    );
    ($($t:ident),*) => (
        method_decl_impl!(-T, &T, $($t),*);
        method_decl_impl!(-T, &mut T, $($t),*);
    );
}

method_decl_impl!();
method_decl_impl!(A);
method_decl_impl!(A, B);
method_decl_impl!(A, B, C);
method_decl_impl!(A, B, C, D);
method_decl_impl!(A, B, C, D, E);
method_decl_impl!(A, B, C, D, E, F);
method_decl_impl!(A, B, C, D, E, F, G);
method_decl_impl!(A, B, C, D, E, F, G, H);
method_decl_impl!(A, B, C, D, E, F, G, H, I);
method_decl_impl!(A, B, C, D, E, F, G, H, I, J);
method_decl_impl!(A, B, C, D, E, F, G, H, I, J, K);
method_decl_impl!(A, B, C, D, E, F, G, H, I, J, K, L);

fn method_type_encoding<F>() -> CString where F: IntoMethodImp {
    let mut types = F::Ret::encode().as_str().to_string();
    types.extend(F::argument_encodings().iter().map(|e| e.as_str()));
    CString::new(types).unwrap()
}

/// A type for declaring a new class and adding new methods and ivars to it
/// before registering it.
pub struct ClassDecl {
    cls: *mut Class,
}

impl ClassDecl {
    /// Constructs a `ClassDecl` with the given superclass and name.
    /// Returns `None` if the class couldn't be allocated.
    pub fn new(superclass: &Class, name: &str) -> Option<ClassDecl> {
        let name = CString::new(name).unwrap();
        let cls = unsafe {
            runtime::objc_allocateClassPair(superclass, name.as_ptr(), 0)
        };
        if cls.is_null() {
            None
        } else {
            Some(ClassDecl { cls: cls })
        }
    }

    /// Adds a method with the given name and implementation to self.
    /// Panics if the method wasn't sucessfully added
    /// or if the selector and function take different numbers of arguments.
    /// Unsafe because the caller must ensure that the types match those that
    /// are expected when the method is invoked from Objective-C.
    pub unsafe fn add_method<F>(&mut self, sel: Sel, func: F)
            where F: IntoMethodImp<Callee=Object> {
        let types = method_type_encoding::<F>();
        let imp = match func.into_imp(sel) {
            Ok(imp) => imp,
            Err(err) => panic!("{}", err),
        };

        let success = runtime::class_addMethod(self.cls, sel, imp,
            types.as_ptr());
        assert!(success != NO, "Failed to add method {:?}", sel);
    }

    /// Adds a class method with the given name and implementation to self.
    /// Panics if the method wasn't sucessfully added
    /// or if the selector and function take different numbers of arguments.
    /// Unsafe because the caller must ensure that the types match those that
    /// are expected when the method is invoked from Objective-C.
    pub unsafe fn add_class_method<F>(&mut self, sel: Sel, func: F)
            where F: IntoMethodImp<Callee=Class> {
        let types = method_type_encoding::<F>();
        let imp = match func.into_imp(sel) {
            Ok(imp) => imp,
            Err(err) => panic!("{}", err),
        };

        let cls_obj = self.cls as *const Object;
        let metaclass = runtime::object_getClass(cls_obj) as *mut Class;
        let success = runtime::class_addMethod(metaclass, sel, imp,
            types.as_ptr());
        assert!(success != NO, "Failed to add class method {:?}", sel);
    }

    /// Adds an ivar with type `T` and the provided name to self.
    /// Panics if the ivar wasn't successfully added.
    pub fn add_ivar<T>(&mut self, name: &str) where T: Encode {
        let c_name = CString::new(name).unwrap();
        let encoding = CString::new(T::encode().as_str()).unwrap();
        let size = mem::size_of::<T>() as size_t;
        let align = mem::align_of::<T>() as u8;
        let success = unsafe {
            runtime::class_addIvar(self.cls, c_name.as_ptr(), size, align,
                encoding.as_ptr())
        };
        assert!(success != NO, "Failed to add ivar {}", name);
    }

    /// Registers self, consuming it and returning a reference to the
    /// newly registered `Class`.
    pub fn register(self) -> &'static Class {
        unsafe {
            let cls = self.cls;
            runtime::objc_registerClassPair(cls);
            // Forget self otherwise the class will be disposed in drop
            mem::forget(self);
            &*cls
        }
    }
}

impl Drop for ClassDecl {
    fn drop(&mut self) {
        unsafe {
            runtime::objc_disposeClassPair(self.cls);
        }
    }
}

#[cfg(test)]
mod tests {
    use runtime::{Object, Sel};
    use test_utils;
    use super::IntoMethodImp;

    #[test]
    fn test_custom_class() {
        // Registering the custom class is in test_utils
        let obj = test_utils::custom_object();
        unsafe {
            let _: () = msg_send![obj, setFoo:13u32];
            let result: u32 = msg_send![obj, foo];
            assert!(result == 13);
        }
    }

    #[test]
    fn test_class_method() {
        let cls = test_utils::custom_class();
        unsafe {
            let result: u32 = msg_send![cls, classFoo];
            assert!(result == 7);
        }
    }

    #[test]
    fn test_mismatched_args() {
        extern fn wrong_num_args_method(_obj: &Object, _cmd: Sel, _a: i32) { }

        let sel = sel!(doSomethingWithFoo:bar:);
        let f: extern fn(&Object, Sel, i32) = wrong_num_args_method;
        let imp = f.into_imp(sel);
        assert!(imp.is_err());
    }
}
<commit_msg>Renamed IntoMethodImp to MethodImplementation.<commit_after>/*!
Functionality for declaring Objective-C classes.

Classes can be declared using the `ClassDecl` struct. Instance variables and
methods can then be added before the class is ultimately registered.

# Example

The following example demonstrates declaring a class named `MyNumber` that has
one ivar, a `u32` named `_number` and a `number` method that returns it:

```
# #[macro_use] extern crate objc;
# use objc::declare::ClassDecl;
# use objc::runtime::{Class, Object, Sel};
# fn main() {
let superclass = Class::get("NSObject").unwrap();
let mut decl = ClassDecl::new(superclass, "MyNumber").unwrap();

// Add an instance variable
decl.add_ivar::<u32>("_number");

// Add an ObjC method for getting the number
extern fn my_number_get(this: &Object, _cmd: Sel) -> u32 {
    unsafe { *this.get_ivar("_number") }
}
unsafe {
    decl.add_method(sel!(number),
        my_number_get as extern fn(&Object, Sel) -> u32);
}

decl.register();
# }
```
*/

use std::error::Error;
use std::ffi::CString;
use std::fmt;
use std::mem;
use libc::size_t;

use {Encode, Encoding, Message};
use runtime::{Class, Imp, NO, Object, Sel, self};

/// An error returned from `MethodImplementation::into_imp` to indicate that a
/// selector and function accept unequal numbers of arguments.
#[derive(Clone, PartialEq, Debug)]
pub struct UnequalArgsError {
    sel_args: usize,
    fn_args: usize,
}

impl fmt::Display for UnequalArgsError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Selector accepts {} arguments, but function accepts {}",
            self.sel_args, self.fn_args)
    }
}

impl Error for UnequalArgsError {
    fn description(&self) -> &str {
        "Selector and function accept unequal numbers of arguments"
    }
}

/// Types that can be used as the implementation of an Objective-C method.
pub trait MethodImplementation {
    /// The callee type of the method.
    type Callee: Message;
    /// The return type of the method.
    type Ret: Encode;

    /// Returns the type encodings of Self's arguments.
    fn argument_encodings() -> Box<[Encoding]>;

    /// Consumes self to create a method implementation for the given selector.
    ///
    /// Returns an error if self and the selector do not accept the same number
    /// of arguments.
    fn into_imp(self, sel: Sel) -> Result<Imp, UnequalArgsError>;
}

macro_rules! count_idents {
    () => (0);
    ($a:ident) => (1);
    ($a:ident, $($b:ident),+) => (1 + count_idents!($($b),*));
}

macro_rules! method_decl_impl {
    (-$s:ident, $sp:ty, $($t:ident),*) => (
        impl<$s, R $(, $t)*> MethodImplementation for extern fn($sp, Sel $(, $t)*) -> R
                where $s: Message, R: Encode $(, $t: Encode)* {
            type Callee = $s;
            type Ret = R;

            fn argument_encodings() -> Box<[Encoding]> {
                Box::new([
                    <*mut Object>::encode(),
                    Sel::encode(),
                    $($t::encode()),*
                ])
            }

            fn into_imp(self, sel: Sel) -> Result<Imp, UnequalArgsError> {
                // Add 2 to the arguments for self and _cmd
                let fn_args = 2 + count_idents!($($t),*);
                let sel_args = 2 + sel.name().chars().filter(|&c| c == ':').count();
                if sel_args == fn_args {
                    unsafe { Ok(mem::transmute(self)) }
                } else {
                    Err(UnequalArgsError { sel_args: sel_args, fn_args: fn_args })
                }
            }
        }
    );
    ($($t:ident),*) => (
        method_decl_impl!(-T, &T, $($t),*);
        method_decl_impl!(-T, &mut T, $($t),*);
    );
}

method_decl_impl!();
method_decl_impl!(A);
method_decl_impl!(A, B);
method_decl_impl!(A, B, C);
method_decl_impl!(A, B, C, D);
method_decl_impl!(A, B, C, D, E);
method_decl_impl!(A, B, C, D, E, F);
method_decl_impl!(A, B, C, D, E, F, G);
method_decl_impl!(A, B, C, D, E, F, G, H);
method_decl_impl!(A, B, C, D, E, F, G, H, I);
method_decl_impl!(A, B, C, D, E, F, G, H, I, J);
method_decl_impl!(A, B, C, D, E, F, G, H, I, J, K);
method_decl_impl!(A, B, C, D, E, F, G, H, I, J, K, L);

fn method_type_encoding<F>() -> CString where F: MethodImplementation {
    let mut types = F::Ret::encode().as_str().to_string();
    types.extend(F::argument_encodings().iter().map(|e| e.as_str()));
    CString::new(types).unwrap()
}

/// A type for declaring a new class and adding new methods and ivars to it
/// before registering it.
pub struct ClassDecl {
    cls: *mut Class,
}

impl ClassDecl {
    /// Constructs a `ClassDecl` with the given superclass and name.
    /// Returns `None` if the class couldn't be allocated.
    pub fn new(superclass: &Class, name: &str) -> Option<ClassDecl> {
        let name = CString::new(name).unwrap();
        let cls = unsafe {
            runtime::objc_allocateClassPair(superclass, name.as_ptr(), 0)
        };
        if cls.is_null() {
            None
        } else {
            Some(ClassDecl { cls: cls })
        }
    }

    /// Adds a method with the given name and implementation to self.
    /// Panics if the method wasn't sucessfully added
    /// or if the selector and function take different numbers of arguments.
    /// Unsafe because the caller must ensure that the types match those that
    /// are expected when the method is invoked from Objective-C.
    pub unsafe fn add_method<F>(&mut self, sel: Sel, func: F)
            where F: MethodImplementation<Callee=Object> {
        let types = method_type_encoding::<F>();
        let imp = match func.into_imp(sel) {
            Ok(imp) => imp,
            Err(err) => panic!("{}", err),
        };

        let success = runtime::class_addMethod(self.cls, sel, imp,
            types.as_ptr());
        assert!(success != NO, "Failed to add method {:?}", sel);
    }

    /// Adds a class method with the given name and implementation to self.
    /// Panics if the method wasn't sucessfully added
    /// or if the selector and function take different numbers of arguments.
    /// Unsafe because the caller must ensure that the types match those that
    /// are expected when the method is invoked from Objective-C.
    pub unsafe fn add_class_method<F>(&mut self, sel: Sel, func: F)
            where F: MethodImplementation<Callee=Class> {
        let types = method_type_encoding::<F>();
        let imp = match func.into_imp(sel) {
            Ok(imp) => imp,
            Err(err) => panic!("{}", err),
        };

        let cls_obj = self.cls as *const Object;
        let metaclass = runtime::object_getClass(cls_obj) as *mut Class;
        let success = runtime::class_addMethod(metaclass, sel, imp,
            types.as_ptr());
        assert!(success != NO, "Failed to add class method {:?}", sel);
    }

    /// Adds an ivar with type `T` and the provided name to self.
    /// Panics if the ivar wasn't successfully added.
    pub fn add_ivar<T>(&mut self, name: &str) where T: Encode {
        let c_name = CString::new(name).unwrap();
        let encoding = CString::new(T::encode().as_str()).unwrap();
        let size = mem::size_of::<T>() as size_t;
        let align = mem::align_of::<T>() as u8;
        let success = unsafe {
            runtime::class_addIvar(self.cls, c_name.as_ptr(), size, align,
                encoding.as_ptr())
        };
        assert!(success != NO, "Failed to add ivar {}", name);
    }

    /// Registers self, consuming it and returning a reference to the
    /// newly registered `Class`.
    pub fn register(self) -> &'static Class {
        unsafe {
            let cls = self.cls;
            runtime::objc_registerClassPair(cls);
            // Forget self otherwise the class will be disposed in drop
            mem::forget(self);
            &*cls
        }
    }
}

impl Drop for ClassDecl {
    fn drop(&mut self) {
        unsafe {
            runtime::objc_disposeClassPair(self.cls);
        }
    }
}

#[cfg(test)]
mod tests {
    use runtime::{Object, Sel};
    use test_utils;
    use super::MethodImplementation;

    #[test]
    fn test_custom_class() {
        // Registering the custom class is in test_utils
        let obj = test_utils::custom_object();
        unsafe {
            let _: () = msg_send![obj, setFoo:13u32];
            let result: u32 = msg_send![obj, foo];
            assert!(result == 13);
        }
    }

    #[test]
    fn test_class_method() {
        let cls = test_utils::custom_class();
        unsafe {
            let result: u32 = msg_send![cls, classFoo];
            assert!(result == 7);
        }
    }

    #[test]
    fn test_mismatched_args() {
        extern fn wrong_num_args_method(_obj: &Object, _cmd: Sel, _a: i32) { }

        let sel = sel!(doSomethingWithFoo:bar:);
        let f: extern fn(&Object, Sel, i32) = wrong_num_args_method;
        let imp = f.into_imp(sel);
        assert!(imp.is_err());
    }
}
<|endoftext|>


<commit_before>extern crate libloading;

use libindy::ErrorCode;

use command_executor::{Command, CommandContext, CommandParams, CommandMetadata, CommandResult};
use commands::get_str_param;

pub mod about_command {
    use super::*;

    command!(CommandMetadata::build("about", "Show about information").finalize());

    fn execute(_ctx: &CommandContext, _params: &CommandParams) -> CommandResult {
        trace!("execute >> _ctx: params: {:?}", _params);

        println_succ!("Hyperledger Indy CLI (https://github.com/hyperledger/indy-sdk)");
        println!();
        println_succ!("This is the official CLI tool for Hyperledger Indy (https://www.hyperledger.org/projects),");
        println_succ!("which provides a distributed-ledger-based foundation for");
        println_succ!("self-sovereign identity (https://sovrin.org/).");
        println!();
        println_succ!("Version: {}", env!("CARGO_PKG_VERSION"));
        println_succ!("Apache License Version 2.0");
        println_succ!("Copyright 2017 Sovrin Foundation");
        println!();

        let res = Ok(());

        trace!("execute << {:?}", res);
        res
    }
}

pub mod show_command {
    use super::*;
    use std::io::Read;
    use std::fs::File;

    command!(CommandMetadata::build("show", "Print the content of text file")
                            .add_main_param("file", "The path to file to show")
                            .add_example("show /home/file.txt")
                            .finalize());

    fn execute(_ctx: &CommandContext, params: &CommandParams) -> CommandResult {
        trace!("execute >> params: {:?}", params);

        let file = get_str_param("file", params).map_err(error_err!())?;

        let mut file = File::open(file)
            .map_err(error_err!())
            .map_err(map_println_err!("Can't read the file"))?;

        let content = {
            let mut s = String::new();
            file.read_to_string(&mut s)
                .map_err(error_err!())
                .map_err(|err| println_err!("Can't read the file: {}", err))?;
            s
        };

        println!("{}", content);
        let res = Ok(());

        trace!("execute << {:?}", res);
        res
    }
}

pub mod prompt_command {
    use super::*;

    command!(CommandMetadata::build("prompt", "Change command prompt")
                            .add_main_param("prompt", "New prompt string")
                            .add_example("prompt new-prompt")
                            .finalize());

    fn execute(ctx: &CommandContext, params: &CommandParams) -> CommandResult {
        trace!("execute >> ctx: {:?}, params: {:?}", ctx, params);

        let prompt = get_str_param("prompt", params).map_err(error_err!())?;

        ctx.set_main_prompt(prompt.to_owned());
        println_succ!("Command prompt has been set to \"{}\"", prompt);
        let res = Ok(());

        trace!("execute << {:?}", res);
        res
    }
}

pub mod load_plugin_command {
    use super::*;

    command!(CommandMetadata::build("load-plugin", "Load plugin in Libindy")
                            .add_required_param("library", "Name of plugin (can be absolute or relative path)")
                            .add_required_param("initializer", "Name of plugin init function")
                            .add_example("load-plugin library=libsovtoken initializer=sovtoken_init")
                            .finalize());

    fn execute(_ctx: &CommandContext, params: &CommandParams) -> CommandResult {
        trace!("execute >> params: {:?}", params);

        let library = get_str_param("library", params).map_err(error_err!())?;
        let initializer = get_str_param("initializer", params).map_err(error_err!())?;

        let res = load_plugin(_ctx, library, initializer)?;

        trace!("execute << {:?}", res);

        Ok(res)
    }
}

pub fn load_plugin(ctx: &CommandContext, library: &str, initializer: &str) -> Result<(), ()> {
    let lib = libloading::Library::new(library)
        .map_err(|_| println_err!("Plugin not found: {:?}", library))?;

    unsafe {
        let init_func: libloading::Symbol<unsafe extern fn() -> ErrorCode> = lib.get(initializer.as_bytes())
            .map_err(|_| println_err!("Init function not found"))?;

        match init_func() {
            ErrorCode::Success => println_succ!("Plugin has been loaded: \"{}\"", library),
            _ => return Err(println_err!("Plugin has not been loaded: \"{}\"", library))
        }
    }

    //TODO think more about behaviour in case of init_func failed
    ctx.add_plugin(library, lib);

    Ok(())
}

pub mod exit_command {
    use super::*;

    command!(CommandMetadata::build("exit", "Exit Indy CLI").finalize());

    fn execute(ctx: &CommandContext, _params: &CommandParams) -> CommandResult {
        trace!("execute >> ctx: {:?}, params: {:?}", ctx, _params);

        ctx.set_exit();
        let res = Ok(());

        trace!("execute << {:?}", res);
        res
    }
}

#[cfg(test)]
#[cfg(feature = "nullpay_plugin")]
pub mod tests {
    use super::*;

    pub const NULL_PAYMENT_METHOD: &'static str = "null";
    pub const NULL_PAYMENT_PLUGIN_INIT_FUNCTION: &'static str = "nullpay_init";

    #[cfg(any(unix))]
    pub const NULL_PAYMENT_PLUGIN: &'static str = "libnullpay.so";
    #[cfg(any(windows))]
    pub const NULL_PAYMENT_PLUGIN: &'static str = "nullpay.dll";

    mod load {
        use super::*;
        use utils::test::TestUtils;

        #[test]
        pub fn load_works() {
            TestUtils::cleanup_storage();
            let ctx = CommandContext::new();

            let cmd = load_plugin_command::new();
            let mut params = CommandParams::new();
            params.insert("library", NULL_PAYMENT_PLUGIN.to_string());
            params.insert("initializer", NULL_PAYMENT_PLUGIN_INIT_FUNCTION.to_string());
            cmd.execute(&ctx, &params).unwrap();

            TestUtils::cleanup_storage();
        }

        #[test]
        pub fn load_works_for_unknown_plugin() {
            let ctx = CommandContext::new();

            let cmd = load_plugin_command::new();
            let mut params = CommandParams::new();
            params.insert("library", "unknown_payment_plugin".to_string());
            params.insert("initializer", NULL_PAYMENT_PLUGIN_INIT_FUNCTION.to_string());
            cmd.execute(&ctx, &params).unwrap_err();
        }

        #[test]
        pub fn load_works_for_unknown_init_function() {
            let ctx = CommandContext::new();

            let cmd = load_plugin_command::new();
            let mut params = CommandParams::new();
            params.insert("library", NULL_PAYMENT_PLUGIN.to_string());
            params.insert("initializer", "unknown_init_function".to_string());
            cmd.execute(&ctx, &params).unwrap_err();
        }
    }

    pub fn load_null_payment_plugin(ctx: &CommandContext) -> () {
        let lib = _load_lib(NULL_PAYMENT_PLUGIN).unwrap();
        unsafe {
            let init_func: libloading::Symbol<unsafe extern fn() -> ErrorCode> = lib.get(NULL_PAYMENT_PLUGIN_INIT_FUNCTION.as_bytes()).unwrap();
            init_func();
        }
        ctx.add_plugin(NULL_PAYMENT_PLUGIN, lib)
    }

    #[cfg(unix)]
    fn _load_lib(library: &str) -> libloading::Result<libloading::Library> {
        libloading::os::unix::Library::open(Some(library), ::libc::RTLD_NOW | ::libc::RTLD_NODELETE)
            .map(libloading::Library::from)
    }

    #[cfg(not(unix))]
    fn _load_lib(library: &str) -> libloading::Result<libloading::Library> {
        libloading::Library::new(library)
    }
}<commit_msg>Fix one more test for plugin loading verification.<commit_after>extern crate libloading;

use libindy::ErrorCode;

use command_executor::{Command, CommandContext, CommandParams, CommandMetadata, CommandResult};
use commands::get_str_param;

pub mod about_command {
    use super::*;

    command!(CommandMetadata::build("about", "Show about information").finalize());

    fn execute(_ctx: &CommandContext, _params: &CommandParams) -> CommandResult {
        trace!("execute >> _ctx: params: {:?}", _params);

        println_succ!("Hyperledger Indy CLI (https://github.com/hyperledger/indy-sdk)");
        println!();
        println_succ!("This is the official CLI tool for Hyperledger Indy (https://www.hyperledger.org/projects),");
        println_succ!("which provides a distributed-ledger-based foundation for");
        println_succ!("self-sovereign identity (https://sovrin.org/).");
        println!();
        println_succ!("Version: {}", env!("CARGO_PKG_VERSION"));
        println_succ!("Apache License Version 2.0");
        println_succ!("Copyright 2017 Sovrin Foundation");
        println!();

        let res = Ok(());

        trace!("execute << {:?}", res);
        res
    }
}

pub mod show_command {
    use super::*;
    use std::io::Read;
    use std::fs::File;

    command!(CommandMetadata::build("show", "Print the content of text file")
                            .add_main_param("file", "The path to file to show")
                            .add_example("show /home/file.txt")
                            .finalize());

    fn execute(_ctx: &CommandContext, params: &CommandParams) -> CommandResult {
        trace!("execute >> params: {:?}", params);

        let file = get_str_param("file", params).map_err(error_err!())?;

        let mut file = File::open(file)
            .map_err(error_err!())
            .map_err(map_println_err!("Can't read the file"))?;

        let content = {
            let mut s = String::new();
            file.read_to_string(&mut s)
                .map_err(error_err!())
                .map_err(|err| println_err!("Can't read the file: {}", err))?;
            s
        };

        println!("{}", content);
        let res = Ok(());

        trace!("execute << {:?}", res);
        res
    }
}

pub mod prompt_command {
    use super::*;

    command!(CommandMetadata::build("prompt", "Change command prompt")
                            .add_main_param("prompt", "New prompt string")
                            .add_example("prompt new-prompt")
                            .finalize());

    fn execute(ctx: &CommandContext, params: &CommandParams) -> CommandResult {
        trace!("execute >> ctx: {:?}, params: {:?}", ctx, params);

        let prompt = get_str_param("prompt", params).map_err(error_err!())?;

        ctx.set_main_prompt(prompt.to_owned());
        println_succ!("Command prompt has been set to \"{}\"", prompt);
        let res = Ok(());

        trace!("execute << {:?}", res);
        res
    }
}

pub mod load_plugin_command {
    use super::*;

    command!(CommandMetadata::build("load-plugin", "Load plugin in Libindy")
                            .add_required_param("library", "Name of plugin (can be absolute or relative path)")
                            .add_required_param("initializer", "Name of plugin init function")
                            .add_example("load-plugin library=libsovtoken initializer=sovtoken_init")
                            .finalize());

    fn execute(_ctx: &CommandContext, params: &CommandParams) -> CommandResult {
        trace!("execute >> params: {:?}", params);

        let library = get_str_param("library", params).map_err(error_err!())?;
        let initializer = get_str_param("initializer", params).map_err(error_err!())?;

        let res = load_plugin(_ctx, library, initializer)?;

        trace!("execute << {:?}", res);

        Ok(res)
    }
}

pub fn load_plugin(ctx: &CommandContext, library: &str, initializer: &str) -> Result<(), ()> {
    let lib = _load_lib(library)
        .map_err(|_| println_err!("Plugin not found: {:?}", library))?;

    unsafe {
        let init_func: libloading::Symbol<unsafe extern fn() -> ErrorCode> = lib.get(initializer.as_bytes())
            .map_err(|_| println_err!("Init function not found"))?;

        match init_func() {
            ErrorCode::Success => println_succ!("Plugin has been loaded: \"{}\"", library),
            _ => return Err(println_err!("Plugin has not been loaded: \"{}\"", library))
        }
    }

    //TODO think more about behaviour in case of init_func failed
    ctx.add_plugin(library, lib);

    Ok(())
}

#[cfg(all(unix, test))]
fn _load_lib(library: &str) -> libloading::Result<libloading::Library> {
    libloading::os::unix::Library::open(Some(library), ::libc::RTLD_NOW | ::libc::RTLD_NODELETE)
        .map(libloading::Library::from)
}

#[cfg(any(not(unix), not(test)))]
fn _load_lib(library: &str) -> libloading::Result<libloading::Library> {
    libloading::Library::new(library)
}

pub mod exit_command {
    use super::*;

    command!(CommandMetadata::build("exit", "Exit Indy CLI").finalize());

    fn execute(ctx: &CommandContext, _params: &CommandParams) -> CommandResult {
        trace!("execute >> ctx: {:?}, params: {:?}", ctx, _params);

        ctx.set_exit();
        let res = Ok(());

        trace!("execute << {:?}", res);
        res
    }
}

#[cfg(test)]
#[cfg(feature = "nullpay_plugin")]
pub mod tests {
    use super::*;

    pub const NULL_PAYMENT_METHOD: &'static str = "null";
    pub const NULL_PAYMENT_PLUGIN_INIT_FUNCTION: &'static str = "nullpay_init";

    #[cfg(any(unix))]
    pub const NULL_PAYMENT_PLUGIN: &'static str = "libnullpay.so";
    #[cfg(any(windows))]
    pub const NULL_PAYMENT_PLUGIN: &'static str = "nullpay.dll";

    mod load {
        use super::*;
        use utils::test::TestUtils;

        #[test]
        pub fn load_works() {
            TestUtils::cleanup_storage();
            let ctx = CommandContext::new();

            let cmd = load_plugin_command::new();
            let mut params = CommandParams::new();
            params.insert("library", NULL_PAYMENT_PLUGIN.to_string());
            params.insert("initializer", NULL_PAYMENT_PLUGIN_INIT_FUNCTION.to_string());
            cmd.execute(&ctx, &params).unwrap();

            TestUtils::cleanup_storage();
        }

        #[test]
        pub fn load_works_for_unknown_plugin() {
            let ctx = CommandContext::new();

            let cmd = load_plugin_command::new();
            let mut params = CommandParams::new();
            params.insert("library", "unknown_payment_plugin".to_string());
            params.insert("initializer", NULL_PAYMENT_PLUGIN_INIT_FUNCTION.to_string());
            cmd.execute(&ctx, &params).unwrap_err();
        }

        #[test]
        pub fn load_works_for_unknown_init_function() {
            let ctx = CommandContext::new();

            let cmd = load_plugin_command::new();
            let mut params = CommandParams::new();
            params.insert("library", NULL_PAYMENT_PLUGIN.to_string());
            params.insert("initializer", "unknown_init_function".to_string());
            cmd.execute(&ctx, &params).unwrap_err();
        }
    }

    pub fn load_null_payment_plugin(ctx: &CommandContext) -> () {
        let lib = _load_lib(NULL_PAYMENT_PLUGIN).unwrap();
        unsafe {
            let init_func: libloading::Symbol<unsafe extern fn() -> ErrorCode> = lib.get(NULL_PAYMENT_PLUGIN_INIT_FUNCTION.as_bytes()).unwrap();
            init_func();
        }
        ctx.add_plugin(NULL_PAYMENT_PLUGIN, lib)
    }
}<|endoftext|>