Refactored spells import, working on level_1
This commit is contained in:
@@ -8,6 +8,4 @@ DATABASE_PORT=5432
|
|||||||
|
|
||||||
SERVICE_HOST=localhost
|
SERVICE_HOST=localhost
|
||||||
SERVICE_PORT=5000
|
SERVICE_PORT=5000
|
||||||
|
DATA_DIR_PATH=
|
||||||
DISCORD_TOKEN=
|
|
||||||
OPENAI_API_KEY=
|
|
||||||
48
service/data/layout.json
Normal file
48
service/data/layout.json
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"school": "",
|
||||||
|
"level": 1,
|
||||||
|
"ritual": false,
|
||||||
|
"casting_time": {
|
||||||
|
"value": 1,
|
||||||
|
"unit": "action"
|
||||||
|
},
|
||||||
|
"saving_throw": [],
|
||||||
|
"attack_type": [],
|
||||||
|
"damage_inflict": [],
|
||||||
|
"damage_resist": [],
|
||||||
|
"conditions": [],
|
||||||
|
"range": {
|
||||||
|
"type": "point",
|
||||||
|
"value": 1,
|
||||||
|
"unit": "feet"
|
||||||
|
},
|
||||||
|
"area": {
|
||||||
|
"type": "cube",
|
||||||
|
"size": 1,
|
||||||
|
"unit": "feet"
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"verbal": false,
|
||||||
|
"somatic": false,
|
||||||
|
"material": false,
|
||||||
|
"materials_needed": "",
|
||||||
|
"materials_cost": 0,
|
||||||
|
"materials_consumed": false
|
||||||
|
},
|
||||||
|
"durations": [
|
||||||
|
{
|
||||||
|
"type": "instantaneous",
|
||||||
|
"value": 1,
|
||||||
|
"unit": "minute"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"classes": [],
|
||||||
|
"sources": [ { "source": "", "page": 0 } ],
|
||||||
|
"tags": [],
|
||||||
|
"description": {
|
||||||
|
"entries": [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -213,8 +213,7 @@
|
|||||||
"entries": [
|
"entries": [
|
||||||
"You choose nonmagical flame that you can see within range and that fits within a 5-foot cube. You affect it in one of the following ways:",
|
"You choose nonmagical flame that you can see within range and that fits within a 5-foot cube. You affect it in one of the following ways:",
|
||||||
{
|
{
|
||||||
"type": "list",
|
"list": [
|
||||||
"items": [
|
|
||||||
"You instantaneously expand the flame 5 feet in one direction, provided that wood or other fuel is present in the new location.",
|
"You instantaneously expand the flame 5 feet in one direction, provided that wood or other fuel is present in the new location.",
|
||||||
"You instantaneously extinguish the flames within the cube.",
|
"You instantaneously extinguish the flames within the cube.",
|
||||||
"You double or halve the area of bright light and dim light cast by the flame, change its color, or both. The change lasts for 1 hour.",
|
"You double or halve the area of bright light and dim light cast by the flame, change its color, or both. The change lasts for 1 hour.",
|
||||||
@@ -253,7 +252,7 @@
|
|||||||
{
|
{
|
||||||
"type": "concentration",
|
"type": "concentration",
|
||||||
"value": 1,
|
"value": 1,
|
||||||
"unit": "minutes"
|
"unit": "minute"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"classes": ["artificer", "druid", "sorcerer", "warlock", "wizard"],
|
"classes": ["artificer", "druid", "sorcerer", "warlock", "wizard"],
|
||||||
@@ -293,7 +292,7 @@
|
|||||||
{
|
{
|
||||||
"type": "concentration",
|
"type": "concentration",
|
||||||
"value": 1,
|
"value": 1,
|
||||||
"unit": "minutes"
|
"unit": "minute"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"classes": ["artificer", "bard", "sorcerer", "wizard"],
|
"classes": ["artificer", "bard", "sorcerer", "wizard"],
|
||||||
@@ -341,8 +340,7 @@
|
|||||||
"entries": [
|
"entries": [
|
||||||
"Whispering to the spirits of nature, you create one of the following effects within range:",
|
"Whispering to the spirits of nature, you create one of the following effects within range:",
|
||||||
{
|
{
|
||||||
"type": "list",
|
"list": [
|
||||||
"items": [
|
|
||||||
"You create a tiny, harmless sensory effect that predicts what the weather will be at your location for the next 24 hours. The effect might manifest as a golden orb for clear skies, a cloud for rain, falling snowflakes for snow, and so on. This effect persists for 1 round.",
|
"You create a tiny, harmless sensory effect that predicts what the weather will be at your location for the next 24 hours. The effect might manifest as a golden orb for clear skies, a cloud for rain, falling snowflakes for snow, and so on. This effect persists for 1 round.",
|
||||||
"You instantly make a flower blossom, a seed pod open, or a leaf bud bloom.",
|
"You instantly make a flower blossom, a seed pod open, or a leaf bud bloom.",
|
||||||
"You create an instantaneous, harmless sensory effect, such as falling leaves, a puff of wind, the sound of a small animal, or the faint odor of skunk. The effect must fit in a 5-foot cube.",
|
"You create an instantaneous, harmless sensory effect, such as falling leaves, a puff of wind, the sound of a small animal, or the faint odor of skunk. The effect must fit in a 5-foot cube.",
|
||||||
@@ -490,7 +488,7 @@
|
|||||||
{
|
{
|
||||||
"type": "concentration",
|
"type": "concentration",
|
||||||
"value": 1,
|
"value": 1,
|
||||||
"unit": "minutes"
|
"unit": "minute"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"classes": ["bard", "sorcerer", "warlock", "wizard"],
|
"classes": ["bard", "sorcerer", "warlock", "wizard"],
|
||||||
@@ -611,7 +609,7 @@
|
|||||||
{
|
{
|
||||||
"type": "concentration",
|
"type": "concentration",
|
||||||
"value": 1,
|
"value": 1,
|
||||||
"unit": "minutes"
|
"unit": "minute"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"classes": ["artificer", "cleric", "druid"],
|
"classes": ["artificer", "cleric", "druid"],
|
||||||
@@ -658,8 +656,7 @@
|
|||||||
"entries": [
|
"entries": [
|
||||||
"You seize the air and compel it to create one of the following effects at a point you can see within range:",
|
"You seize the air and compel it to create one of the following effects at a point you can see within range:",
|
||||||
{
|
{
|
||||||
"type": "list",
|
"list": [
|
||||||
"items": [
|
|
||||||
"One Medium or smaller creature that you choose must succeed on a Strength saving throw or be pushed up to 5 feet away from you.",
|
"One Medium or smaller creature that you choose must succeed on a Strength saving throw or be pushed up to 5 feet away from you.",
|
||||||
"You create a small blast of air capable of moving one object that is neither held nor carried and that weighs no more than 5 pounds. The object is pushed up to 10 feet away from you. It isn't pushed with enough force to cause damage.",
|
"You create a small blast of air capable of moving one object that is neither held nor carried and that weighs no more than 5 pounds. The object is pushed up to 10 feet away from you. It isn't pushed with enough force to cause damage.",
|
||||||
"You create a harmless sensory effect using air, such as causing leaves to rustle, wind to slam shutters shut, or your clothing to ripple in a breeze."
|
"You create a harmless sensory effect using air, such as causing leaves to rustle, wind to slam shutters shut, or your clothing to ripple in a breeze."
|
||||||
@@ -820,7 +817,7 @@
|
|||||||
{
|
{
|
||||||
"type": "timed",
|
"type": "timed",
|
||||||
"value": 1,
|
"value": 1,
|
||||||
"unit": "minutes"
|
"unit": "minute"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"classes": ["artificer", "bard", "sorcerer", "warlock", "wizard"],
|
"classes": ["artificer", "bard", "sorcerer", "warlock", "wizard"],
|
||||||
@@ -861,7 +858,7 @@
|
|||||||
{
|
{
|
||||||
"type": "timed",
|
"type": "timed",
|
||||||
"value": 1,
|
"value": 1,
|
||||||
"unit": "minutes"
|
"unit": "minute"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"classes": ["artificer", "druid", "warlock"],
|
"classes": ["artificer", "druid", "warlock"],
|
||||||
@@ -882,7 +879,7 @@
|
|||||||
"ritual": false,
|
"ritual": false,
|
||||||
"casting_time": {
|
"casting_time": {
|
||||||
"value": 1,
|
"value": 1,
|
||||||
"unit": "minutes"
|
"unit": "minute"
|
||||||
},
|
},
|
||||||
"range": {
|
"range": {
|
||||||
"type": "touch"
|
"type": "touch"
|
||||||
@@ -1014,7 +1011,7 @@
|
|||||||
{
|
{
|
||||||
"type": "timed",
|
"type": "timed",
|
||||||
"value": 1,
|
"value": 1,
|
||||||
"unit": "minutes"
|
"unit": "minute"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"classes": ["bard", "sorcerer", "warlock", "wizard"],
|
"classes": ["bard", "sorcerer", "warlock", "wizard"],
|
||||||
@@ -1073,8 +1070,7 @@
|
|||||||
"entries": [
|
"entries": [
|
||||||
"You choose a portion of dirt or stone that you can see within range and that fits within a 5-foot cube. You manipulate it in one of the following ways:",
|
"You choose a portion of dirt or stone that you can see within range and that fits within a 5-foot cube. You manipulate it in one of the following ways:",
|
||||||
{
|
{
|
||||||
"type": "list",
|
"list": [
|
||||||
"items": [
|
|
||||||
"If you target an area of loose earth, you can instantaneously excavate it, move it along the ground, and deposit it up to 5 feet away. This movement doesn't have enough force to cause damage.",
|
"If you target an area of loose earth, you can instantaneously excavate it, move it along the ground, and deposit it up to 5 feet away. This movement doesn't have enough force to cause damage.",
|
||||||
"You cause shapes, colors, or both to appear on the dirt or stone, spelling out words, creating images, or shaping patterns. The changes last for 1 hour.",
|
"You cause shapes, colors, or both to appear on the dirt or stone, spelling out words, creating images, or shaping patterns. The changes last for 1 hour.",
|
||||||
"If the dirt or stone you target is on the ground, you cause it to become difficult terrain. Alternatively, you can cause the ground to become normal terrain if it is already difficult terrain. This change lasts for 1 hour."
|
"If the dirt or stone you target is on the ground, you cause it to become difficult terrain. Alternatively, you can cause the ground to become normal terrain if it is already difficult terrain. This change lasts for 1 hour."
|
||||||
@@ -1162,8 +1158,7 @@
|
|||||||
"entries": [
|
"entries": [
|
||||||
"This spell is a minor magical trick that novice spellcasters use for practice. You create one of the following magical effects within range:",
|
"This spell is a minor magical trick that novice spellcasters use for practice. You create one of the following magical effects within range:",
|
||||||
{
|
{
|
||||||
"type": "list",
|
"list": [
|
||||||
"items": [
|
|
||||||
"You create an instantaneous, harmless sensory effect, such as a shower of sparks, a puff of wind, faint musical notes, or an odd odor.",
|
"You create an instantaneous, harmless sensory effect, such as a shower of sparks, a puff of wind, faint musical notes, or an odd odor.",
|
||||||
"You instantaneously light or snuff out a candle, a torch, or a small campfire.",
|
"You instantaneously light or snuff out a candle, a torch, or a small campfire.",
|
||||||
"You instantaneously clean or soil an object no larger than 1 cubic foot.",
|
"You instantaneously clean or soil an object no larger than 1 cubic foot.",
|
||||||
@@ -1402,7 +1397,7 @@
|
|||||||
"type": "instantaneous"
|
"type": "instantaneous"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"classes": [],
|
"classes": ["wizard"],
|
||||||
"sources": [
|
"sources": [
|
||||||
{ "source": "EGW", "page": 189 }
|
{ "source": "EGW", "page": 189 }
|
||||||
],
|
],
|
||||||
@@ -1455,8 +1450,7 @@
|
|||||||
"entries": [
|
"entries": [
|
||||||
"You choose an area of water that you can see within range and that fits within a 5-foot cube. You manipulate it in one of the following ways:",
|
"You choose an area of water that you can see within range and that fits within a 5-foot cube. You manipulate it in one of the following ways:",
|
||||||
{
|
{
|
||||||
"type": "list",
|
"list": [
|
||||||
"items": [
|
|
||||||
"You instantaneously move or otherwise change the flow of the water as you direct, up to 5 feet in any direction. This movement doesn't have enough force to cause damage.",
|
"You instantaneously move or otherwise change the flow of the water as you direct, up to 5 feet in any direction. This movement doesn't have enough force to cause damage.",
|
||||||
"You cause the water to form into simple shapes and animate at your direction. This change lasts for 1 hour.",
|
"You cause the water to form into simple shapes and animate at your direction. This change lasts for 1 hour.",
|
||||||
"You change the water's color or opacity. The water must be changed in the same way throughout. This change lasts for 1 hour.",
|
"You change the water's color or opacity. The water must be changed in the same way throughout. This change lasts for 1 hour.",
|
||||||
@@ -1493,7 +1487,7 @@
|
|||||||
{
|
{
|
||||||
"type": "timed",
|
"type": "timed",
|
||||||
"value": 1,
|
"value": 1,
|
||||||
"unit": "minutes"
|
"unit": "minute"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"classes": ["druid"],
|
"classes": ["druid"],
|
||||||
@@ -1642,7 +1636,7 @@
|
|||||||
{
|
{
|
||||||
"type": "timed",
|
"type": "timed",
|
||||||
"value": 1,
|
"value": 1,
|
||||||
"unit": "minutes"
|
"unit": "minute"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"classes": ["cleric"],
|
"classes": ["cleric"],
|
||||||
@@ -1654,17 +1648,16 @@
|
|||||||
"entries": [
|
"entries": [
|
||||||
"You manifest a minor wonder, a sign of supernatural power, within range. You create one of the following magical effects within range:",
|
"You manifest a minor wonder, a sign of supernatural power, within range. You create one of the following magical effects within range:",
|
||||||
{
|
{
|
||||||
"type": "list",
|
"list": [
|
||||||
"items": [
|
"Your voice booms up to three times as loud as normal for 1 minute.",
|
||||||
"Your voice booms up to three times as loud as normal for 1 minutes.",
|
"You cause flames to flicker, brighten, dim, or change color for 1 minute.",
|
||||||
"You cause flames to flicker, brighten, dim, or change color for 1 minutes.",
|
"You cause harmless tremors in the ground for 1 minute.",
|
||||||
"You cause harmless tremors in the ground for 1 minutes.",
|
|
||||||
"You create an instantaneous sound that originates from a point of your choice within range, such as a rumble of thunder, the cry of a raven, or ominous whispers.",
|
"You create an instantaneous sound that originates from a point of your choice within range, such as a rumble of thunder, the cry of a raven, or ominous whispers.",
|
||||||
"You instantaneously cause an unlocked door or window to fly open or slam shut.",
|
"You instantaneously cause an unlocked door or window to fly open or slam shut.",
|
||||||
"You alter the appearance of your eyes for 1 minutes."
|
"You alter the appearance of your eyes for 1 minute."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"If you cast this spell multiple times, you can have up to three of its 1-minutes effects active at a time, and you can dismiss such an effect as an action."
|
"If you cast this spell multiple times, you can have up to three of its 1-minute effects active at a time, and you can dismiss such an effect as an action."
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -51,6 +51,6 @@ pub fn connection() -> Result<DbConnection, ServiceError> {
|
|||||||
.map_err(|e| ServiceError::new(500, format!("Failed getting db connection: {}", e)))
|
.map_err(|e| ServiceError::new(500, format!("Failed getting db connection: {}", e)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_data() {
|
pub fn load_data(data_dir_path: &str) {
|
||||||
spells::load_data();
|
spells::load_data(data_dir_path);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,46 +2,57 @@ mod model;
|
|||||||
mod routes;
|
mod routes;
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
|
use std::{fs::{metadata, File, read_dir}, path::Path, io::BufReader};
|
||||||
|
|
||||||
|
use log::{warn, trace};
|
||||||
pub use model::*;
|
pub use model::*;
|
||||||
pub use types::*;
|
pub use types::*;
|
||||||
pub use routes::init_routes;
|
pub use routes::init_routes;
|
||||||
|
|
||||||
pub fn load_data() {
|
pub fn load_data(data_dir_path: &str) {
|
||||||
let root_path = std::env::current_dir().unwrap();
|
if Path::new(data_dir_path).exists() {
|
||||||
let files = [
|
let meta = metadata(data_dir_path).unwrap();
|
||||||
"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"
|
if meta.is_dir() {
|
||||||
];
|
let spells_dir_path = format!("{}/spells", data_dir_path);
|
||||||
let mut spells: Vec<Spell> = vec![];
|
if Path::new(&spells_dir_path).exists() {
|
||||||
for file in files {
|
let meta = metadata(&spells_dir_path).unwrap();
|
||||||
let mut data_path = std::path::PathBuf::from(&root_path);
|
if meta.is_dir() {
|
||||||
data_path.push(format!("data/spells/{}", file));
|
for entry in read_dir(&spells_dir_path).unwrap() {
|
||||||
let path = data_path.to_str().unwrap();
|
let entry = entry.unwrap();
|
||||||
match std::fs::read_to_string(path) {
|
let path = entry.path();
|
||||||
Ok(data) => {
|
if path.is_file() {
|
||||||
log::debug!("Loading spells from {}", path);
|
let file = File::open(path).unwrap();
|
||||||
match serde_json::from_str::<serde_json::Value>(&data) {
|
let reader = BufReader::new(file);
|
||||||
Ok(json) => {
|
let result: Result<Vec<Spell>, serde_json::Error> = serde_json::from_reader(reader);
|
||||||
match serde_json::from_value::<Vec<Spell>>(json) {
|
match result {
|
||||||
Ok(mut new_spells) => spells.append(&mut new_spells),
|
Ok(spells) => {
|
||||||
Err(err) => log::error!("Failed to parse spells data: {}", err)
|
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 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)]
|
#[derive(Queryable, QueryableByName, Serialize, Deserialize)]
|
||||||
#[diesel(table_name = spells)]
|
#[diesel(table_name = spells)]
|
||||||
@@ -198,6 +198,8 @@ pub struct Spell {
|
|||||||
pub ritual: bool,
|
pub ritual: bool,
|
||||||
pub casting_time: CastingTime,
|
pub casting_time: CastingTime,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub effect: Option<Effect>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub saving_throw: Option<Vec<AbilityType>>,
|
pub saving_throw: Option<Vec<AbilityType>>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub attack_type: Option<SpellAttackType>,
|
pub attack_type: Option<SpellAttackType>,
|
||||||
@@ -232,7 +234,8 @@ impl From<QuerySpell> for Spell {
|
|||||||
school: SchoolType::Abjuration,
|
school: SchoolType::Abjuration,
|
||||||
level: 0,
|
level: 0,
|
||||||
ritual: false,
|
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,
|
saving_throw: None,
|
||||||
attack_type: None,
|
attack_type: None,
|
||||||
damage_inflict: None,
|
damage_inflict: None,
|
||||||
|
|||||||
@@ -58,50 +58,10 @@ impl FromStr for SchoolType {
|
|||||||
pub struct CastingTime {
|
pub struct CastingTime {
|
||||||
pub value: i32,
|
pub value: i32,
|
||||||
#[serde(rename = "unit")]
|
#[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)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub enum SpellAttackType {
|
pub enum SpellAttackType {
|
||||||
#[serde(rename = "melee")]
|
#[serde(rename = "melee")]
|
||||||
@@ -303,8 +263,15 @@ pub struct Description {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Entry {
|
pub struct Entry {
|
||||||
pub entry_type: String,
|
pub text: Option<Vec<String>>,
|
||||||
pub items: 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 {
|
impl<'de> Deserialize<'de> for Entry {
|
||||||
@@ -312,36 +279,82 @@ impl<'de> Deserialize<'de> for Entry {
|
|||||||
let value = serde_json::Value::deserialize(deserializer)?;
|
let value = serde_json::Value::deserialize(deserializer)?;
|
||||||
match value {
|
match value {
|
||||||
serde_json::Value::String(s) => Ok(Entry {
|
serde_json::Value::String(s) => Ok(Entry {
|
||||||
entry_type: "string".to_string(),
|
text: Some(vec![s]),
|
||||||
items: vec![s]
|
list: None,
|
||||||
|
table: None,
|
||||||
}),
|
}),
|
||||||
serde_json::Value::Object(o) => {
|
serde_json::Value::Object(o) => {
|
||||||
let entry_type = match o.get("type") {
|
let list = match o.get("list") {
|
||||||
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") {
|
|
||||||
Some(i) => match i.as_array() {
|
Some(i) => match i.as_array() {
|
||||||
Some(a) => {
|
Some(a) => {
|
||||||
let mut items = Vec::new();
|
let mut list = Vec::new();
|
||||||
for item in a {
|
for item in a {
|
||||||
match item.as_str() {
|
match item.as_str() {
|
||||||
Some(s) => items.push(s.to_string()),
|
Some(s) => list.push(s.to_string()),
|
||||||
None => return Err(serde::de::Error::custom("Invalid entry item"))
|
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 {
|
Ok(Entry {
|
||||||
entry_type,
|
text: None,
|
||||||
items
|
list,
|
||||||
|
table
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
_ => Err(serde::de::Error::custom("Invalid entry"))
|
_ => Err(serde::de::Error::custom("Invalid entry"))
|
||||||
@@ -351,15 +364,17 @@ impl<'de> Deserialize<'de> for Entry {
|
|||||||
|
|
||||||
impl Serialize for Entry {
|
impl Serialize for Entry {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
|
||||||
match self.entry_type.as_str() {
|
let mut map = serializer.serialize_map(Some(1))?;
|
||||||
"string" => serializer.serialize_str(&self.items[0]),
|
if let Some(text) = &self.text {
|
||||||
_ => {
|
map.serialize_entry("text", text)?;
|
||||||
let mut map = serializer.serialize_map(Some(2))?;
|
|
||||||
map.serialize_entry("type", &self.entry_type)?;
|
|
||||||
map.serialize_entry("items", &self.items)?;
|
|
||||||
map.end()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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>,
|
pub materials_cost: Option<i32>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub materials_consumed: Option<bool>
|
pub materials_consumed: Option<bool>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Effect {
|
||||||
|
pub effect_type: Option<String>
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use actix_cors::Cors;
|
|||||||
use actix_web::{HttpServer, App};
|
use actix_web::{HttpServer, App};
|
||||||
|
|
||||||
use dotenv::dotenv;
|
use dotenv::dotenv;
|
||||||
use log::{error, info};
|
use log::{error, info, warn};
|
||||||
|
|
||||||
mod db;
|
mod db;
|
||||||
|
|
||||||
@@ -17,7 +17,10 @@ async fn main() -> std::io::Result<()> {
|
|||||||
dotenv().ok();
|
dotenv().ok();
|
||||||
env_logger::init_from_env(env_logger::Env::default().filter_or("RUST_LOG", "warn,siren=info"));
|
env_logger::init_from_env(env_logger::Env::default().filter_or("RUST_LOG", "warn,siren=info"));
|
||||||
db::init();
|
db::init();
|
||||||
db::load_data();
|
match env::var("DATA_DIR_PATH") {
|
||||||
|
Ok(data_dir_path) => db::load_data(&data_dir_path),
|
||||||
|
Err(err) => warn!("Unable to load initial database data: {}", err)
|
||||||
|
};
|
||||||
|
|
||||||
let host = env::var("SERVICE_HOST").unwrap_or("localhost".to_string());
|
let host = env::var("SERVICE_HOST").unwrap_or("localhost".to_string());
|
||||||
let port = env::var("SERVICE_PORT").unwrap_or("5000".to_string());
|
let port = env::var("SERVICE_PORT").unwrap_or("5000".to_string());
|
||||||
|
|||||||
Reference in New Issue
Block a user