use anyhow::{Context, Result, anyhow, bail};
use serde_json::{Map, Value};
use std::fs;
use std::path::Path;
use crate::util::config::Config;
pub fn load_spec_from_file(spec_path: String) -> Result<Value> {
let path = Path::new(&spec_path);
if !path.exists() {
bail!("Could not find file '{}'", spec_path)
}
let file_content = fs::read_to_string(path)?;
serde_yaml::from_str(&file_content)
.with_context(|| format!("Failed to parse spec file at {}", path.to_string_lossy()))
}
pub fn load_spec_from_project_directory(config: &Config) -> Result<Value> {
let rngo_path = Path::new(".rngo");
let effects_path = rngo_path.join("effects");
let effect_files = fs::read_dir(effects_path.clone()).with_context(|| {
format!(
"Failed to read from effects directory at '{}'",
effects_path.to_string_lossy()
)
})?;
let mut effects_map = Map::new();
for entry in effect_files {
let entry = entry?;
let path = entry.path();
let content = fs::read_to_string(&path)?;
let yaml_value: serde_yaml::Value = serde_yaml::from_str(&content).with_context(|| {
format!("Failed to parse effect file at {}", path.to_string_lossy())
})?;
let mut json_value: serde_json::Value = serde_json::to_value(yaml_value)?;
if let Some(obj) = json_value.as_object_mut() {
obj.entry("type").or_insert_with(|| "state.create".into());
}
if let Some(filename) = path.file_stem().and_then(|s| s.to_str()) {
effects_map.insert(filename.to_string(), json_value);
}
}
if effects_map.is_empty() {
bail!(
"No effects found under {}",
effects_path.to_string_lossy()
)
}
let systems_map = load_systems_from_project_directory()?;
let mut spec = Map::new();
spec.insert("seed".into(), config.seed.into());
if let Some(key) = &config.key {
spec.insert("key".into(), key.clone().into());
} else {
let dir_name = std::env::current_dir()
.ok()
.and_then(|dir| {
dir.file_name()
.and_then(|s| s.to_str())
.map(|s| s.to_string())
})
.ok_or_else(|| anyhow!("Failed to get current directory"))?;
spec.insert("key".into(), dir_name.into());
}
if let Some(start) = &config.start {
spec.insert("start".into(), start.clone().into());
}
if let Some(end) = &config.end {
spec.insert("end".into(), end.clone().into());
}
if !systems_map.is_empty() {
spec.insert("systems".into(), serde_json::Value::Object(systems_map));
}
spec.insert("effects".into(), serde_json::Value::Object(effects_map));
Ok(serde_json::Value::Object(spec))
}
pub fn load_systems_from_project_directory() -> Result<Map<String, Value>> {
let rngo_path = Path::new(".rngo");
let systems_path = rngo_path.join("systems");
let mut systems_map = Map::new();
if systems_path.is_dir() {
let system_files = fs::read_dir(systems_path.clone()).with_context(|| {
format!(
"Failed to read from systems directory at '{}'",
systems_path.to_string_lossy()
)
})?;
for system in system_files {
let system = system?;
let path = system.path();
let content = fs::read_to_string(&path)?;
let yaml_value: serde_yaml::Value = serde_yaml::from_str(&content)
.with_context(|| format!("Failed to parse file at {}", path.to_string_lossy()))?;
let json_value: serde_json::Value = serde_json::to_value(yaml_value)?;
if let Some(filename) = path.file_stem().and_then(|s| s.to_str()) {
systems_map.insert(filename.to_string(), json_value);
}
}
}
Ok(systems_map)
}
pub fn ensure_spec_output_is_stream(mut spec: Value) -> Value {
match spec {
Value::Object(ref mut map) => {
map.insert("output".into(), "stream".into());
spec
}
_ => spec,
}
}
pub fn get_spec_key(spec: &mut Value) -> Option<String> {
match spec {
Value::Object(map) => {
if let Some(key) = map.remove("key") {
key.as_str().map(|s| s.to_string())
} else {
None
}
}
_ => None,
}
}