wasm-split 0.1.0

Code splitting for WebAssembly
Documentation
/**
 * Copyright 2019 Google Inc. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
use std::collections::{HashMap, HashSet};

pub struct CallGraph(pub HashMap<u32, HashSet<u32>>);

impl CallGraph {
    pub fn new() -> CallGraph {
        CallGraph(HashMap::new())
    }

    pub fn iter<'a>(&'a self) -> std::collections::hash_map::Iter<'a, u32, HashSet<u32>> {
        self.0.iter()
    }

    pub fn get<'a>(&'a self, id: u32) -> Option<&'a HashSet<u32>> {
        self.0.get(&id)
    }

    pub fn all_funcs(&self) -> HashSet<u32> {
        self.iter()
            .fold(HashSet::new(), |mut all_funcs, (_id, deps)| {
                deps.iter().for_each(|&v| {
                    all_funcs.insert(v);
                });
                all_funcs
            })
    }

    fn flatten_for_id(&self, id: u32, flat: &mut CallGraph) -> Option<()> {
        if flat.0.contains_key(&id) {
            return None;
        }

        let direct_funcs = self.0.get(&id)?;

        // Insert an empty HashSet as a recursion anchor
        flat.0.insert(id, HashSet::new());

        let mut result = HashSet::new();
        direct_funcs.iter().for_each(|&direct_func| {
            result.insert(direct_func);
        });
        direct_funcs
            .iter()
            .filter(|&direct_func| direct_func != &id)
            .map(|&direct_func| -> Option<()> {
                self.flatten_for_id(direct_func, flat);
                flat.0.get(&direct_func)?.iter().for_each(|&indirect_func| {
                    result.insert(indirect_func);
                });
                Some(())
            })
            .for_each(drop);

        // Overwrite the recursion anchror with read result
        flat.0.insert(id, result);
        Some(())
    }

    pub fn flatten(&self) -> CallGraph {
        let mut flat = CallGraph::new();

        self.0.keys().for_each(|&id| {
            self.flatten_for_id(id, &mut flat);
        });
        flat
    }
}

impl std::fmt::Display for CallGraph {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        self.0
            .iter()
            .map(|(id, calls)| write!(f, "{} => {:?}\n", id, calls))
            .fold(Ok(()), |result, v| result.and(v))
    }
}