Galactica/crates/content/src/lib.rs

118 lines
2.9 KiB
Rust
Raw Normal View History

2023-12-28 17:04:41 -08:00
mod engine;
2023-12-27 20:13:39 -08:00
mod gun;
2023-12-27 19:51:58 -08:00
mod ship;
mod system;
2023-12-29 15:14:04 -08:00
mod util;
2023-12-24 23:03:00 -08:00
2023-12-28 17:04:41 -08:00
pub use engine::Engine;
2023-12-27 20:13:39 -08:00
pub use gun::{Gun, Projectile};
2023-12-28 17:04:41 -08:00
pub use ship::{EnginePoint, GunPoint, Ship};
2023-12-27 19:51:58 -08:00
pub use system::{Object, System};
2023-12-25 09:01:12 -08:00
2023-12-27 19:51:58 -08:00
use anyhow::{bail, Context, Result};
use std::collections::HashMap;
use std::{fs::File, io::Read, path::Path};
use toml;
2023-12-25 09:01:12 -08:00
use walkdir::WalkDir;
2023-12-27 19:51:58 -08:00
mod syntax {
2023-12-27 20:13:39 -08:00
use super::HashMap;
2023-12-28 17:04:41 -08:00
use super::{engine, gun, ship, system};
2023-12-27 19:51:58 -08:00
use serde::Deserialize;
#[derive(Debug, Deserialize)]
pub struct Root {
2023-12-27 20:13:39 -08:00
pub gun: Option<HashMap<String, gun::syntax::Gun>>,
pub ship: Option<HashMap<String, ship::syntax::Ship>>,
pub system: Option<HashMap<String, system::syntax::System>>,
2023-12-28 17:04:41 -08:00
pub engine: Option<HashMap<String, engine::syntax::Engine>>,
2023-12-27 19:51:58 -08:00
}
}
trait Build {
/// Build a processed System struct from raw serde data
fn build(root: &syntax::Root) -> Result<Vec<Self>>
where
Self: Sized;
}
2023-12-28 17:06:33 -08:00
/// Represents generic game content, not connected to any game objects.
2023-12-27 19:51:58 -08:00
#[derive(Debug)]
pub struct Content {
pub systems: Vec<system::System>,
pub ships: Vec<ship::Ship>,
2023-12-27 20:13:39 -08:00
pub guns: Vec<gun::Gun>,
2023-12-28 17:04:41 -08:00
pub engines: Vec<engine::Engine>,
2023-12-27 19:51:58 -08:00
}
2023-12-27 20:13:39 -08:00
macro_rules! quick_name_dup_check {
($array:expr, $root:ident, $build:expr) => {{
let mut p = $build(&$root)?;
for s in &$array {
2023-12-27 19:51:58 -08:00
for o in &p {
if s.name == o.name {
bail!(
2023-12-27 20:13:39 -08:00
"Error parsing content: duplicate ship names `{}` and `{}`",
2023-12-27 19:51:58 -08:00
s.name,
o.name
)
2023-12-25 09:01:12 -08:00
}
2023-12-27 19:51:58 -08:00
}
}
2023-12-27 20:13:39 -08:00
$array.append(&mut p);
}};
}
2023-12-27 19:51:58 -08:00
2023-12-27 20:13:39 -08:00
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)?);
}
2023-12-25 09:01:12 -08:00
2023-12-27 20:13:39 -08:00
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);
2023-12-28 17:04:41 -08:00
quick_name_dup_check!(self.engines, root, engine::Engine::build);
2023-12-27 19:51:58 -08:00
return Ok(());
}
2023-12-25 09:01:12 -08:00
2023-12-27 19:51:58 -08:00
pub fn load_dir(path: &str) -> Result<Self> {
let mut content = Self {
systems: Vec::new(),
ships: Vec::new(),
2023-12-27 20:13:39 -08:00
guns: Vec::new(),
2023-12-28 17:04:41 -08:00
engines: Vec::new(),
2023-12-27 19:51:58 -08:00
};
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()))?;
2023-12-25 09:01:12 -08:00
}
}
2023-12-27 19:51:58 -08:00
return Ok(content);
}
2023-12-25 09:01:12 -08:00
}