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.
 */
extern crate clap;
extern crate parity_wasm;

use std::path::Path;
use std::string::String;

use clap::{App, Arg};

use wasm_split::{split_module, Result, SplitError};

fn main() {
    let matches = App::new("wasm-split")
        .version(env!("CARGO_PKG_VERSION"))
        .author(env!("CARGO_PKG_AUTHORS"))
        .about(env!("CARGO_PKG_DESCRIPTION"))
        .arg(
            Arg::with_name("entry_name")
                .short("e")
                .long("entry-name")
                .value_name("NAME")
                .help("Name of a function export that is needed in the main module")
                .takes_value(true)
                .required(true), //.multiple(true)
        )
        .arg(
            Arg::with_name("module_name")
                .long("module-name")
                .value_name("NAME")
                .help("Name of the module for the table import")
                .default_value("env")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("field_name")
                .long("field-name")
                .value_name("NAME")
                .help("Name of the of the table import/export")
                .default_value("wasm_split_table")
                .takes_value(true),
        )
        .arg(Arg::with_name("input_file").required(true))
        .get_matches();

    let entry_name = matches.value_of("entry_name").unwrap();
    let module_name = matches.value_of("module_name").unwrap();
    let field_name = matches.value_of("field_name").unwrap();
    let input_file = Path::new(matches.value_of("input_file").unwrap());
    let (output_file_main, output_file_side) =
        output_names(&input_file).expect("Could generate output file names");

    let module =
        parity_wasm::deserialize_file(input_file.to_str().expect("Invalid input file name"))
            .expect("Unparsable Wasm file");

    let result = split_module(&module, entry_name, module_name, field_name);
    match result {
        Ok((main_module, side_module)) => {
            parity_wasm::serialize_to_file(&output_file_main, main_module)
                .expect("Couldn’t write main module");
            parity_wasm::serialize_to_file(&output_file_side, side_module)
                .expect("Couldn’t write side module");

            println!("Success");
            println!("=======");
            println!("");
            println!(
                "It is recommended that you run `wasm-opt` on both {} and {}!",
                output_file_main.display(),
                output_file_side.display()
            );
        }
        Err(err) => println!("Error: {}", err),
    };
}

fn output_names(input_file: &Path) -> Result<(Box<Path>, Box<Path>)> {
    let stem = String::from(
        input_file
            .file_stem()
            .and_then(|stem| stem.to_str())
            .ok_or(SplitError::BadFileName)?,
    );
    let extension = String::from(
        input_file
            .extension()
            .and_then(|stem| stem.to_str())
            .unwrap_or("wasm"),
    );

    let output_file_main = input_file
        .with_file_name(String::from("") + &stem + &String::from("_main.") + &extension)
        .into_boxed_path();

    let output_file_side = input_file
        .with_file_name(String::from("") + &stem + &String::from("_side.") + &extension)
        .into_boxed_path();

    Ok((output_file_main, output_file_side))
}