Refactored spells import, working on level_1

This commit is contained in:
Benjamin Sherriff
2023-10-05 14:26:02 -04:00
parent 1b41849115
commit 49b3a38543
9 changed files with 3044 additions and 145 deletions

View File

@@ -51,6 +51,6 @@ pub fn connection() -> Result<DbConnection, ServiceError> {
.map_err(|e| ServiceError::new(500, format!("Failed getting db connection: {}", e)))
}
pub fn load_data() {
spells::load_data();
pub fn load_data(data_dir_path: &str) {
spells::load_data(data_dir_path);
}

View File

@@ -2,46 +2,57 @@ mod model;
mod routes;
mod types;
use std::{fs::{metadata, File, read_dir}, path::Path, io::BufReader};
use log::{warn, trace};
pub use model::*;
pub use types::*;
pub use routes::init_routes;
pub fn load_data() {
let root_path = std::env::current_dir().unwrap();
let files = [
"cantrips.json", "level_1.json", "level_2.json", "level_3.json", "level_4.json", "level_5.json", "level_6.json", "level_7.json", "level_8.json", "level_9.json"
];
let mut spells: Vec<Spell> = vec![];
for file in files {
let mut data_path = std::path::PathBuf::from(&root_path);
data_path.push(format!("data/spells/{}", file));
let path = data_path.to_str().unwrap();
match std::fs::read_to_string(path) {
Ok(data) => {
log::debug!("Loading spells from {}", path);
match serde_json::from_str::<serde_json::Value>(&data) {
Ok(json) => {
match serde_json::from_value::<Vec<Spell>>(json) {
Ok(mut new_spells) => spells.append(&mut new_spells),
Err(err) => log::error!("Failed to parse spells data: {}", err)
pub fn load_data(data_dir_path: &str) {
if Path::new(data_dir_path).exists() {
let meta = metadata(data_dir_path).unwrap();
if meta.is_dir() {
let spells_dir_path = format!("{}/spells", data_dir_path);
if Path::new(&spells_dir_path).exists() {
let meta = metadata(&spells_dir_path).unwrap();
if meta.is_dir() {
for entry in read_dir(&spells_dir_path).unwrap() {
let entry = entry.unwrap();
let path = entry.path();
if path.is_file() {
let file = File::open(path).unwrap();
let reader = BufReader::new(file);
let result: Result<Vec<Spell>, serde_json::Error> = serde_json::from_reader(reader);
match result {
Ok(spells) => {
for spell in spells {
let mut filters = QueryFilters::default();
filters.by_name = Some(spell.name.clone());
match QuerySpell::get_all(&filters, 100, 1) {
Ok(spells) => {
if spells.len() > 0 {
trace!("Spell '{}' already exists", spell.name);
continue;
}
},
Err(err) => {
warn!("Error checking if spell '{}' exists: {}", spell.name, err);
continue;
}
};
let spell = InsertSpell::insert(spell.into()).unwrap();
trace!("Inserted spell: {}", spell.name);
}
},
Err(err) => warn!("Error reading spells from file: {}", err)
};
}
},
Err(err) => log::error!("Failed to parse spells data to value: {}", err)
};
},
Err(err) => log::error!("Failed to read from {}: {}", file, err)
};
}
let count = QuerySpell::get_count(&QueryFilters::default()).unwrap();
if count >= spells.len() as i64 {
log::warn!("Spell data is already loaded");
return;
}
for spell in spells {
let spell_name = spell.name.clone();
match InsertSpell::insert(spell.into()) {
Ok(_) => {},
Err(err) => log::error!("Failed to insert '{}' spell: {}", spell_name, err)
}
}
}
}
} else {
warn!("Data path '{}' does not exist, no data imported", data_dir_path);
}
}

View File

@@ -4,7 +4,7 @@ use siren::ServiceError;
use crate::db::{schema::spells::{self}, classes::AbilityType, conditions::ConditionType};
use super::{SchoolType, CastingTime, CastingType, SpellAttackType, SpellDamageType, Range, Area, Components, Duration, Source, Description, DurationType};
use super::{SchoolType, CastingTime, SpellAttackType, SpellDamageType, Range, Area, Components, Duration, Source, Description, DurationType, Effect};
#[derive(Queryable, QueryableByName, Serialize, Deserialize)]
#[diesel(table_name = spells)]
@@ -198,6 +198,8 @@ pub struct Spell {
pub ritual: bool,
pub casting_time: CastingTime,
#[serde(skip_serializing_if = "Option::is_none")]
pub effect: Option<Effect>,
#[serde(skip_serializing_if = "Option::is_none")]
pub saving_throw: Option<Vec<AbilityType>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub attack_type: Option<SpellAttackType>,
@@ -232,7 +234,8 @@ impl From<QuerySpell> for Spell {
school: SchoolType::Abjuration,
level: 0,
ritual: false,
casting_time: CastingTime { value: 0, casting_type: CastingType::Action },
casting_time: CastingTime { value: 0, casting_type: "".to_string(), note: None },
effect: None,
saving_throw: None,
attack_type: None,
damage_inflict: None,

View File

@@ -58,50 +58,10 @@ impl FromStr for SchoolType {
pub struct CastingTime {
pub value: i32,
#[serde(rename = "unit")]
pub casting_type: CastingType
pub casting_type: String,
pub note: Option<String>
}
#[derive(Debug, Serialize, Deserialize)]
pub enum CastingType {
#[serde(rename = "action")]
Action,
#[serde(rename = "bonus")]
BonusAction,
#[serde(rename = "reaction")]
Reaction,
#[serde(rename = "minutes")]
Minutes,
#[serde(rename = "hours")]
Hours
}
// impl CastingType {
// pub fn to_string(&self) -> String {
// match self {
// CastingType::Action => "action".to_string(),
// CastingType::BonusAction => "bonus".to_string(),
// CastingType::Reaction => "reaction".to_string(),
// CastingType::Minutes => "minutes".to_string(),
// CastingType::Hours => "hours".to_string()
// }
// }
// }
// impl FromStr for CastingType {
// type Err = ();
// fn from_str(s: &str) -> Result<Self, Self::Err> {
// match s {
// "action" => Ok(CastingType::Action),
// "bonus" => Ok(CastingType::BonusAction),
// "reaction" => Ok(CastingType::Reaction),
// "minutes" => Ok(CastingType::Minutes),
// "hours" => Ok(CastingType::Hours),
// _ => Err(())
// }
// }
// }
#[derive(Debug, Serialize, Deserialize)]
pub enum SpellAttackType {
#[serde(rename = "melee")]
@@ -303,8 +263,15 @@ pub struct Description {
#[derive(Debug)]
pub struct Entry {
pub entry_type: String,
pub items: Vec<String>
pub text: Option<Vec<String>>,
pub list: Option<Vec<String>>,
pub table: Option<EntryTable>
}
#[derive(Debug, Serialize, Deserialize)]
pub struct EntryTable {
pub headers: Vec<String>,
pub rows: Vec<Vec<String>>
}
impl<'de> Deserialize<'de> for Entry {
@@ -312,36 +279,82 @@ impl<'de> Deserialize<'de> for Entry {
let value = serde_json::Value::deserialize(deserializer)?;
match value {
serde_json::Value::String(s) => Ok(Entry {
entry_type: "string".to_string(),
items: vec![s]
text: Some(vec![s]),
list: None,
table: None,
}),
serde_json::Value::Object(o) => {
let entry_type = match o.get("type") {
Some(t) => match t.as_str() {
Some(s) => s.to_string(),
None => return Err(serde::de::Error::custom("Invalid entry type"))
},
None => return Err(serde::de::Error::custom("Missing entry type"))
};
let items = match o.get("items") {
let list = match o.get("list") {
Some(i) => match i.as_array() {
Some(a) => {
let mut items = Vec::new();
let mut list = Vec::new();
for item in a {
match item.as_str() {
Some(s) => items.push(s.to_string()),
None => return Err(serde::de::Error::custom("Invalid entry item"))
Some(s) => list.push(s.to_string()),
None => return Err(serde::de::Error::custom("Invalid entry list item"))
}
}
items
Some(list)
},
None => return Err(serde::de::Error::custom("Invalid entry items"))
None => return Err(serde::de::Error::custom("Invalid entry list items"))
},
None => return Err(serde::de::Error::custom("Missing entry items"))
None => None
};
let table = match o.get("table") {
Some(t) => match t.as_object() {
Some(o) => {
let mut headers = Vec::new();
let mut rows = Vec::new();
match o.get("headers") {
Some(c) => match c.as_array() {
Some(a) => {
for item in a {
match item.as_str() {
Some(s) => headers.push(s.to_string()),
None => return Err(serde::de::Error::custom("Invalid entry table header"))
}
}
},
None => return Err(serde::de::Error::custom("Invalid entry table headers"))
},
None => return Err(serde::de::Error::custom("Missing entry table headers"))
};
match o.get("rows") {
Some(r) => match r.as_array() {
Some(a) => {
for row in a {
match row.as_array() {
Some(a) => {
let mut row = Vec::new();
for item in a {
match item.as_str() {
Some(s) => row.push(s.to_string()),
None => return Err(serde::de::Error::custom("Invalid entry table row item"))
}
}
rows.push(row);
},
None => return Err(serde::de::Error::custom("Invalid entry table row"))
}
}
},
None => return Err(serde::de::Error::custom("Invalid entry table rows"))
},
None => return Err(serde::de::Error::custom("Missing entry table rows"))
};
Some(EntryTable {
headers,
rows
})
},
None => return Err(serde::de::Error::custom("Invalid entry table"))
},
None => None
};
Ok(Entry {
entry_type,
items
text: None,
list,
table
})
},
_ => Err(serde::de::Error::custom("Invalid entry"))
@@ -351,15 +364,17 @@ impl<'de> Deserialize<'de> for Entry {
impl Serialize for Entry {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
match self.entry_type.as_str() {
"string" => serializer.serialize_str(&self.items[0]),
_ => {
let mut map = serializer.serialize_map(Some(2))?;
map.serialize_entry("type", &self.entry_type)?;
map.serialize_entry("items", &self.items)?;
map.end()
}
let mut map = serializer.serialize_map(Some(1))?;
if let Some(text) = &self.text {
map.serialize_entry("text", text)?;
}
if let Some(list) = &self.list {
map.serialize_entry("list", list)?;
}
if let Some(table) = &self.table {
map.serialize_entry("table", table)?;
}
map.end()
}
}
@@ -374,4 +389,9 @@ pub struct Components {
pub materials_cost: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub materials_consumed: Option<bool>
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Effect {
pub effect_type: Option<String>
}