1use crate::Info;
63use crate::Tristate;
64use crate::Variant;
65use crate::VariantDataType;
66use crate::{
67 ast_extra::AstExtra,
68 dependencies::{ItemDependency, ResolvedExpr, SelectAndImplyDependencyResolver},
69 ItemReference,
70};
71use kconfig_parser::{
72 ast::{
73 parser::{ConditionContainer, Container, ElementType},
74 ConfigType, Dependency, Element,
75 },
76 Ast, Symbol,
77};
78use std::{
79 collections::{HashMap, HashSet},
80 hash::Hash,
81};
82
83#[derive(Debug, Clone)]
84pub struct Item {
85 name: String,
87
88 enabled: bool,
90
91 help: Vec<String>,
94
95 parent: Option<String>,
97
98 conditions: HashSet<Dependency>,
100
101 maybe_elt: Option<Element>,
103
104 macro_symbols: HashSet<Symbol>,
108
109 config_items: HashMap<ConfigType, ConfigItem>,
113
114 dependencies: HashSet<ItemDependency>,
117
118 selects_me: HashMap<String, Option<ResolvedExpr>>,
120
121 imply_me: HashMap<String, Option<ResolvedExpr>>,
123
124 selected: HashMap<String, Option<ResolvedExpr>>,
126
127 implied: HashMap<String, Option<ResolvedExpr>>,
129}
130
131impl PartialEq for Item {
132 fn eq(&self, other: &Self) -> bool {
133 self.name == other.name
134 && self.enabled == other.enabled
135 && self.help == other.help
136 && self.parent == other.parent
137 && self.conditions == other.conditions
138 && self.maybe_elt == other.maybe_elt
139 }
140}
141impl Eq for Item {}
142
143impl Item {
144 fn new(name: &str, macro_symbols: &HashSet<Symbol>) -> Self {
145 let name = name.to_string();
146 Self {
147 name,
148 enabled: true,
149 help: vec![],
150 conditions: HashSet::new(),
151 parent: None,
152 maybe_elt: None,
153 macro_symbols: macro_symbols.clone(),
154 config_items: HashMap::new(),
155 dependencies: HashSet::new(),
156 selects_me: HashMap::new(),
157 imply_me: HashMap::new(),
158 selected: HashMap::new(),
159 implied: HashMap::new(),
160 }
161 }
162
163 pub(crate) fn new_from_ast(
164 main_menu_name: &str,
165 macro_symbols: &HashSet<Symbol>,
166 ast: &Ast,
167 forward_dependency_resolver: &mut SelectAndImplyDependencyResolver,
168 ) -> HashMap<ItemReference, Self> {
169 let mut item = Item::new(main_menu_name, macro_symbols);
170 let mut items = HashMap::new();
171 let empty_conditions = HashSet::new();
172
173 for name in ast.element_names() {
174 if let Ok(elt) = ast.element_from_name(&name) {
175 let child_items = item.create_child(
176 name,
177 ast,
178 elt,
179 empty_conditions.clone(),
180 forward_dependency_resolver,
181 );
182 items.extend(child_items.into_iter());
183 }
184 }
185
186 for condition in ast.conditional_blocks() {
187 for (_, elt) in condition.elements() {
188 let mut additional_conditions = HashSet::new();
189 additional_conditions.insert(condition.dependency());
190 items.extend(item.create_child(
191 elt.name(),
192 &ast,
193 elt,
194 additional_conditions,
195 forward_dependency_resolver,
196 ));
197 }
198 }
199
200 items.insert(ItemReference::Menu(main_menu_name.to_string()), item);
201 items
202 }
203
204 pub(crate) fn init_from_elt(
205 &mut self,
206 ast: &Ast,
207 elt: &Element,
208 conditions: HashSet<Dependency>,
209 forward_dependency_resolver: &mut SelectAndImplyDependencyResolver,
210 ) -> HashMap<ItemReference, Self> {
211 let mut items = HashMap::new();
212
213 for (name, elt) in elt.sub_elements() {
214 let child_items = self.create_child(
215 name,
216 ast,
217 elt,
218 conditions.clone(),
219 forward_dependency_resolver,
220 );
221 items.extend(child_items.into_iter());
222 }
223
224 for condition in elt.conditional_blocks() {
225 for (_, elt) in condition.elements() {
226 let mut additional_conditions = conditions.clone();
227 additional_conditions.insert(condition.dependency());
228 items.extend(self.create_child(
229 elt.name(),
230 &ast,
231 elt,
232 additional_conditions,
233 forward_dependency_resolver,
234 ));
235 }
236 }
237
238 items
239 }
240
241 fn create_child(
242 &mut self,
243 name: String,
244 ast: &Ast,
245 elt: Element,
246 conditions: HashSet<Dependency>,
247 forward_dependency_resolver: &mut SelectAndImplyDependencyResolver,
248 ) -> HashMap<ItemReference, Self> {
249 let mut item = Self::new(&name, &self.macro_symbols);
250 item.conditions = conditions.clone();
251 for paragraph in elt.help() {
252 item.help.push(paragraph);
253 }
254
255 item.parent = Some(self.name.clone());
256 let mut items = item.init_from_elt(ast, &elt, conditions, forward_dependency_resolver);
257
258 let itemref = match &elt.elt_type() {
259 ElementType::Config => ItemReference::Config(name.clone()),
260 ElementType::MenuConfig => ItemReference::MenuConfig(name.clone()),
261 ElementType::Menu => ItemReference::Menu(name.clone()),
262 };
263
264 item.maybe_elt = Some(elt.clone());
265
266 if let Some(types) = elt.types() {
267 for (config_type, _) in types {
268 if let Some(config) = ConfigItem::new(config_type, &elt) {
269 item.config_items.insert(config_type.clone(), config);
270 }
271 }
272 }
273
274 if let Some(reverse_dependencies) = elt.reverse_dependencies() {
275 for (selected_name, maybe_condition) in reverse_dependencies {
276 forward_dependency_resolver.put_select(&itemref, selected_name, maybe_condition)
277 }
278 }
279
280 if let Some(weak_dependencies) = elt.weak_dependencies() {
281 for (implied_name, maybe_condition) in weak_dependencies {
282 forward_dependency_resolver.put_imply(&itemref, implied_name, maybe_condition)
283 }
284 }
285
286 items.insert(itemref, item);
287 items
288 }
289
290 pub(crate) fn set_dependencies(&mut self, dependencies: HashSet<ItemDependency>) {
291 self.dependencies = dependencies;
292 }
293
294 pub(crate) fn set_selects_me(&mut self, dependencies: HashMap<String, Option<ResolvedExpr>>) {
295 self.selects_me = dependencies;
296 }
297
298 pub(crate) fn set_selected(&mut self, dependencies: HashMap<String, Option<ResolvedExpr>>) {
299 self.selected = dependencies;
300 }
301
302 pub(crate) fn set_imply_me(&mut self, dependencies: HashMap<String, Option<ResolvedExpr>>) {
303 self.imply_me = dependencies;
304 }
305
306 pub(crate) fn set_implied(&mut self, dependencies: HashMap<String, Option<ResolvedExpr>>) {
307 self.implied = dependencies;
308 }
309
310 pub fn name<'a>(&'a self) -> &'a str {
313 &self.name
314 }
315
316 pub fn itemref(&self) -> ItemReference {
319 match &self.maybe_elt {
320 Some(elt) => match elt.elt_type() {
321 ElementType::Config => ItemReference::Config(self.name.clone()),
322 ElementType::MenuConfig => ItemReference::MenuConfig(self.name.clone()),
323 ElementType::Menu => ItemReference::Menu(self.name.clone()),
324 },
325 None => ItemReference::Menu(self.name.clone()),
327 }
328 }
329
330 pub fn config_items<'a>(&'a self) -> &'a HashMap<ConfigType, ConfigItem> {
331 &self.config_items
332 }
333
334 pub fn config_items_mut<'a>(&'a mut self) -> &'a mut HashMap<ConfigType, ConfigItem> {
335 &mut self.config_items
336 }
337
338 pub fn conditions(&self) -> HashSet<Dependency> {
339 self.conditions.clone()
340 }
341
342 pub fn item_dependencies(&self) -> HashSet<ItemDependency> {
344 self.dependencies.clone()
345 }
346
347 pub fn selected(&self) -> HashMap<String, Option<ResolvedExpr>> {
349 self.selected.clone()
350 }
351
352 pub fn implied<'a>(&self) -> HashMap<String, Option<ResolvedExpr>> {
354 self.implied.clone()
355 }
356
357 pub fn selects_me(&self) -> HashMap<String, Option<ResolvedExpr>> {
359 self.selects_me.clone()
360 }
361
362 pub fn imply_me(&self) -> HashMap<String, Option<ResolvedExpr>> {
364 self.imply_me.clone()
365 }
366
367 pub fn raw_elt<'a>(&'a self) -> &'a Option<Element> {
368 &self.maybe_elt
369 }
370
371 pub fn parent<'a>(&'a self) -> &'a Option<String> {
372 &self.parent
373 }
374
375 pub fn info(&self) -> Info {
376 Info::new(self.help.clone(), self.itemref())
377 }
378
379 pub(crate) fn collect_all_dependencies(&self) -> HashSet<MaybeIncompleteItemReference> {
380 let mut itemrefs: HashSet<MaybeIncompleteItemReference> = HashSet::new();
381
382 for item_dependency in self.item_dependencies() {
383 for itemref in item_dependency.expr().itemrefs() {
384 itemrefs.insert(MaybeIncompleteItemReference::ItemReference(itemref.clone()));
385 }
386 }
387
388 for (name, expr) in self.imply_me() {
389 itemrefs.insert(MaybeIncompleteItemReference::String(name));
390 if let Some(expr) = expr {
391 for itemref in expr.itemrefs() {
392 itemrefs.insert(MaybeIncompleteItemReference::ItemReference(itemref.clone()));
393 }
394 }
395 }
396
397 for (name, expr) in self.selects_me() {
398 itemrefs.insert(MaybeIncompleteItemReference::String(name));
399 if let Some(expr) = expr {
400 for itemref in expr.itemrefs() {
401 itemrefs.insert(MaybeIncompleteItemReference::ItemReference(itemref.clone()));
402 }
403 }
404 }
405
406 for (_, config_item) in self.config_items() {
407 for itemref in config_item.collect_all_dependencies() {
408 itemrefs.insert(MaybeIncompleteItemReference::ItemReference(itemref.clone()));
409 }
410 }
411
412 itemrefs
413 }
414}
415
416#[derive(Clone, Debug, PartialEq, Eq, Hash)]
420pub(crate) enum MaybeIncompleteItemReference {
421 ItemReference(ItemReference),
422 String(String),
423}
424
425#[derive(Clone, Debug, PartialEq, Eq)]
428pub struct ConfigItem {
429 prompt: Option<String>,
431
432 is_menu: bool,
436
437 value: Option<Variant>,
440
441 config_value: Option<Variant>,
444
445 config_type: ConfigType,
447
448 defaults: HashMap<ResolvedExpr, Option<ResolvedExpr>>,
450
451 maybe_expr: Option<ResolvedExpr>,
454
455 choices: Option<(bool, Tristate)>,
461}
462
463impl ConfigItem {
464 pub(crate) fn new(config_type: &ConfigType, elt: &Element) -> Option<Self> {
465 let maybe_is_menu = match elt.elt_type() {
466 ElementType::Config => Some(false),
467 ElementType::MenuConfig => Some(true),
468 ElementType::Menu => None,
469 };
470
471 maybe_is_menu.map(|is_menu| Self {
472 maybe_expr: None,
473 is_menu,
474 config_value: None,
475 value: None,
476 choices: match config_type {
477 ConfigType::Bool | ConfigType::Tristate => Some((true, Tristate::FALSE)),
478 _ => None,
479 },
480 config_type: config_type.clone(),
481 prompt: elt.prompt(),
482 defaults: HashMap::new(),
483 })
484 }
485
486 pub(crate) fn value(&self) -> Option<Variant> {
489 self.value.clone()
490 }
491
492 pub(crate) fn set_value(&mut self, value: Variant) {
494 self.value = Some(value.transform(VariantDataType::from(&self.config_type)))
495 }
496
497 pub(crate) fn set_config_value(&mut self, value: Variant) {
499 self.config_value = Some(value.transform(VariantDataType::from(&self.config_type)))
500 }
501
502 pub(crate) fn config_value(&self) -> Option<Variant> {
504 self.config_value.clone()
505 }
506
507 pub fn choice_mode(&self) -> Option<bool> {
510 match self.choices {
511 Some((selected, _)) => Some(selected),
512 None => None,
513 }
514 }
515
516 pub fn choice_low(&self) -> Variant {
518 match self.choices {
519 Some((_, bound)) => Variant::from(bound),
520 None => Variant::from(false),
521 }
522 }
523
524 pub(crate) fn set_choice_mode(&mut self, selected: bool, low_bound: Tristate) {
526 self.choices = Some((selected, low_bound));
527 }
528
529 pub(crate) fn reset_value(&mut self) {
531 self.value = None
532 }
533
534 pub(crate) fn reset_config_value(&mut self) {
536 self.config_value = None
537 }
538
539 pub(crate) fn reset_all(&mut self) {
542 self.reset_value();
543 self.reset_config_value();
544 if let Some(_) = self.choices {
545 self.choices = Some((true, Tristate::FALSE));
546 }
547 }
548
549 pub fn config_type(&self) -> ConfigType {
551 self.config_type.clone()
552 }
553
554 pub(crate) fn set_dependencies(&mut self, resolved_expr: ResolvedExpr) {
556 self.maybe_expr = Some(resolved_expr);
557 }
558
559 pub(crate) fn maybe_dependencies(&self) -> Option<ResolvedExpr> {
560 match &self.maybe_expr {
561 Some(expr) => Some(expr.clone()),
562 None => None,
563 }
564 }
565
566 pub(crate) fn add_default(
567 &mut self,
568 default_expr: ResolvedExpr,
569 dependency: Option<ResolvedExpr>,
570 ) {
571 self.defaults.insert(default_expr, dependency);
572 }
573
574 pub(crate) fn defaults(&self) -> HashMap<ResolvedExpr, Option<ResolvedExpr>> {
575 let mut map = HashMap::new();
576 for (k, v) in &self.defaults {
577 let cloned = match v {
578 Some(v) => Some(v.clone()),
579 None => None,
580 };
581 map.insert(k.clone(), cloned);
582 }
583 map
584 }
585
586 pub fn prompt<'a>(&'a self) -> &'a Option<String> {
590 &self.prompt
591 }
592
593 pub fn is_menu(&self) -> bool {
595 self.is_menu
596 }
597
598 pub fn collect_all_dependencies(&self) -> HashSet<ItemReference> {
600 let mut itemrefs: HashSet<ItemReference> = HashSet::new();
601 for (k, v) in &self.defaults {
602 for itemref in k.itemrefs() {
603 itemrefs.insert(itemref.clone());
604 }
605 if let Some(expr) = v {
606 for itemref in expr.itemrefs() {
607 itemrefs.insert(itemref.clone());
608 }
609 }
610 }
611
612 if let Some(expr) = &self.maybe_expr {
613 for itemref in expr.itemrefs() {
614 itemrefs.insert(itemref.clone());
615 }
616 }
617
618 itemrefs
619 }
620}
621
622impl Hash for Item {
623 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
624 self.name.hash(state);
625 self.enabled.hash(state);
626 self.help.hash(state);
627 for parent in self.conditions.iter() {
628 parent.hash(state)
629 }
630 }
631}