diff --git a/Cargo.toml b/Cargo.toml index ae2080f..9820f8b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,8 @@ tracing = "0.1" [dependencies.warp] version = "0.3" -features = ["tls"] +features = ["tls", "multipart"] +default-features = false [dependencies.assembly-core] version = "0.2.1" diff --git a/src/api/adapter.rs b/src/api/adapter.rs index 89b8c59..4179501 100644 --- a/src/api/adapter.rs +++ b/src/api/adapter.rs @@ -1,3 +1,5 @@ +use std::collections::BTreeMap; + use assembly_data::xml::localization::LocaleNode; use paradox_typed_db::typed_rows::TypedRow; use serde::{ser::SerializeMap, Serialize}; @@ -12,6 +14,12 @@ impl FindHash for &T { } } +impl FindHash for BTreeMap { + fn find_hash(&self, v: i32) -> Option { + self.get(&v).copied() + } +} + #[derive(Debug, Copy, Clone)] pub(crate) struct IdentityHash; diff --git a/src/api/rev/behaviors.rs b/src/api/rev/behaviors.rs new file mode 100644 index 0000000..14fb12f --- /dev/null +++ b/src/api/rev/behaviors.rs @@ -0,0 +1,106 @@ +use assembly_core::buffer::CastError; +use paradox_typed_db::{ + typed_rows::{BehaviorTemplateRow, TypedRow}, + typed_tables::{BehaviorParameterTable, BehaviorTemplateTable}, + TypedDatabase, +}; +use serde::ser::SerializeMap; +use serde::Serialize; + +use crate::api::map_res; +use std::{collections::BTreeSet, convert::Infallible}; + +use super::{Api, Ext, Rev}; +use warp::{ + filters::BoxedFilter, + reply::{Json, WithStatus}, + Filter, +}; + +#[derive(Clone)] +pub(crate) struct BehaviorParameters<'a, 'b> { + key: i32, + table: &'b BehaviorParameterTable<'a>, +} + +impl Serialize for BehaviorParameters<'_, '_> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut m = serializer.serialize_map(None)?; + for e in self.table.key_iter(self.key) { + m.serialize_key(e.parameter_id())?; + m.serialize_value(&e.value())?; + } + m.end() + } +} + +#[derive(Clone, Serialize)] +pub(crate) struct Behavior<'a, 'b> { + #[serde(flatten)] + template: Option>, + parameters: BehaviorParameters<'a, 'b>, +} + +struct EmbeddedBehaviors<'a, 'b> { + keys: &'b BTreeSet, + table_templates: &'b BehaviorTemplateTable<'a>, + table_parameters: &'b BehaviorParameterTable<'a>, +} + +impl Serialize for EmbeddedBehaviors<'_, '_> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut m = serializer.serialize_map(Some(self.keys.len()))?; + for &behavior_id in self.keys { + m.serialize_key(&behavior_id)?; + let b = Behavior { + template: BehaviorTemplateRow::get( + self.table_templates, + behavior_id, + behavior_id, + self.table_templates.col_behavior_id, + ), + parameters: BehaviorParameters { + key: behavior_id, + table: self.table_parameters, + }, + }; + m.serialize_value(&b)?; + } + m.end() + } +} + +fn rev_behavior_api(db: &TypedDatabase, rev: Rev, behavior_id: i32) -> Result { + let data = rev.inner.behaviors.get(&behavior_id); + let set = rev.inner.get_behavior_set(behavior_id); + let val = Api { + data, + embedded: EmbeddedBehaviors { + keys: &set, + table_templates: &db.behavior_templates, + table_parameters: &db.behavior_parameters, + }, + }; + Ok(warp::reply::json(&val)) +} + +pub(super) fn behaviors_api< + F: Filter + Send + Sync + Clone + 'static, +>( + rev: &F, +) -> BoxedFilter<(WithStatus,)> { + let rev_behaviors = rev.clone().and(warp::path("behaviors")); + let rev_behavior_id_base = rev_behaviors.and(warp::path::param::()); + let rev_behavior_id = rev_behavior_id_base + .and(warp::path::end()) + .map(rev_behavior_api) + .map(map_res); + + rev_behavior_id.boxed() +} diff --git a/src/api/rev/common.rs b/src/api/rev/common.rs new file mode 100644 index 0000000..404a369 --- /dev/null +++ b/src/api/rev/common.rs @@ -0,0 +1,96 @@ +use std::collections::HashMap; + +use paradox_typed_db::{ + typed_rows::{MissionTaskRow, ObjectsRef}, + typed_tables::MissionTasksTable, +}; +use serde::Serialize; + +use crate::api::adapter::{FindHash, IdentityHash, TypedTableIterAdapter}; + +use super::data::MissionTaskUIDLookup; + +#[derive(Debug, Clone)] +pub struct MapFilter<'a, E> { + base: &'a HashMap, + keys: &'a [i32], +} + +pub(super) type ObjectsRefAdapter<'a, 'b> = + TypedTableIterAdapter<'a, 'b, ObjectsRef<'a, 'b>, IdentityHash, &'b [i32]>; + +#[derive(Serialize)] +pub(super) struct ObjectTypeEmbedded<'a, 'b> { + pub objects: ObjectsRefAdapter<'a, 'b>, +} + +impl<'a, E> MapFilter<'a, E> { + fn to_iter<'b: 'a>(&'b self) -> impl Iterator + 'b { + self.keys + .iter() + .filter_map(move |k| self.base.get(k).map(move |v| (*k, v))) + } +} + +impl<'a, E: Serialize> Serialize for MapFilter<'a, E> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.collect_map(self.to_iter()) + } +} + +pub(super) type MissionTaskHash<'b> = &'b HashMap; + +pub(super) type MissionTasks<'a, 'b> = + TypedTableIterAdapter<'a, 'b, MissionTaskRow<'a, 'b>, MissionTaskHash<'b>, &'b [i32]>; + +pub(super) struct MissionTaskIconsAdapter<'a, 'b> { + table: &'b MissionTasksTable<'a>, + key: i32, +} + +impl<'a, 'b> Serialize for MissionTaskIconsAdapter<'a, 'b> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.collect_seq(self.table.as_task_icon_iter(self.key)) + } +} + +#[derive(Clone)] +pub(super) struct MissionsTaskIconsAdapter<'a, 'b> { + table: &'b MissionTasksTable<'a>, + keys: &'b [i32], +} + +impl<'a, 'b> MissionsTaskIconsAdapter<'a, 'b> { + pub fn new(table: &'b MissionTasksTable<'a>, keys: &'b [i32]) -> Self { + Self { table, keys } + } +} + +impl<'a, 'b> Serialize for MissionsTaskIconsAdapter<'a, 'b> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.collect_map(self.keys.iter().copied().map(|key| { + ( + key, + MissionTaskIconsAdapter { + table: self.table, + key, + }, + ) + })) + } +} + +impl<'a> FindHash for HashMap { + fn find_hash(&self, v: i32) -> Option { + self.get(&v).map(|r| r.mission) + } +} diff --git a/src/api/rev/component_types.rs b/src/api/rev/component_types.rs new file mode 100644 index 0000000..509bc24 --- /dev/null +++ b/src/api/rev/component_types.rs @@ -0,0 +1,99 @@ +use std::convert::Infallible; + +use assembly_core::buffer::CastError; +use paradox_typed_db::TypedDatabase; +use serde::Serialize; +use warp::{ + filters::BoxedFilter, + reply::{Json, WithStatus}, + Filter, +}; + +use super::{ + common::{ObjectTypeEmbedded, ObjectsRefAdapter}, + Api, Ext, Rev, +}; +use crate::api::{map_opt_res, map_res}; + +#[derive(Serialize)] +struct Components { + components: Vec, +} + +fn rev_component_types_api(_db: &TypedDatabase, rev: Rev) -> Result { + let components: Vec = rev.inner.component_use.keys().copied().collect(); + let val = Components { components }; + Ok(warp::reply::json(&val)) +} + +fn rev_component_type_api( + _db: &TypedDatabase, + rev: Rev, + key: i32, +) -> Result, CastError> { + let val = rev.inner.component_use.get(&key); + Ok(val.map(|data| { + let keys: Vec = data + .components + .iter() + .flat_map(|(_, u)| u.lots.iter().copied()) + .collect(); + let embedded = ObjectTypeEmbedded { + objects: ObjectsRefAdapter::new(&_db.objects, &keys), + }; + warp::reply::json(&Api { data, embedded }) + })) +} + +fn rev_single_component_api( + _db: &TypedDatabase, + rev: Rev, + key: i32, + cid: i32, +) -> Result, CastError> { + let val = rev + .inner + .component_use + .get(&key) + .and_then(|c| c.components.get(&cid)); + Ok(val.map(warp::reply::json)) +} + +pub(super) fn component_types_api< + F: Filter + Send + Sync + Clone + 'static, +>( + rev: &F, +) -> BoxedFilter<(WithStatus,)> { + let rev_component_types_base = rev.clone().and(warp::path("component_types")); + + let rev_single_component_type = rev_component_types_base + .clone() + .and(warp::path::param()) + .and(warp::path::param()) + .and(warp::path::end()) + .map(rev_single_component_api) + .map(map_opt_res) + .boxed(); + + let rev_component_type = rev_component_types_base + .clone() + .and(warp::path::param()) + .and(warp::path::end()) + .map(rev_component_type_api) + .map(map_opt_res) + .boxed(); + + let rev_component_types_list = rev_component_types_base + .clone() + .and(warp::path::end()) + .map(rev_component_types_api) + .map(map_res) + .boxed(); + + rev_single_component_type + .or(rev_component_type) + .unify() + .or(rev_component_types_list) + .unify() + .boxed() +} diff --git a/src/api/rev/data.rs b/src/api/rev/data.rs index d7fe981..3df6218 100644 --- a/src/api/rev/data.rs +++ b/src/api/rev/data.rs @@ -57,15 +57,18 @@ pub struct ActivityRev { rebuild: Vec, } -#[derive(Debug, Clone, Serialize)] -pub struct LootTableItem { - pub itemid: i32, - pub id: i32, +#[derive(Debug, Default, Clone, Serialize)] +pub struct LootTableIndexRev { + /// This is a map from `LootTable::id` to `LootTable::itemid` for the current LootTableIndex + pub items: BTreeMap, } #[derive(Debug, Default, Clone, Serialize)] -pub struct LootTableIndexRev { - pub items: Vec, +pub struct FactionRev { + /// DestructibleComponents have the current ID in `factionList` + pub destructible_list: Vec, + /// DestructibleComponents have the current ID in `faction` + pub destructible: Vec, } #[derive(Debug, Clone, Serialize)] @@ -191,7 +194,21 @@ impl ReverseLookup { let itemid = l.itemid(); let id = l.id(); let entry = loot_table_index.entry(lti).or_default(); - entry.items.push(LootTableItem { itemid, id }); + entry.items.insert(id, itemid); + } + + let mut factions: BTreeMap = BTreeMap::new(); + for d in db.destructible_component.row_iter() { + if let Some(faction) = d.faction() { + let entry = factions.entry(faction).or_default(); + entry.destructible.push(d.id()); + } + + let faction_list: i32 = d.faction_list().decode().parse().unwrap(); + if faction_list >= 0 { + let entry = factions.entry(faction_list).or_default(); + entry.destructible_list.push(d.id()); + } } Self { diff --git a/src/api/rev/missions.rs b/src/api/rev/missions.rs new file mode 100644 index 0000000..488bc08 --- /dev/null +++ b/src/api/rev/missions.rs @@ -0,0 +1,194 @@ +use std::{borrow::Borrow, collections::BTreeMap, convert::Infallible}; + +use assembly_core::buffer::CastError; +use assembly_data::xml::localization::LocaleNode; +use paradox_typed_db::{typed_rows::MissionsRow, TypedDatabase}; +use serde::{ser::SerializeMap, Serialize}; +use warp::{ + filters::BoxedFilter, + reply::{Json, WithStatus}, + Filter, +}; + +use crate::api::{ + adapter::{IdentityHash, LocaleTableAdapter, TypedTableIterAdapter}, + map_res, PercentDecoded, +}; + +use super::{common::MissionsTaskIconsAdapter, Api, Rev}; + +#[derive(Debug, Clone)] +struct MissionSubtypesAdapter<'a>(&'a BTreeMap>); + +impl<'a> Serialize for MissionSubtypesAdapter<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.collect_seq(self.0.keys()) + } +} + +#[derive(Debug, Clone)] +struct MissionTypesAdapter<'a>(&'a BTreeMap>>); + +impl<'a> Serialize for MissionTypesAdapter<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut m = serializer.serialize_map(Some(self.0.len()))?; + for (key, value) in self.0 { + m.serialize_entry(key, &MissionSubtypesAdapter(value))?; + } + m.end() + } +} + +fn rev_mission_types_api(_db: &TypedDatabase, rev: Rev) -> Result { + Ok(warp::reply::json(&MissionTypesAdapter( + &rev.inner.mission_types, + ))) +} + +#[derive(Clone, Serialize)] +struct MissionLocale<'b> { + #[serde(rename = "MissionText")] + mission_text: LocaleTableAdapter<'b>, + #[serde(rename = "Missions")] + missions: LocaleTableAdapter<'b>, +} + +impl<'b> MissionLocale<'b> { + pub fn new(node: &'b LocaleNode, keys: &'b [i32]) -> Self { + Self { + mission_text: LocaleTableAdapter::new( + node.str_children.get("MissionText").unwrap(), + keys, + ), + missions: LocaleTableAdapter::new(node.str_children.get("Missions").unwrap(), keys), + } + } +} + +type MissionsAdapter<'a, 'b> = + TypedTableIterAdapter<'a, 'b, MissionsRow<'a, 'b>, IdentityHash, &'b [i32]>; + +/// This is the root type that holds all embedded value for the `mission_types` lookup +#[derive(Clone, Serialize)] +struct MissionTypesEmbedded<'a, 'b> { + #[serde(rename = "Missions")] + missions: MissionsAdapter<'a, 'b>, + #[serde(rename = "MissionTaskIcons")] + mission_task_icons: MissionsTaskIconsAdapter<'a, 'b>, + locale: MissionLocale<'b>, +} + +#[derive(Debug, Clone, Serialize)] +struct Subtypes<'a> { + subtypes: MissionSubtypesAdapter<'a>, +} + +#[derive(Debug, Clone, Serialize)] +struct MissionIDList<'b> { + mission_ids: &'b [i32], +} + +fn missions_reply<'a, 'b>( + db: &'b TypedDatabase<'a>, + mission_ids: &'b [i32], +) -> Api, MissionTypesEmbedded<'a, 'b>> { + Api { + data: MissionIDList { mission_ids }, + embedded: MissionTypesEmbedded { + missions: MissionsAdapter::new(&db.missions, mission_ids), + mission_task_icons: MissionsTaskIconsAdapter::new(&db.mission_tasks, mission_ids), + locale: MissionLocale::new(&db.locale, mission_ids), + }, + } +} + +fn rev_mission_type_api( + db: &TypedDatabase, + rev: Rev, + d_type: PercentDecoded, +) -> Result { + let key: &String = d_type.borrow(); + match rev.inner.mission_types.get(key) { + Some(t) => match t.get("") { + Some(missions) => Ok(warp::reply::json(&missions_reply(db, missions))), + None => Ok(warp::reply::json(&Subtypes { + subtypes: MissionSubtypesAdapter(t), + })), + }, + None => Ok(warp::reply::json(&())), + } +} + +fn rev_mission_subtype_api( + db: &TypedDatabase, + rev: Rev, + d_type: PercentDecoded, + d_subtype: PercentDecoded, +) -> Result { + let t_key: &String = d_type.borrow(); + let t = rev.inner.mission_types.get(t_key); + let s_key: &String = d_subtype.borrow(); + let s = t.and_then(|t| t.get(s_key)); + let m = s.map(|missions| missions_reply(db, missions)); + Ok(warp::reply::json(&m)) +} + +fn rev_mission_types_full_api(_db: &TypedDatabase, rev: Rev) -> Result { + Ok(warp::reply::json(&rev.inner.mission_types)) +} + +pub(super) fn mission_types_api< + F: Filter + Send + Sync + Clone + 'static, +>( + rev: &F, +) -> BoxedFilter<(WithStatus,)> { + let rev_mission_types_base = rev.clone().and(warp::path("mission_types")); + + let rev_mission_types_full = rev_mission_types_base + .clone() + .and(warp::path("full")) + .and(warp::path::end()) + .map(rev_mission_types_full_api) + .map(map_res) + .boxed(); + + let rev_mission_type = rev_mission_types_base + .clone() + .and(warp::path::param()) + .and(warp::path::end()) + .map(rev_mission_type_api) + .map(map_res) + .boxed(); + + let rev_mission_subtype = rev_mission_types_base + .clone() + .and(warp::path::param()) + .and(warp::path::param()) + .and(warp::path::end()) + .map(rev_mission_subtype_api) + .map(map_res) + .boxed(); + + let rev_mission_types_list = rev_mission_types_base + .clone() + .and(warp::path::end()) + .map(rev_mission_types_api) + .map(map_res) + .boxed(); + + let rev_mission_types = rev_mission_types_full + .or(rev_mission_type) + .unify() + .or(rev_mission_subtype) + .unify() + .or(rev_mission_types_list) + .unify(); + + rev_mission_types.boxed() +} diff --git a/src/api/rev/mod.rs b/src/api/rev/mod.rs index 5595160..0cd389b 100644 --- a/src/api/rev/mod.rs +++ b/src/api/rev/mod.rs @@ -4,31 +4,27 @@ //! database lookups by some specific ID such as an "object template id" or a "skill id" //! and produce data from multiple tables. use assembly_core::buffer::CastError; -use assembly_data::xml::localization::LocaleNode; -use paradox_typed_db::{ - typed_rows::{BehaviorTemplateRow, MissionTaskRow, MissionsRow, ObjectsRef, TypedRow}, - typed_tables::{BehaviorParameterTable, BehaviorTemplateTable, MissionTasksTable}, - TypedDatabase, -}; -use serde::{ser::SerializeMap, Serialize}; -use std::{ - borrow::Borrow, - collections::{BTreeMap, BTreeSet, HashMap}, - convert::Infallible, -}; +use paradox_typed_db::TypedDatabase; +use serde::Serialize; +use std::convert::Infallible; use warp::{ filters::BoxedFilter, reply::{Json, WithStatus}, Filter, }; +mod common; mod data; +mod behaviors; +mod component_types; +mod missions; +mod object_types; +mod skills; + pub use data::ReverseLookup; -use self::data::MissionTaskUIDLookup; -use super::{adapter::LocaleTableAdapter, map_opt_res, map_res, tydb_filter, PercentDecoded}; -use crate::api::adapter::{FindHash, IdentityHash, TypedTableIterAdapter}; +use super::{map_res, tydb_filter}; #[derive(Debug, Clone, Serialize)] pub struct Api { @@ -38,388 +34,10 @@ pub struct Api { embedded: E, } -#[derive(Debug, Clone)] -pub struct MapFilter<'a, E> { - base: &'a HashMap, - keys: &'a [i32], -} - -impl<'a, E> MapFilter<'a, E> { - fn to_iter<'b: 'a>(&'b self) -> impl Iterator + 'b { - self.keys - .iter() - .filter_map(move |k| self.base.get(k).map(move |v| (*k, v))) - } -} - -impl<'a, E: Serialize> Serialize for MapFilter<'a, E> { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.collect_map(self.to_iter()) - } -} - -type MissionTaskHash<'b> = &'b HashMap; - -type MissionTasks<'a, 'b> = - TypedTableIterAdapter<'a, 'b, MissionTaskRow<'a, 'b>, MissionTaskHash<'b>, &'b [i32]>; - -type MissionsAdapter<'a, 'b> = - TypedTableIterAdapter<'a, 'b, MissionsRow<'a, 'b>, IdentityHash, &'b [i32]>; - -struct MissionTaskIconsAdapter<'a, 'b> { - table: &'b MissionTasksTable<'a>, - key: i32, -} - -impl<'a, 'b> Serialize for MissionTaskIconsAdapter<'a, 'b> { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.collect_seq(self.table.as_task_icon_iter(self.key)) - } -} - -#[derive(Clone)] -struct MissionsTaskIconsAdapter<'a, 'b> { - table: &'b MissionTasksTable<'a>, - keys: &'b [i32], -} - -impl<'a, 'b> MissionsTaskIconsAdapter<'a, 'b> { - pub fn new(table: &'b MissionTasksTable<'a>, keys: &'b [i32]) -> Self { - Self { table, keys } - } -} - -impl<'a, 'b> Serialize for MissionsTaskIconsAdapter<'a, 'b> { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.collect_map(self.keys.iter().copied().map(|key| { - ( - key, - MissionTaskIconsAdapter { - table: self.table, - key, - }, - ) - })) - } -} - -#[derive(Clone, Serialize)] -pub struct SkillIDEmbedded<'a, 'b> { - #[serde(rename = "MissionTasks")] - mission_tasks: MissionTasks<'a, 'b>, - //MapFilter<'a, MissionTaskUIDLookup>, -} - -impl<'a> FindHash for HashMap { - fn find_hash(&self, v: i32) -> Option { - self.get(&v).map(|r| r.mission) - } -} - -#[derive(Clone)] -pub(crate) struct BehaviorParameters<'a, 'b> { - key: i32, - table: &'b BehaviorParameterTable<'a>, -} - -impl Serialize for BehaviorParameters<'_, '_> { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let mut m = serializer.serialize_map(None)?; - for e in self.table.key_iter(self.key) { - m.serialize_key(e.parameter_id())?; - m.serialize_value(&e.value())?; - } - m.end() - } -} - -#[derive(Clone, Serialize)] -pub(crate) struct Behavior<'a, 'b> { - #[serde(flatten)] - template: Option>, - parameters: BehaviorParameters<'a, 'b>, -} - fn rev_api(_db: &TypedDatabase, _rev: Rev) -> Result { Ok(warp::reply::json(&["skill_ids"])) } -struct EmbeddedBehaviors<'a, 'b> { - keys: &'b BTreeSet, - table_templates: &'b BehaviorTemplateTable<'a>, - table_parameters: &'b BehaviorParameterTable<'a>, -} - -impl Serialize for EmbeddedBehaviors<'_, '_> { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let mut m = serializer.serialize_map(Some(self.keys.len()))?; - for &behavior_id in self.keys { - m.serialize_key(&behavior_id)?; - let b = Behavior { - template: BehaviorTemplateRow::get( - self.table_templates, - behavior_id, - behavior_id, - self.table_templates.col_behavior_id, - ), - parameters: BehaviorParameters { - key: behavior_id, - table: self.table_parameters, - }, - }; - m.serialize_value(&b)?; - } - m.end() - } -} - -#[derive(Debug, Clone)] -struct MissionSubtypes<'a>(&'a BTreeMap>); -#[derive(Debug, Clone)] -struct MissionTypes<'a>(&'a BTreeMap>>); - -impl<'a> Serialize for MissionSubtypes<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.collect_seq(self.0.keys()) - } -} - -impl<'a> Serialize for MissionTypes<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let mut m = serializer.serialize_map(Some(self.0.len()))?; - for (key, value) in self.0 { - m.serialize_entry(key, &MissionSubtypes(value))?; - } - m.end() - } -} - -fn rev_mission_types_api(_db: &TypedDatabase, rev: Rev) -> Result { - Ok(warp::reply::json(&MissionTypes(&rev.inner.mission_types))) -} - -#[derive(Clone, Serialize)] -struct MissionLocale<'b> { - #[serde(rename = "MissionText")] - mission_text: LocaleTableAdapter<'b>, - #[serde(rename = "Missions")] - missions: LocaleTableAdapter<'b>, -} - -impl<'b> MissionLocale<'b> { - pub fn new(node: &'b LocaleNode, keys: &'b [i32]) -> Self { - Self { - mission_text: LocaleTableAdapter::new( - node.str_children.get("MissionText").unwrap(), - keys, - ), - missions: LocaleTableAdapter::new(node.str_children.get("Missions").unwrap(), keys), - } - } -} - -/// This is the root type that holds all embedded value for the `mission_types` lookup -#[derive(Clone, Serialize)] -struct MissionTypesEmbedded<'a, 'b> { - #[serde(rename = "Missions")] - missions: MissionsAdapter<'a, 'b>, - #[serde(rename = "MissionTaskIcons")] - mission_task_icons: MissionsTaskIconsAdapter<'a, 'b>, - locale: MissionLocale<'b>, -} - -#[derive(Debug, Clone, Serialize)] -struct Subtypes<'a> { - subtypes: MissionSubtypes<'a>, -} - -#[derive(Debug, Clone, Serialize)] -struct MissionIDList<'b> { - mission_ids: &'b [i32], -} - -fn missions_reply<'a, 'b>( - db: &'b TypedDatabase<'a>, - mission_ids: &'b [i32], -) -> Api, MissionTypesEmbedded<'a, 'b>> { - Api { - data: MissionIDList { mission_ids }, - embedded: MissionTypesEmbedded { - missions: MissionsAdapter::new(&db.missions, mission_ids), - mission_task_icons: MissionsTaskIconsAdapter::new(&db.mission_tasks, mission_ids), - locale: MissionLocale::new(&db.locale, mission_ids), - }, - } -} - -fn rev_mission_type_api( - db: &TypedDatabase, - rev: Rev, - d_type: PercentDecoded, -) -> Result { - let key: &String = d_type.borrow(); - match rev.inner.mission_types.get(key) { - Some(t) => match t.get("") { - Some(missions) => Ok(warp::reply::json(&missions_reply(db, missions))), - None => Ok(warp::reply::json(&Subtypes { - subtypes: MissionSubtypes(t), - })), - }, - None => Ok(warp::reply::json(&())), - } -} - -fn rev_mission_subtype_api( - db: &TypedDatabase, - rev: Rev, - d_type: PercentDecoded, - d_subtype: PercentDecoded, -) -> Result { - let t_key: &String = d_type.borrow(); - let t = rev.inner.mission_types.get(t_key); - let s_key: &String = d_subtype.borrow(); - let s = t.and_then(|t| t.get(s_key)); - let m = s.map(|missions| missions_reply(db, missions)); - Ok(warp::reply::json(&m)) -} - -fn rev_mission_types_full_api(_db: &TypedDatabase, rev: Rev) -> Result { - Ok(warp::reply::json(&rev.inner.mission_types)) -} - -#[derive(Serialize)] -struct ObjectIDs<'a, T> { - object_ids: &'a [T], -} - -type ObjectsRefAdapter<'a, 'b> = - TypedTableIterAdapter<'a, 'b, ObjectsRef<'a, 'b>, IdentityHash, &'b [i32]>; - -#[derive(Serialize)] -struct ObjectTypeEmbedded<'a, 'b> { - objects: ObjectsRefAdapter<'a, 'b>, -} - -fn rev_object_type_api( - db: &TypedDatabase, - rev: Rev, - ty: PercentDecoded, -) -> Result, CastError> { - let key: &String = ty.borrow(); - tracing::info!("{}", key); - Ok(rev.inner.object_types.get(key).map(|objects| { - let rep = Api { - data: ObjectIDs { - object_ids: objects.as_ref(), - }, - embedded: ObjectTypeEmbedded { - objects: TypedTableIterAdapter::new(&db.objects, objects), - }, - }; - warp::reply::json(&rep) - })) -} - -fn rev_object_types_api(_db: &TypedDatabase, rev: Rev) -> Result { - let keys: Vec<_> = rev.inner.object_types.keys().collect(); - Ok(warp::reply::json(&keys)) -} - -#[derive(Serialize)] -struct Components { - components: Vec, -} - -fn rev_component_types_api(_db: &TypedDatabase, rev: Rev) -> Result { - let components: Vec = rev.inner.component_use.keys().copied().collect(); - let val = Components { components }; - Ok(warp::reply::json(&val)) -} - -fn rev_component_type_api( - _db: &TypedDatabase, - rev: Rev, - key: i32, -) -> Result, CastError> { - let val = rev.inner.component_use.get(&key); - Ok(val.map(|data| { - let keys: Vec = data - .components - .iter() - .flat_map(|(_, u)| u.lots.iter().copied()) - .collect(); - let embedded = ObjectTypeEmbedded { - objects: ObjectsRefAdapter::new(&_db.objects, &keys), - }; - warp::reply::json(&Api { data, embedded }) - })) -} - -fn rev_single_component_api( - _db: &TypedDatabase, - rev: Rev, - key: i32, - cid: i32, -) -> Result, CastError> { - let val = rev - .inner - .component_use - .get(&key) - .and_then(|c| c.components.get(&cid)); - Ok(val.map(warp::reply::json)) -} - -fn rev_behavior_api(db: &TypedDatabase, rev: Rev, behavior_id: i32) -> Result { - let data = rev.inner.behaviors.get(&behavior_id); - let set = rev.inner.get_behavior_set(behavior_id); - let val = Api { - data, - embedded: EmbeddedBehaviors { - keys: &set, - table_templates: &db.behavior_templates, - table_parameters: &db.behavior_parameters, - }, - }; - Ok(warp::reply::json(&val)) -} - -fn rev_skill_id_api(db: &'_ TypedDatabase, rev: Rev, skill_id: i32) -> Result { - let h = rev.inner.skill_ids.get(&skill_id).map(|data| { - let mission_tasks = MissionTasks { - index: &rev.inner.mission_task_uids, - keys: &data.mission_tasks[..], - table: &db.mission_tasks, - id_col: db.mission_tasks.col_uid, - }; - Api { - data, - embedded: SkillIDEmbedded { mission_tasks }, - } - }); - Ok(warp::reply::json(&h)) -} - #[derive(Debug, Copy, Clone)] pub(crate) struct Rev<'a> { inner: &'a ReverseLookup, @@ -433,145 +51,6 @@ fn rev_filter<'a>( type Ext = (&'static TypedDatabase<'static>, Rev<'static>); -fn skill_api + Send + Sync + Clone + 'static>( - rev: &F, -) -> BoxedFilter<(WithStatus,)> { - let rev_skill_ids = rev.clone().and(warp::path("skill_ids")); - let rev_skill_id_base = rev_skill_ids.and(warp::path::param::()); - let rev_skill_id = rev_skill_id_base - .and(warp::path::end()) - .map(rev_skill_id_api) - .map(map_res); - rev_skill_id.boxed() -} - -fn mission_types_api< - F: Filter + Send + Sync + Clone + 'static, ->( - rev: &F, -) -> BoxedFilter<(WithStatus,)> { - let rev_mission_types_base = rev.clone().and(warp::path("mission_types")); - - let rev_mission_types_full = rev_mission_types_base - .clone() - .and(warp::path("full")) - .and(warp::path::end()) - .map(rev_mission_types_full_api) - .map(map_res) - .boxed(); - - let rev_mission_type = rev_mission_types_base - .clone() - .and(warp::path::param()) - .and(warp::path::end()) - .map(rev_mission_type_api) - .map(map_res) - .boxed(); - - let rev_mission_subtype = rev_mission_types_base - .clone() - .and(warp::path::param()) - .and(warp::path::param()) - .and(warp::path::end()) - .map(rev_mission_subtype_api) - .map(map_res) - .boxed(); - - let rev_mission_types_list = rev_mission_types_base - .clone() - .and(warp::path::end()) - .map(rev_mission_types_api) - .map(map_res) - .boxed(); - - let rev_mission_types = rev_mission_types_full - .or(rev_mission_type) - .unify() - .or(rev_mission_subtype) - .unify() - .or(rev_mission_types_list) - .unify(); - - rev_mission_types.boxed() -} - -fn component_types_api< - F: Filter + Send + Sync + Clone + 'static, ->( - rev: &F, -) -> BoxedFilter<(WithStatus,)> { - let rev_component_types_base = rev.clone().and(warp::path("component_types")); - - let rev_single_component_type = rev_component_types_base - .clone() - .and(warp::path::param()) - .and(warp::path::param()) - .and(warp::path::end()) - .map(rev_single_component_api) - .map(map_opt_res) - .boxed(); - - let rev_component_type = rev_component_types_base - .clone() - .and(warp::path::param()) - .and(warp::path::end()) - .map(rev_component_type_api) - .map(map_opt_res) - .boxed(); - - let rev_component_types_list = rev_component_types_base - .clone() - .and(warp::path::end()) - .map(rev_component_types_api) - .map(map_res) - .boxed(); - - rev_single_component_type - .or(rev_component_type) - .unify() - .or(rev_component_types_list) - .unify() - .boxed() -} - -fn object_types_api< - F: Filter + Send + Sync + Clone + 'static, ->( - rev: &F, -) -> BoxedFilter<(WithStatus,)> { - let rev_object_types_base = rev.clone().and(warp::path("object_types")); - - let rev_object_type = rev_object_types_base - .clone() - .and(warp::path::param()) - .and(warp::path::end()) - .map(rev_object_type_api) - .map(map_opt_res) - .boxed(); - - let rev_object_types_list = rev_object_types_base - .clone() - .and(warp::path::end()) - .map(rev_object_types_api) - .map(map_res) - .boxed(); - - rev_object_type.or(rev_object_types_list).unify().boxed() -} - -fn behaviors_api + Send + Sync + Clone + 'static>( - rev: &F, -) -> BoxedFilter<(WithStatus,)> { - let rev_behaviors = rev.clone().and(warp::path("behaviors")); - let rev_behavior_id_base = rev_behaviors.and(warp::path::param::()); - let rev_behavior_id = rev_behavior_id_base - .and(warp::path::end()) - .map(rev_behavior_api) - .map(map_res); - - rev_behavior_id.boxed() -} - pub(super) fn make_api_rev( db: &'static TypedDatabase<'static>, rev: &'static ReverseLookup, @@ -579,11 +58,11 @@ pub(super) fn make_api_rev( let db = tydb_filter(db); let rev = db.and(rev_filter(rev)); - let rev_skills = skill_api(&rev); - let rev_mission_types = mission_types_api(&rev); - let rev_object_types = object_types_api(&rev); - let rev_component_types = component_types_api(&rev); - let rev_behaviors = behaviors_api(&rev); + let rev_skills = skills::skill_api(&rev); + let rev_mission_types = missions::mission_types_api(&rev); + let rev_object_types = object_types::object_types_api(&rev); + let rev_component_types = component_types::component_types_api(&rev); + let rev_behaviors = behaviors::behaviors_api(&rev); let first = rev .clone() diff --git a/src/api/rev/object_types.rs b/src/api/rev/object_types.rs new file mode 100644 index 0000000..645b1b2 --- /dev/null +++ b/src/api/rev/object_types.rs @@ -0,0 +1,73 @@ +use std::{borrow::Borrow, convert::Infallible}; + +use assembly_core::buffer::CastError; +use paradox_typed_db::TypedDatabase; +use serde::Serialize; +use warp::{ + filters::BoxedFilter, + reply::{Json, WithStatus}, + Filter, +}; + +use super::{common::ObjectTypeEmbedded, Ext}; +use crate::api::{ + adapter::TypedTableIterAdapter, + map_opt_res, map_res, + rev::{Api, Rev}, + PercentDecoded, +}; + +#[derive(Serialize)] +struct ObjectIDs<'a, T> { + object_ids: &'a [T], +} + +fn rev_object_type_api( + db: &TypedDatabase, + rev: Rev, + ty: PercentDecoded, +) -> Result, CastError> { + let key: &String = ty.borrow(); + tracing::info!("{}", key); + Ok(rev.inner.object_types.get(key).map(|objects| { + let rep = Api { + data: ObjectIDs { + object_ids: objects.as_ref(), + }, + embedded: ObjectTypeEmbedded { + objects: TypedTableIterAdapter::new(&db.objects, objects), + }, + }; + warp::reply::json(&rep) + })) +} + +fn rev_object_types_api(_db: &TypedDatabase, rev: Rev) -> Result { + let keys: Vec<_> = rev.inner.object_types.keys().collect(); + Ok(warp::reply::json(&keys)) +} + +pub(super) fn object_types_api< + F: Filter + Send + Sync + Clone + 'static, +>( + rev: &F, +) -> BoxedFilter<(WithStatus,)> { + let rev_object_types_base = rev.clone().and(warp::path("object_types")); + + let rev_object_type = rev_object_types_base + .clone() + .and(warp::path::param()) + .and(warp::path::end()) + .map(rev_object_type_api) + .map(map_opt_res) + .boxed(); + + let rev_object_types_list = rev_object_types_base + .clone() + .and(warp::path::end()) + .map(rev_object_types_api) + .map(map_res) + .boxed(); + + rev_object_type.or(rev_object_types_list).unify().boxed() +} diff --git a/src/api/rev/skills.rs b/src/api/rev/skills.rs new file mode 100644 index 0000000..d4f7dca --- /dev/null +++ b/src/api/rev/skills.rs @@ -0,0 +1,48 @@ +use super::{common::MissionTasks, Api, Ext, Rev}; +use crate::api::map_res; +use assembly_core::buffer::CastError; +use paradox_typed_db::TypedDatabase; +use serde::Serialize; +use std::convert::Infallible; +use warp::{ + filters::BoxedFilter, + reply::{Json, WithStatus}, + Filter, +}; + +#[derive(Clone, Serialize)] +pub struct SkillIDEmbedded<'a, 'b> { + #[serde(rename = "MissionTasks")] + mission_tasks: MissionTasks<'a, 'b>, + //MapFilter<'a, MissionTaskUIDLookup>, +} + +fn rev_skill_id_api(db: &'_ TypedDatabase, rev: Rev, skill_id: i32) -> Result { + let h = rev.inner.skill_ids.get(&skill_id).map(|data| { + let mission_tasks = MissionTasks { + index: &rev.inner.mission_task_uids, + keys: &data.mission_tasks[..], + table: &db.mission_tasks, + id_col: db.mission_tasks.col_uid, + }; + Api { + data, + embedded: SkillIDEmbedded { mission_tasks }, + } + }); + Ok(warp::reply::json(&h)) +} + +pub(super) fn skill_api< + F: Filter + Send + Sync + Clone + 'static, +>( + rev: &F, +) -> BoxedFilter<(WithStatus,)> { + let rev_skill_ids = rev.clone().and(warp::path("skill_ids")); + let rev_skill_id_base = rev_skill_ids.and(warp::path::param::()); + let rev_skill_id = rev_skill_id_base + .and(warp::path::end()) + .map(rev_skill_id_api) + .map(map_res); + rev_skill_id.boxed() +}