118 lines
2.9 KiB
Rust
118 lines
2.9 KiB
Rust
|
mod engine;
|
||
|
mod gun;
|
||
|
mod ship;
|
||
|
mod system;
|
||
|
mod util;
|
||
|
|
||
|
pub use engine::Engine;
|
||
|
pub use gun::{Gun, Projectile};
|
||
|
pub use ship::{EnginePoint, GunPoint, Ship};
|
||
|
pub use system::{Object, System};
|
||
|
|
||
|
use anyhow::{bail, Context, Result};
|
||
|
use std::collections::HashMap;
|
||
|
use std::{fs::File, io::Read, path::Path};
|
||
|
use toml;
|
||
|
use walkdir::WalkDir;
|
||
|
|
||
|
mod syntax {
|
||
|
use super::HashMap;
|
||
|
use super::{engine, gun, ship, system};
|
||
|
use serde::Deserialize;
|
||
|
|
||
|
#[derive(Debug, Deserialize)]
|
||
|
pub struct Root {
|
||
|
pub gun: Option<HashMap<String, gun::syntax::Gun>>,
|
||
|
pub ship: Option<HashMap<String, ship::syntax::Ship>>,
|
||
|
pub system: Option<HashMap<String, system::syntax::System>>,
|
||
|
pub engine: Option<HashMap<String, engine::syntax::Engine>>,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
trait Build {
|
||
|
/// Build a processed System struct from raw serde data
|
||
|
fn build(root: &syntax::Root) -> Result<Vec<Self>>
|
||
|
where
|
||
|
Self: Sized;
|
||
|
}
|
||
|
|
||
|
/// Represents generic game content, not connected to any game objects.
|
||
|
#[derive(Debug)]
|
||
|
pub struct Content {
|
||
|
pub systems: Vec<system::System>,
|
||
|
pub ships: Vec<ship::Ship>,
|
||
|
pub guns: Vec<gun::Gun>,
|
||
|
pub engines: Vec<engine::Engine>,
|
||
|
}
|
||
|
|
||
|
macro_rules! quick_name_dup_check {
|
||
|
($array:expr, $root:ident, $build:expr) => {{
|
||
|
let mut p = $build(&$root)?;
|
||
|
for s in &$array {
|
||
|
for o in &p {
|
||
|
if s.name == o.name {
|
||
|
bail!(
|
||
|
"Error parsing content: duplicate ship names `{}` and `{}`",
|
||
|
s.name,
|
||
|
o.name
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
$array.append(&mut p);
|
||
|
}};
|
||
|
}
|
||
|
|
||
|
impl Content {
|
||
|
fn try_parse(path: &Path) -> Result<syntax::Root> {
|
||
|
let mut file_string = String::new();
|
||
|
let _ = File::open(path)?.read_to_string(&mut file_string);
|
||
|
let file_string = file_string.trim();
|
||
|
return Ok(toml::from_str(&file_string)?);
|
||
|
}
|
||
|
|
||
|
fn add_root(&mut self, root: syntax::Root) -> Result<()> {
|
||
|
quick_name_dup_check!(self.systems, root, system::System::build);
|
||
|
quick_name_dup_check!(self.guns, root, gun::Gun::build);
|
||
|
quick_name_dup_check!(self.ships, root, ship::Ship::build);
|
||
|
quick_name_dup_check!(self.engines, root, engine::Engine::build);
|
||
|
return Ok(());
|
||
|
}
|
||
|
|
||
|
pub fn load_dir(path: &str) -> Result<Self> {
|
||
|
let mut content = Self {
|
||
|
systems: Vec::new(),
|
||
|
ships: Vec::new(),
|
||
|
guns: Vec::new(),
|
||
|
engines: Vec::new(),
|
||
|
};
|
||
|
|
||
|
for e in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) {
|
||
|
if e.metadata().unwrap().is_file() {
|
||
|
// TODO: better warnings
|
||
|
match e.path().extension() {
|
||
|
Some(t) => {
|
||
|
if t.to_str() != Some("toml") {
|
||
|
println!("[WARNING] {e:#?} is not a toml file, skipping.");
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
None => {
|
||
|
println!("[WARNING] {e:#?} is not a toml file, skipping.");
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
let path = e.path();
|
||
|
let root = Self::try_parse(path)
|
||
|
.with_context(|| format!("Could not load {:#?}", e.path()))?;
|
||
|
content
|
||
|
.add_root(root)
|
||
|
.with_context(|| format!("Could not parse {}", path.display()))?;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return Ok(content);
|
||
|
}
|
||
|
}
|