use std::fs;
use std::path::{Path, PathBuf};
use cargo_metadata;
use cargo_toml;
use toml;
use crate::build::BuildArgs;
use crate::error::*;
use crate::serve::ServeArgs;
use crate::test::TestArgs;
const STATIC_FOLDER: &'static str = "static";
fn user_static_index() -> String {
format!("{}/index.html", STATIC_FOLDER)
}
fn user_static_style_scss() -> String {
format!("{}/style.scss", STATIC_FOLDER)
}
pub enum SimiStage {
UserSource,
CargoBuild,
WasmBindgen,
SimiFinalApp,
}
#[derive(Deserialize)]
struct SimiToml {
browser_drivers: Option<Vec<String>>,
index: Option<String>,
scss: Option<String>,
output_path: Option<String>,
wasm_serve_path: Option<String>,
}
#[derive(Debug)]
struct UserStatics {
index: Option<PathBuf>,
scss: Option<PathBuf>,
}
#[derive(Debug)]
pub struct SimiConfig {
name: String,
target: String,
release: bool,
nightly: bool,
source_path: PathBuf,
target_path: PathBuf,
output_path: PathBuf,
wasm_serve_path: Option<String>,
serve_port: u16,
browser_drivers: Vec<String>,
with_head: bool,
user_statics: UserStatics,
}
impl SimiToml {
fn validate(&self) -> Result<(), Error> {
if let Some(ref index) = self.index {
let p = Path::new(&index);
if let Err(e) = p.canonicalize() {
return Err(format_err!("Invalid index file path={}: {}", index, e));
}
}
if let Some(ref scss) = self.scss {
let p = Path::new(&scss);
if let Err(e) = p.canonicalize() {
return Err(format_err!("Invalid scss file path={}: {}", scss, e));
}
}
if let Some(ref output) = self.output_path {
let p = Path::new(&output);
if let Err(e) = p.canonicalize() {
return Err(format_err!("Invalid output_path={}: {}", output, e));
}
}
if let Some(ref drivers) = self.browser_drivers {
for d in drivers {
let p = Path::new(&d);
if let Err(e) = p.canonicalize() {
return Err(format_err!("Invalid browser_drivers path={}: {}", d, e));
}
}
}
Ok(())
}
}
impl UserStatics {
fn new(simi_toml: &SimiToml, cwd: &PathBuf) -> Result<Self, Error> {
let mut us = UserStatics {
index: simi_toml.index.as_ref().map(PathBuf::from),
scss: simi_toml.scss.as_ref().map(PathBuf::from),
};
if let Some(ref mut index) = us.index {
let ok_index = index.canonicalize()?;
*index = ok_index;
} else {
let index = cwd.clone().join(user_static_index());
if index.exists() {
us.index = Some(index);
}
}
if let Some(ref mut scss) = us.scss {
let ok_scss = scss.canonicalize()?;
*scss = ok_scss;
} else {
let scss = cwd.clone().join(user_static_style_scss());
if scss.exists() {
us.scss = Some(scss);
}
}
Ok(us)
}
}
impl SimiConfig {
fn new(arg: BuildArgs, serve_port: u16) -> Result<SimiConfig, Error> {
let cwd = ::std::env::current_dir().expect("unable to get current working directory");
let simi_toml = SimiConfig::load_simi_toml(&cwd)?;
let user_statics = UserStatics::new(&simi_toml, &cwd)?;
let cargo_toml = cwd.clone().join("Cargo.toml");
let main_toml = cargo_toml::Manifest::from_slice(&fs::read(cargo_toml.as_path())?)?;
let metadata = match cargo_metadata::MetadataCommand::new()
.manifest_path(cargo_toml.as_path())
.exec()
{
Ok(value) => value,
Err(e) => return Err(format_err!("error: {}", e)),
};
let output_path = simi_toml
.output_path
.map_or(cwd.clone().join("simi-site"), PathBuf::from);
let simi_config = SimiConfig {
name: main_toml
.package
.expect("main package toml")
.name
.replace("-", "_"),
target: crate::DEFAULT_TARGET.to_string(),
release: arg.release,
nightly: arg.nightly,
source_path: cwd,
target_path: PathBuf::from(&metadata.target_directory),
output_path,
wasm_serve_path: simi_toml
.wasm_serve_path
.map(|s| s.trim_end_matches('/').to_string()),
serve_port,
browser_drivers: simi_toml.browser_drivers.unwrap_or(Vec::new()),
with_head: false,
user_statics,
};
Ok(simi_config)
}
fn load_simi_toml(cwd: &PathBuf) -> Result<SimiToml, Error> {
let simi_toml = cwd.join(".simi.toml");
if simi_toml.exists() {
let simi_toml: SimiToml = toml::from_slice(&fs::read(simi_toml.as_path())?)?;
simi_toml.validate()?;
Ok(simi_toml)
} else {
Ok(SimiToml {
browser_drivers: None,
index: None,
scss: None,
output_path: None,
wasm_serve_path: None,
})
}
}
pub fn from_build(arg: BuildArgs) -> Result<SimiConfig, Error> {
SimiConfig::new(arg, crate::DEFAULT_SERVE_PORT)
}
pub fn from_serve(arg: ServeArgs) -> Result<SimiConfig, Error> {
let ServeArgs {
build,
port,
serve_only: _,
} = arg;
SimiConfig::new(build, port)
}
pub fn for_test(arg: TestArgs) -> Result<SimiConfig, Error> {
let mut config = SimiConfig::new(BuildArgs { release: false, nightly: arg.nightly }, crate::DEFAULT_SERVE_PORT)?;
config.with_head = arg.with_head;
Ok(config)
}
pub fn simi_app_name(&self) -> &String {
&self.name
}
pub fn wasm_serve_path(&self) -> &Option<String> {
&self.wasm_serve_path
}
pub fn target(&self) -> &String {
&self.target
}
pub fn release(&self) -> bool {
self.release
}
pub fn nightly(&self) -> bool {
self.nightly
}
pub fn output_path(&self) -> &PathBuf {
&self.output_path
}
pub fn serve_port(&self) -> u16 {
self.serve_port
}
pub fn get_wasm_file_name(&self, with_bg: bool) -> String {
if with_bg {
format!("{}_bg.wasm", self.name)
} else {
format!("{}.wasm", self.name)
}
}
pub fn get_wasm_file_path(&self, stage: SimiStage) -> PathBuf {
match stage {
SimiStage::CargoBuild => {
let mut rs = self.target_path.clone();
rs.push(self.target());
rs.push(if self.release { "release" } else { "debug" });
rs.push(&self.get_wasm_file_name(false));
rs
}
SimiStage::SimiFinalApp => {
let mut rs = self.output_path.clone();
rs.push(&self.get_wasm_file_name(true));
rs
}
_ => unreachable!(),
}
}
pub fn get_wasm_bindgen_js_file_path(&self, stage: SimiStage) -> PathBuf {
match stage {
SimiStage::WasmBindgen | SimiStage::SimiFinalApp => {
self.output_path.join(format!("{}.js", self.name))
}
_ => unreachable!(),
}
}
pub fn get_wasm_loader_js(&self) -> PathBuf {
self.output_path.join(format!("{}_bg.js", self.name))
}
pub fn get_index_html_path(&self, stage: SimiStage) -> Option<PathBuf> {
match stage {
SimiStage::UserSource => self.user_statics.index.clone(),
SimiStage::SimiFinalApp => {
let mut rs = self.output_path.clone();
rs.push("index.html");
Some(rs)
}
_ => unreachable!(),
}
}
pub fn get_scss_path(&self) -> &Option<PathBuf> {
&self.user_statics.scss
}
pub fn get_css_path(&self) -> PathBuf {
self.output_path.join(self.get_css_file_name().unwrap())
}
pub fn get_css_file_name(&self) -> Option<String> {
self.user_statics.scss.as_ref().map(|p| {
p.file_name()
.unwrap()
.to_string_lossy()
.replace(".scss", ".css")
})
}
pub fn get_static_path(&self) -> PathBuf {
self.source_path.join(STATIC_FOLDER)
}
pub fn has_browser_driver(&self) -> bool {
self.browser_drivers.len() > 0
}
pub fn browser_drivers(&self) -> &Vec<String> {
&self.browser_drivers
}
pub fn with_head(&self) -> bool {
self.with_head
}
}