1use crate::block::{BV, Block, Comparator, Eq::Single};
9use crate::context::ScopeContext;
10use crate::everything::Everything;
11use crate::item::Item;
12use crate::report::{ErrorKey, Severity, warn};
13use crate::token::Token;
14use crate::tooltipped::Tooltipped;
15use crate::trigger::{validate_trigger, validate_trigger_key_bv};
16use crate::validator::Validator;
17
18fn validate_desc_map_block(
25 caller: &str,
26 block: &Block,
27 data: &Everything,
28 sc: &mut ScopeContext,
29 f: &impl Fn(&Token, &Everything, &mut ScopeContext),
30) {
31 let mut vd = Validator::new(block, data);
32 let mut seen_desc = false;
33 let mut seen_unconditional_desc = false;
34 vd.unknown_fields(|key, bv| {
35 if key.is("desc") || key.is("first_valid") || key.is("random_valid") {
36 if seen_desc && caller == "triggered_desc" {
37 let msg = "multiple descs in one triggered_desc";
38 let info = "only the last one will be shown";
39 warn(ErrorKey::DuplicateField).msg(msg).info(info).loc(key).push();
40 }
41 if seen_unconditional_desc && caller == "first_valid" {
42 let msg = "multiple unconditional desc in one first_valid";
43 let info = "only the first one will be shown";
44 warn(ErrorKey::DuplicateField).msg(msg).info(info).loc(key).push();
45 }
46 if key.is("desc") {
47 match bv {
48 BV::Value(token) => {
49 if !token.as_str().contains(' ') {
50 f(token, data, sc);
51 }
52 }
53 BV::Block(block) => {
54 validate_desc_map_block(key.as_str(), block, data, sc, f);
55 }
56 }
57 seen_unconditional_desc = true;
59 } else if let Some(block) = bv.expect_block() {
60 validate_desc_map_block(key.as_str(), block, data, sc, f);
61 }
62 seen_desc = true;
63 } else if key.is("triggered_desc") {
64 if let Some(block) = bv.expect_block() {
65 if seen_desc && caller == "triggered_desc" {
66 let msg = "multiple descs in one triggered_desc";
67 let info = "only the last one will be shown";
68 warn(ErrorKey::DuplicateField).msg(msg).info(info).loc(key).push();
69 }
70 validate_desc_map_block(key.as_str(), block, data, sc, f);
71 seen_desc = true;
72 }
73 } else if key.is("trigger") {
74 if let Some(block) = bv.expect_block() {
75 if caller != "triggered_desc" {
76 let msg = "`trigger` is only for `triggered_desc";
77 warn(ErrorKey::Validation).msg(msg).loc(key).push();
78 }
79 validate_trigger(block, data, sc, Tooltipped::No);
80 }
81 } else if key.is("switch") {
82 if let Some(block) = bv.expect_block() {
83 let mut vd = Validator::new(block, data);
85 vd.req_field("trigger");
86 if let Some(target) = vd.field_value("trigger").cloned() {
87 let mut count = 0;
88 vd.set_allow_questionmark_equals(true);
89 vd.unknown_block_fields(|key, block| {
90 count += 1;
91 if !key.is("fallback") {
92 let synthetic_bv = BV::Value(key.clone());
93 validate_trigger_key_bv(
94 &target,
95 Comparator::Equals(Single),
96 &synthetic_bv,
97 data,
98 sc,
99 Tooltipped::No,
100 false,
101 Severity::Warning,
102 );
103 }
104
105 validate_desc_map_block("switch", block, data, sc, f);
106 });
107 if count == 0 {
108 let msg = "switch with no branches";
109 warn(ErrorKey::Logic).msg(msg).loc(key).push();
110 }
111 }
112 }
113 } else {
114 warn(ErrorKey::UnknownField).msg("unexpected key in description").loc(key).push();
115 }
116 });
117}
118
119pub fn validate_desc_map<F: Fn(&Token, &Everything, &mut ScopeContext)>(
123 bv: &BV,
124 data: &Everything,
125 sc: &mut ScopeContext,
126 f: F,
127) {
128 match bv {
129 BV::Value(t) => {
130 if !t.as_str().contains(' ') {
131 f(t, data, sc);
132 }
133 }
134 BV::Block(b) => {
135 validate_desc_map_block("", b, data, sc, &f);
136 }
137 }
138}
139
140pub fn validate_desc(bv: &BV, data: &Everything, sc: &mut ScopeContext) {
143 validate_desc_map(bv, data, sc, |token, data, sc| {
144 data.verify_exists(Item::Localization, token);
145 data.validate_localization_sc(token.as_str(), sc);
146 });
147}