Refactored spells import, working on level_1
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user