1use std::fmt::{Display, Formatter};
2
3use bitflags::bitflags;
4
5use crate::vic3::tables::modifs::modif_flow_suggest;
6use crate::{
7 Severity, Token, context, modif,
8 report::{ErrorKey, report},
9 scopes::Scopes,
10 vic3::tables::modifs::{MODIF_FLOW_MAP, lookup_modif},
11};
12
13bitflags! {
14 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
18 #[rustfmt::skip]
19 pub struct ModifKinds: u32 {
20 const Character = 1<<0;
21 const Country = 1<<1;
22 const State = 1<<2;
23 const Unit = 1<<3 | 1<<4;
24 const Battle = 1<<5;
25 const Building = 1<<6;
26 const InterestGroup = 1<<7;
27 const Market = 1<<8;
28 const PoliticalMovement = 1<<9;
29 const Tariff = 1<<10;
30 const Tax = 1<<11;
31 const Goods = 1<<12;
32 const MilitaryFormation = 1<<13;
33 const PowerBloc = 1<<14;
34 const UnitCombat = 1<<3;
40 const UnitNonCombat = 1<<4;
41 }
42}
43
44impl ModifKinds {
45 pub fn require_from(
46 self,
47 other: Self,
48 token: &Token,
49 scope: Option<(&Token, Scopes, &context::Reason)>,
50 sev: Severity,
51 ) {
52 let valid_kinds = self
53 .iter()
54 .map(|kind| *MODIF_FLOW_MAP.get(&kind).unwrap_or(&kind))
55 .reduce(ModifKinds::union)
56 .unwrap_or(self);
57 if !valid_kinds.intersects(other) {
58 let from = if let Some((_, scopes, _)) = scope {
59 format!("{scopes} scope")
60 } else {
61 self.to_string()
62 };
63 let msg = format!("`{token}` is a modifier for {other} and will not flow from {from}");
64 let info = if self.is_empty() {
65 format!("there are no valid modifiers for {from}")
66 } else if let Some(suggest) = modif_flow_suggest(token.as_str(), valid_kinds) {
67 format!("a similar modifier exists that would flow `{suggest}`")
68 } else {
69 format!("valid modifiers are for {valid_kinds}")
70 };
71 let mut report = report(ErrorKey::Modifiers, sev)
72 .msg(msg)
73 .info(info)
74 .wiki("https://vic3.paradoxwikis.com/Modifier_types#Modifier_type_flow")
75 .loc(token);
76 if let Some((token, _, reason)) = scope {
77 report = report
78 .loc_msg(token, "from this temporary modifier")
79 .loc_msg(reason.token(), format!("scope was {}", reason.msg()));
80 }
81 report.push();
82 }
83 }
84}
85
86impl modif::ModifKinds for ModifKinds {
87 fn lookup_modif(
88 name: &crate::Token,
89 data: &crate::Everything,
90 warn: Option<crate::Severity>,
91 ) -> Option<Self> {
92 lookup_modif(name, data, warn)
93 }
94
95 fn require(self, other: Self, token: &Token) {
96 self.require_from(other, token, None, Severity::Error);
97 }
98}
99
100impl Display for ModifKinds {
101 fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
102 let mut vec = Vec::new();
103 if self.contains(ModifKinds::Battle) {
104 vec.push("battle");
105 }
106 if self.contains(ModifKinds::Building) {
107 vec.push("building");
108 }
109 if self.contains(ModifKinds::Character) {
110 vec.push("character");
111 }
112 if self.contains(ModifKinds::Country) {
113 vec.push("country");
114 }
115 if self.contains(ModifKinds::InterestGroup) {
116 vec.push("interest group");
117 }
118 if self.contains(ModifKinds::Market) {
119 vec.push("market");
120 }
121 if self.contains(ModifKinds::PoliticalMovement) {
122 vec.push("political movement");
123 }
124 if self.contains(ModifKinds::State) {
125 vec.push("state");
126 }
127 if self.contains(ModifKinds::Tariff) {
128 vec.push("tariff");
129 }
130 if self.contains(ModifKinds::Tax) {
131 vec.push("tax");
132 }
133 if self.contains(ModifKinds::Unit) {
134 vec.push("unit");
135 } else if self.contains(ModifKinds::UnitCombat) {
136 vec.push("unit (combat)");
137 } else if self.contains(ModifKinds::UnitNonCombat) {
138 vec.push("unit (non-combat)");
139 }
140 if self.contains(ModifKinds::Goods) {
141 vec.push("goods");
142 }
143 if self.contains(ModifKinds::MilitaryFormation) {
144 vec.push("military formation");
145 }
146 if self.contains(ModifKinds::PowerBloc) {
147 vec.push("power bloc");
148 }
149 write!(f, "{}", vec.join(", "))
150 }
151}