rtest 0.2.2

integration test building framework
Documentation
use super::{Environment, Process, ProcessId, Resource, ResourceId};
use crate::algos::dependency::Inventory;
use std::collections::{HashMap, HashSet};

pub struct EnvironmentBuilder<R, P> {
    resources: HashMap<ResourceId, R>,
    processes: HashMap<ProcessId, P>,
    initial:   Vec<ResourceId>,
}

impl<R, P> Default for EnvironmentBuilder<R, P> {
    fn default() -> Self {
        Self {
            resources: HashMap::new(),
            processes: HashMap::new(),
            initial:   Vec::new(),
        }
    }
}

impl<R: Resource, P: Process> EnvironmentBuilder<R, P> {
    pub fn add_resource(&mut self, r: R) { self.resources.insert(r.id(), r); }

    pub fn add_process(&mut self, p: P) { self.processes.insert(p.id(), p); }

    pub fn add_initial(&mut self, id: ResourceId) { self.initial.push(id) }

    fn get_resource_ids(&self) -> Vec<ResourceId> {
        let mut result = HashSet::new();
        for p in self.processes.values() {
            for r in p
                .tools()
                .into_iter()
                .chain(p.inputs().into_iter())
                .chain(p.outputs().into_iter())
            {
                result.insert(r);
            }
        }
        result.drain().collect()
    }

    pub fn build(self) -> Environment<R, P> {
        // verify that all ids are known
        for used_id in self.get_resource_ids() {
            if !self.resources.contains_key(&used_id) {
                panic!("Invalid Graph, a Process depends on Resource {used_id} which wasn't specified")
            }
        }

        Environment::new(self.resources, self.processes, Inventory::with_list(&self.initial))
    }
}

#[cfg(test)]
mod tests {
    use super::{EnvironmentBuilder, ProcessId, ResourceId};
    struct Process {
        id:        ProcessId,
        inputs:    Vec<ResourceId>,
        reference: Vec<ResourceId>,
        outputs:   Vec<ResourceId>,
    }

    struct Resource {
        id: ResourceId,
    }

    impl super::Process for Process {
        type I = Vec<ResourceId>;

        fn cost(&self) -> u64 { 64 }

        fn id(&self) -> ProcessId { self.id }

        fn inputs(&self) -> Self::I { self.inputs.clone() }

        fn outputs(&self) -> Self::I { self.outputs.clone() }

        fn tools(&self) -> Self::I { self.reference.clone() }
    }

    impl super::Resource for Resource {
        fn id(&self) -> ResourceId { self.id.clone() }
    }

    #[test]
    fn can_buid_graph() {
        let mut dg = EnvironmentBuilder::default();
        dg.add_process(Process {
            id:        0,
            reference: Vec::new(),
            inputs:    vec!["0".to_string()],
            outputs:   vec!["1".to_string()],
        });
        dg.add_process(Process {
            id:        1,
            reference: Vec::new(),
            inputs:    vec!["1".to_string()],
            outputs:   Vec::new(),
        });
        dg.add_resource(Resource { id: "0".to_string() });
        dg.add_resource(Resource { id: "1".to_string() });

        let _dg = dg.build();
    }
}