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