added log! macro

This commit is contained in:
TotallyNot 2024-01-01 11:59:18 +01:00
parent 86f9333aec
commit b45d04b872
5 changed files with 140 additions and 522 deletions

View file

@ -1,20 +1,18 @@
use std::{collections::HashMap, sync::Mutex};
use std::sync::Mutex;
use bevy_ecs::{prelude::*, system::SystemParam};
use macros::LogMessage;
use crate::{
entity_registry::EntityRegistry,
hierarchy::Children,
player::{
stats::{
AdditiveBonus, BaselineStat, CritRate, DamageBonus, Defence, Dexterity, EffectiveStat,
MultiplicativeBonus, SimpleStatBaseline, SimpleStatBonus, SimpleStatEffective,
SimpleStatMarker, Speed, StatMarker, StatType, Strength, WeaponAccuracy,
},
Player,
player::stats::{
AdditiveBonus, BaselineStat, CritRate, DamageBonus, Defence, Dexterity, EffectiveStat,
MultiplicativeBonus, SimpleStatBaseline, SimpleStatBonus, SimpleStatEffective,
SimpleStatMarker, Speed, StatMarker, StatType, Strength, WeaponAccuracy,
},
weapon::{Weapon, WeaponVerb},
Name, Stages,
weapon::WeaponVerb,
Stages,
};
#[derive(Resource)]
@ -49,6 +47,7 @@ pub enum LogValue<'a> {
Unsigned(u32),
Bool(bool),
String(String),
Entity(Entity),
OptionNone,
Display(&'a (dyn std::fmt::Display + Send + Sync)),
Debug(&'a (dyn std::fmt::Debug + Send + Sync)),
@ -86,6 +85,12 @@ impl From<bool> for LogValue<'static> {
}
}
impl From<Entity> for LogValue<'static> {
fn from(value: Entity) -> Self {
Self::Entity(value)
}
}
impl<'a, T> From<Option<T>> for LogValue<'a>
where
T: Into<LogValue<'a>>,
@ -100,11 +105,7 @@ where
#[cfg(feature = "json")]
impl<'a> LogValue<'a> {
fn to_value(
&self,
player_registry: &HashMap<Entity, PlayerInfo>,
weapon_registry: &HashMap<Entity, WeaponInfo>,
) -> serde_json::Value {
fn to_value(&self, entity_registry: &EntityRegistry) -> serde_json::Value {
match self {
LogValue::OptionNone => serde_json::Value::Null,
LogValue::Float(val) => {
@ -115,27 +116,20 @@ impl<'a> LogValue<'a> {
LogValue::Unsigned(val) => serde_json::Value::Number(serde_json::Number::from(*val)),
LogValue::Debug(boxed) => serde_json::Value::String(format!("{boxed:?}")),
LogValue::Display(boxed) => serde_json::Value::String(format!("{boxed}")),
LogValue::Player(id) => serde_json::json!({
"type": "player",
"name": player_registry.get(id).unwrap().name,
}),
LogValue::Weapon(id) => serde_json::json!({
"type": "weapon",
"name": weapon_registry.get(id).unwrap().name,
}),
LogValue::Player(id) => {
serde_json::to_value(entity_registry.0.get(id).unwrap()).unwrap()
}
LogValue::Weapon(id) => {
serde_json::to_value(entity_registry.0.get(id).unwrap()).unwrap()
}
LogValue::Entity(id) => {
serde_json::to_value(entity_registry.0.get(id).unwrap()).unwrap()
}
}
}
}
pub trait LogMessage: Send + Sync + 'static {
fn torn_style(
&self,
_player_registry: HashMap<Entity, PlayerInfo>,
_weapon_registry: HashMap<Entity, WeaponInfo>,
) -> Option<String> {
None
}
fn tag(&self) -> &'static str;
fn entries(&self) -> Vec<(&'static str, LogValue<'_>)>;
@ -143,9 +137,6 @@ pub trait LogMessage: Send + Sync + 'static {
#[derive(Resource, Default)]
pub struct Log {
pub player_registry: HashMap<Entity, PlayerInfo>,
pub weapon_registry: HashMap<Entity, WeaponInfo>,
pub entries: Vec<Box<dyn LogMessage>>,
pub expanded: bool,
@ -153,7 +144,7 @@ pub struct Log {
impl Log {
#[cfg(feature = "json")]
pub fn to_value(&self) -> serde_json::Value {
pub fn to_value(&self, entity_registry: &EntityRegistry) -> serde_json::Value {
use serde_json::json;
serde_json::json!({
@ -161,7 +152,7 @@ impl Log {
json!({
"type": e.tag(),
"values": serde_json::Value::Object(
e.entries().iter().map(|e| (e.0.to_owned(), e.1.to_value(&self.player_registry, &self.weapon_registry))).collect()
e.entries().iter().map(|e| (e.0.to_owned(), e.1.to_value(entity_registry))).collect()
)
})
).collect::<Vec<_>>()
@ -190,11 +181,8 @@ impl std::fmt::Display for Log {
LogValue::OptionNone => write!(f, "None")?,
LogValue::Display(val) => write!(f, "\"{val}\"")?,
LogValue::Debug(val) => write!(f, "\"{val:?}\"")?,
LogValue::Player(id) => {
write!(f, "\"{}\"", self.player_registry.get(&id).unwrap().name)?
}
LogValue::Weapon(id) => {
write!(f, "\"{}\"", self.weapon_registry.get(&id).unwrap().name)?
LogValue::Player(id) | LogValue::Weapon(id) | LogValue::Entity(id) => {
write!(f, "{:?}", id)?
}
};
@ -418,31 +406,6 @@ fn logging_enabled(logging: Res<Logging>) -> bool {
logging.0
}
fn register_entities(
player_q: Query<(Entity, &Name), With<Player>>,
weapon_q: Query<(Entity, &Name, &WeaponVerb), With<Weapon>>,
mut log: ResMut<Log>,
) {
for (player, name) in player_q.iter() {
log.player_registry.insert(
player,
PlayerInfo {
name: name.0.clone(),
},
);
}
for (weapon, name, verb) in weapon_q.iter() {
log.weapon_registry.insert(
weapon,
WeaponInfo {
name: name.0.clone(),
verb: *verb,
},
);
}
}
fn append_log_messages(mut events: EventReader<LogEvent>, mut log: ResMut<Log>) {
for event in events.read() {
log.entries.push(event.0.lock().unwrap().take().unwrap());
@ -541,13 +504,59 @@ fn log_simple_stat_changes<Stat: SimpleStatMarker>(
}
}
pub struct DynamicLogMessage {
pub label: &'static str,
pub entries: Vec<(&'static str, LogValue<'static>)>,
}
impl LogMessage for DynamicLogMessage {
fn tag(&self) -> &'static str {
self.label
}
fn entries(&self) -> Vec<(&'static str, LogValue<'_>)> {
self.entries.clone()
}
}
#[macro_export]
macro_rules! log {
($logger:expr, $tag:literal, { $($fields:tt)* }) => {
$logger.log(|| $crate::log_message!($tag, { $($fields)* }))
};
}
#[macro_export]
macro_rules! log_message {
($tag:literal, { $($fields:tt)* }) => {
$crate::log::DynamicLogMessage { label: $tag, entries: $crate::log_values!($($fields)*) }
};
}
#[macro_export]
macro_rules! log_values {
(@ { $(,)* $($out:expr),* $(,)* } $(,)*) => {
vec![$($out),*]
};
(@ { $(,)* $($out:expr),* } $label:ident: $val:expr, $($rest:tt)*) => {
$crate::log_values!(@ { $($out),*, (stringify!($label),$val.into()) } $($rest)*)
};
(@ { $(,)* $($out:expr),* } $label:ident: ?$val:expr, $($rest:tt)*) => {
$crate::log_values!(@ { $($out),*, (stringify!($label),$crate::log::LogValue::String(format!("{:?}",$val))) } $($rest)*)
};
(@ { $(,)* $($out:expr),* } $label:ident: %$val:expr, $($rest:tt)*) => {
$crate::log_values!(@ { $($out),*, (stringify!($label),$crate::log::LogValue::String(format!("{}",$val))) } $($rest)*)
};
($($args:tt)* ) => {
$crate::log_values!(@ { } $($args)*,)
};
}
pub(crate) fn configure(stages: &mut Stages) {
stages.world.insert_resource(Log::default());
stages.world.insert_resource(Logging(false));
stages.add_event::<LogEvent>();
stages
.equip
.add_systems(register_entities.run_if(logging_enabled));
stages.post_turn.add_systems(append_log_messages);
stages.turn.add_systems(
@ -563,3 +572,22 @@ pub(crate) fn configure(stages: &mut Stages) {
.run_if(logging_enabled),
);
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn log_keys() {
let message = log_message!("test", { foo: 12u32, bar: ?17 });
assert_eq!(message.label, "test");
let mut entries = message.entries.into_iter();
let next = entries.next();
assert!(matches!(next, Some(("foo", LogValue::Unsigned(12)))));
let next = entries.next();
assert!(matches!(next, Some(("bar", LogValue::String(val))) if val == "17"));
}
}