kconfig_represent/
dependencies.rs

1/*
2 Cargo KConfig - KConfig parser
3 Copyright (C) 2022  Sjoerd van Leent
4
5--------------------------------------------------------------------------------
6
7Copyright Notice: Apache
8
9Licensed under the Apache License, Version 2.0 (the "License"); you may not use
10this file except in compliance with the License. You may obtain a copy of the
11License at
12
13   https://www.apache.org/licenses/LICENSE-2.0
14
15Unless required by applicable law or agreed to in writing, software distributed
16under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
17CONDITIONS OF ANY KIND, either express or implied. See the License for the
18specific language governing permissions and limitations under the License.
19
20--------------------------------------------------------------------------------
21
22Copyright Notice: GPLv2
23
24This program is free software: you can redistribute it and/or modify
25it under the terms of the GNU General Public License as published by
26the Free Software Foundation, either version 2 of the License, or
27(at your option) any later version.
28
29This program is distributed in the hope that it will be useful,
30but WITHOUT ANY WARRANTY; without even the implied warranty of
31MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
32GNU General Public License for more details.
33
34You should have received a copy of the GNU General Public License
35along with this program.  If not, see <https://www.gnu.org/licenses/>.
36
37--------------------------------------------------------------------------------
38
39Copyright Notice: MIT
40
41Permission is hereby granted, free of charge, to any person obtaining a copy of
42this software and associated documentation files (the “Software”), to deal in
43the Software without restriction, including without limitation the rights to
44use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
45the Software, and to permit persons to whom the Software is furnished to do so,
46subject to the following conditions:
47
48The above copyright notice and this permission notice shall be included in all
49copies or substantial portions of the Software.
50
51THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
52IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
53FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
54COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
55IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
56CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
57*/
58
59//! This module contains the resolve logic, which uses the configuration registry,
60//! it's items and it's evaluator to resolve dependencies.
61
62use std::collections::{BTreeSet, HashMap, HashSet};
63use std::fmt::Display;
64
65use kconfig_parser::{
66    ast::{structs::Expr, Dependency, Element},
67    Symbol,
68};
69
70use crate::{Item, ItemReference};
71
72#[derive(Clone, Debug, Eq, PartialEq, Hash)]
73pub struct ForwardDependency {
74    forward_name: String,
75    maybe_condition: Option<Dependency>,
76}
77
78#[derive(Clone, Debug, Eq, PartialEq, Hash)]
79pub struct BackwardDependency {
80    backward_name: String,
81    maybe_condition: Option<Dependency>,
82}
83
84#[derive(Debug)]
85pub(crate) struct SelectAndImplyDependencyResolver {
86    forward_select: HashMap<String, HashSet<ForwardDependency>>,
87    backward_select: HashMap<String, HashSet<BackwardDependency>>,
88    forward_imply: HashMap<String, HashSet<ForwardDependency>>,
89    backward_imply: HashMap<String, HashSet<BackwardDependency>>,
90}
91
92impl SelectAndImplyDependencyResolver {
93    pub(crate) fn new() -> Self {
94        Self {
95            forward_select: HashMap::new(),
96            backward_select: HashMap::new(),
97            forward_imply: HashMap::new(),
98            backward_imply: HashMap::new(),
99        }
100    }
101
102    pub(crate) fn put_select(
103        &mut self,
104        itemref: &ItemReference,
105        selected_name: &str,
106        maybe_condition: &Option<Dependency>,
107    ) {
108        let forward_dependency = ForwardDependency {
109            forward_name: selected_name.to_string(),
110            maybe_condition: maybe_condition.clone(),
111        };
112        match self.forward_select.get_mut(&itemref.to_string()) {
113            Some(value) => {
114                value.insert(forward_dependency);
115            }
116            None => {
117                let mut set = HashSet::new();
118                set.insert(forward_dependency);
119                self.forward_select.insert(itemref.to_string(), set);
120            }
121        }
122
123        let backward_dependency = BackwardDependency {
124            backward_name: itemref.to_string(),
125            maybe_condition: maybe_condition.clone(),
126        };
127        match self.backward_select.get_mut(&selected_name.to_string()) {
128            Some(value) => {
129                value.insert(backward_dependency);
130            }
131            None => {
132                let mut set = HashSet::new();
133                set.insert(backward_dependency);
134                self.backward_select.insert(selected_name.to_string(), set);
135            }
136        }
137    }
138
139    pub(crate) fn put_imply(
140        &mut self,
141        itemref: &ItemReference,
142        implied_name: &str,
143        maybe_condition: &Option<Dependency>,
144    ) {
145        let forward_dependency = ForwardDependency {
146            forward_name: implied_name.to_string(),
147            maybe_condition: maybe_condition.clone(),
148        };
149        match self.forward_imply.get_mut(&itemref.to_string()) {
150            Some(value) => {
151                value.insert(forward_dependency);
152            }
153            None => {
154                let mut set = HashSet::new();
155                set.insert(forward_dependency);
156                self.forward_imply.insert(itemref.to_string(), set);
157            }
158        }
159        let backward_dependency = BackwardDependency {
160            backward_name: itemref.to_string(),
161            maybe_condition: maybe_condition.clone(),
162        };
163        match self.backward_imply.get_mut(&implied_name.to_string()) {
164            Some(value) => {
165                value.insert(backward_dependency);
166            }
167            None => {
168                let mut set = HashSet::new();
169                set.insert(backward_dependency);
170                self.backward_imply.insert(implied_name.to_string(), set);
171            }
172        }
173    }
174}
175
176/// Dynamically resolves the states of menu and configuration items
177/// discovered in the configuration registry by evaluating the AST each
178/// cycle when the state of a single (configuration) item has been changed
179/// through the user interface.
180#[derive(Debug)]
181pub(crate) struct DependencyResolver {
182    select_and_imply_resolver: SelectAndImplyDependencyResolver,
183    macro_symbols: HashSet<Symbol>,
184}
185
186impl DependencyResolver {
187    /// Creates a new resolver instance for the duration of the existence of
188    /// the given items. A resolver is constricted to the scope of the caller
189    /// by it's given lifetime.
190    pub(crate) fn new(
191        select_and_imply_resolver: SelectAndImplyDependencyResolver,
192        macro_symbols: HashSet<Symbol>,
193    ) -> Self {
194        Self {
195            select_and_imply_resolver,
196            macro_symbols,
197        }
198    }
199
200    pub(crate) fn resolve(&mut self, items: &mut HashMap<ItemReference, Item>) {
201        // Filter out all the known itemreferences, and store them into a HashMap, where individual
202        // item references can be looked up, by name
203        let mut lut: HashMap<String, ItemReference> = items
204            .iter()
205            .map(|(k, _)| (k.to_string(), k.clone()))
206            .collect();
207
208        let itemrefs: HashSet<ItemReference> = items.keys().cloned().collect();
209
210        for (_, item) in items.iter_mut() {
211            if let Some(elt) = item.raw_elt() {
212                let item_types = elt.types().cloned();
213                let maybe_defaults = elt.defaults().cloned();
214                let itemref = &item.itemref();
215                item.set_dependencies(self.resolve_dependencies(elt, &item, &itemrefs, &mut lut));
216                item.set_selects_me(self.resolve_select(&itemref, &itemrefs, &mut lut));
217                item.set_imply_me(self.resolve_imply(&itemref, &itemrefs, &mut lut));
218                item.set_selected(self.resolve_selected(&itemref, &itemrefs, &mut lut));
219                item.set_implied(self.resolve_implied(&itemref, &itemrefs, &mut lut));
220                self.resolve_config_dependencies(item_types, item, &itemrefs, &mut lut);
221                self.resolve_default_expressions(maybe_defaults, &itemrefs, &mut lut, item);
222            }
223        }
224    }
225
226    fn resolve_dependencies(
227        &self,
228        elt: &Element,
229        item: &Item,
230        itemrefs: &HashSet<ItemReference>,
231        lut: &mut HashMap<String, ItemReference>,
232    ) -> HashSet<ItemDependency> {
233        let mut set: HashSet<ItemDependency> = HashSet::new();
234
235        if let Some(parent) = item.parent() {
236            set.insert(ItemDependency::Heavy(ResolvedExpr::new(
237                &Expr::Sym(parent.clone()),
238                itemrefs,
239                &self.macro_symbols,
240                lut,
241            )));
242        }
243
244        let dependencies: HashSet<ItemDependency> = elt
245            .dependencies()
246            .iter()
247            .map(|d| d.expr())
248            .map(|e| ResolvedExpr::new(&e, itemrefs, &self.macro_symbols, lut))
249            .map(|re| ItemDependency::Normal(re))
250            .collect();
251
252        let conditions: HashSet<ItemDependency> = item
253            .conditions()
254            .iter()
255            .map(|d| d.expr())
256            .map(|e| ResolvedExpr::new(&e, itemrefs, &self.macro_symbols, lut))
257            .map(|re| ItemDependency::Trivial(re))
258            .collect();
259
260        set.extend(dependencies);
261        set.extend(conditions);
262
263        set
264    }
265
266    fn resolve_select(
267        &self,
268        itemref: &ItemReference,
269        itemrefs: &HashSet<ItemReference>,
270        lut: &mut HashMap<String, ItemReference>,
271    ) -> HashMap<String, Option<ResolvedExpr>> {
272        match self
273            .select_and_imply_resolver
274            .backward_select
275            .get(&itemref.to_string())
276        {
277            Some(s) => {
278                let mut map = HashMap::new();
279                for dep in s {
280                    let name = dep.backward_name.clone();
281                    match &dep.maybe_condition {
282                        Some(dep) => map.insert(
283                            name,
284                            Some(ResolvedExpr::new(
285                                &dep.expr(),
286                                itemrefs,
287                                &self.macro_symbols,
288                                lut,
289                            )),
290                        ),
291                        None => map.insert(name, None),
292                    };
293                }
294                map
295            }
296            None => HashMap::new(),
297        }
298    }
299
300    fn resolve_selected(
301        &self,
302        itemref: &ItemReference,
303        itemrefs: &HashSet<ItemReference>,
304        lut: &mut HashMap<String, ItemReference>,
305    ) -> HashMap<String, Option<ResolvedExpr>> {
306        match self
307            .select_and_imply_resolver
308            .forward_select
309            .get(&itemref.to_string())
310        {
311            Some(s) => {
312                let mut map = HashMap::new();
313                for dep in s {
314                    let name = dep.forward_name.clone();
315                    match &dep.maybe_condition {
316                        Some(dep) => map.insert(
317                            name,
318                            Some(ResolvedExpr::new(
319                                &dep.expr(),
320                                itemrefs,
321                                &self.macro_symbols,
322                                lut,
323                            )),
324                        ),
325                        None => map.insert(name, None),
326                    };
327                }
328                map
329            }
330            None => HashMap::new(),
331        }
332    }
333
334    fn resolve_imply(
335        &self,
336        itemref: &ItemReference,
337        itemrefs: &HashSet<ItemReference>,
338        lut: &mut HashMap<String, ItemReference>,
339    ) -> HashMap<String, Option<ResolvedExpr>> {
340        match self
341            .select_and_imply_resolver
342            .backward_imply
343            .get(&itemref.to_string())
344        {
345            Some(s) => {
346                let mut map = HashMap::new();
347                for dep in s {
348                    let name = dep.backward_name.clone();
349                    match &dep.maybe_condition {
350                        Some(dep) => map.insert(
351                            name,
352                            Some(ResolvedExpr::new(
353                                &dep.expr(),
354                                itemrefs,
355                                &self.macro_symbols,
356                                lut,
357                            )),
358                        ),
359                        None => map.insert(name, None),
360                    };
361                }
362                map
363            }
364            None => HashMap::new(),
365        }
366    }
367
368    fn resolve_implied(
369        &self,
370        itemref: &ItemReference,
371        itemrefs: &HashSet<ItemReference>,
372        lut: &mut HashMap<String, ItemReference>,
373    ) -> HashMap<String, Option<ResolvedExpr>> {
374        match self
375            .select_and_imply_resolver
376            .forward_imply
377            .get(&itemref.to_string())
378        {
379            Some(s) => {
380                let mut map = HashMap::new();
381                for dep in s {
382                    let name = dep.forward_name.clone();
383                    match &dep.maybe_condition {
384                        Some(dep) => map.insert(
385                            name,
386                            Some(ResolvedExpr::new(
387                                &dep.expr(),
388                                itemrefs,
389                                &self.macro_symbols,
390                                lut,
391                            )),
392                        ),
393                        None => map.insert(name, None),
394                    };
395                }
396                map
397            }
398            None => HashMap::new(),
399        }
400    }
401
402    fn resolve_config_dependencies(
403        &mut self,
404        item_types: Option<HashMap<kconfig_parser::ast::ConfigType, Option<Dependency>>>,
405        item: &mut Item,
406        itemrefs: &HashSet<ItemReference>,
407        lut: &mut HashMap<String, ItemReference>,
408    ) {
409        if let Some(types) = item_types {
410            for (config_type, dependency) in types {
411                if let Some(dependency) = dependency {
412                    if let Some(config_item) = item.config_items_mut().get_mut(&config_type) {
413                        let expr = ResolvedExpr::new(
414                            &dependency.expr(),
415                            itemrefs,
416                            &self.macro_symbols,
417                            lut,
418                        );
419                        config_item.set_dependencies(expr)
420                    }
421                }
422            }
423        }
424    }
425
426    fn resolve_default_expressions(
427        &mut self,
428        maybe_defaults: Option<HashMap<Expr, Option<Dependency>>>,
429        itemrefs: &HashSet<ItemReference>,
430        lut: &mut HashMap<String, ItemReference>,
431        item: &mut Item,
432    ) {
433        if let Some(defaults) = maybe_defaults {
434            for (expr, maybe_dependency) in defaults {
435                let default_expr = ResolvedExpr::new(&expr, itemrefs, &self.macro_symbols, lut);
436
437                let maybe_expr = match maybe_dependency {
438                    Some(dependency) => Some(ResolvedExpr::new(
439                        &dependency.expr(),
440                        itemrefs,
441                        &self.macro_symbols,
442                        lut,
443                    )),
444                    None => None,
445                };
446
447                for (_, config_item) in item.config_items_mut().iter_mut() {
448                    config_item.add_default(
449                        default_expr.clone(),
450                        match &maybe_expr {
451                            Some(v) => Some(v.clone()),
452                            None => None,
453                        },
454                    );
455                }
456            }
457        }
458    }
459}
460
461/// Value indicating a resolved expression, which can be evaluated directly.
462/// It knows which item references are dependencies, and is therefore able to
463/// discover the resolution path of an evaluation.
464#[derive(Hash, Clone, PartialEq, Eq, Debug)]
465pub struct ResolvedExpr {
466    item_expr: ItemExpr,
467    itemrefs: BTreeSet<ItemReference>,
468}
469
470impl ResolvedExpr {
471    pub fn new(
472        expr: &Expr,
473        itemrefs: &HashSet<ItemReference>,
474        macro_symbols: &HashSet<Symbol>,
475        lut: &mut HashMap<String, ItemReference>,
476    ) -> ResolvedExpr {
477        let expanded_symbols: HashMap<String, String> = macro_symbols
478            .iter()
479            .map(|s| (s.name(), s.value()))
480            .collect();
481
482        let item_expr = ItemExpr::resolve(expr, itemrefs, &expanded_symbols, lut);
483        let itemrefs = item_expr.itemrefs();
484
485        Self {
486            item_expr,
487            itemrefs: itemrefs,
488        }
489    }
490
491    /// Returns the actual expressions underpinning the resolved expression, which
492    /// acts as a container for both the resolved item expressions, as well as a
493    /// direct reference to all item references within the underlying item
494    /// expressions.
495    pub fn item_expr<'a>(&'a self) -> &'a ItemExpr {
496        &self.item_expr
497    }
498
499    /// Returns the item references used by the given item expression
500    pub fn itemrefs<'a>(&'a self) -> &'a BTreeSet<ItemReference> {
501        &self.itemrefs
502    }
503}
504
505/// Wrapper around an expression, resolved into the macro values, and
506/// item references.
507#[derive(PartialEq, Eq, Debug, Clone, PartialOrd, Ord, Hash)]
508pub enum ItemExpr {
509    /// A value, typically the result of a resolved symbol to a macro
510    Value(String),
511
512    /// An item reference, typically the result of a resolved symbol to
513    /// an item reference.
514    ItemReference(ItemReference),
515
516    /// An equals expression
517    Eq(BTreeSet<ItemExpr>),
518
519    /// A Not equals expression
520    Ne(BTreeSet<ItemExpr>),
521
522    /// A Lesser than expression
523    Lt(BTreeSet<ItemExpr>),
524
525    /// A Greater than expression
526    Gt(BTreeSet<ItemExpr>),
527
528    /// A Lesser than or equal expression
529    Lte(BTreeSet<ItemExpr>),
530
531    /// A Greater than or equal expression
532    Gte(BTreeSet<ItemExpr>),
533
534    /// An and expression
535    And(BTreeSet<ItemExpr>),
536
537    /// An or expression
538    Or(BTreeSet<ItemExpr>),
539
540    /// A subexpression, typically just parentheses surrounding the expression
541    Sub(Box<ItemExpr>),
542
543    /// A negation expression
544    Not(Box<ItemExpr>),
545
546    /// Could not resolve value
547    Unresolved(String),
548}
549impl ItemExpr {
550    fn resolve(
551        expr: &Expr,
552        refs: &HashSet<ItemReference>,
553        syms: &HashMap<String, String>,
554        cache: &mut HashMap<String, ItemReference>,
555    ) -> ItemExpr {
556        match expr {
557            Expr::Sym(s) => Self::cached_sym_to_item_expr(syms, s, cache, refs),
558            Expr::Eq(exprs) => ItemExpr::Eq(Self::expr_to_item_expr(exprs, refs, syms, cache)),
559            Expr::Ne(exprs) => ItemExpr::Ne(Self::expr_to_item_expr(exprs, refs, syms, cache)),
560            Expr::Lt(exprs) => ItemExpr::Lt(Self::expr_to_item_expr(exprs, refs, syms, cache)),
561            Expr::Gt(exprs) => ItemExpr::Gt(Self::expr_to_item_expr(exprs, refs, syms, cache)),
562            Expr::Lte(exprs) => ItemExpr::Lte(Self::expr_to_item_expr(exprs, refs, syms, cache)),
563            Expr::Gte(exprs) => ItemExpr::Gte(Self::expr_to_item_expr(exprs, refs, syms, cache)),
564            Expr::And(exprs) => ItemExpr::And(Self::expr_to_item_expr(exprs, refs, syms, cache)),
565            Expr::Or(exprs) => ItemExpr::Or(Self::expr_to_item_expr(exprs, refs, syms, cache)),
566            Expr::Sub(child) => ItemExpr::Sub(Box::new(Self::resolve(child, refs, syms, cache))),
567            Expr::Not(child) => ItemExpr::Not(Box::new(Self::resolve(child, refs, syms, cache))),
568        }
569    }
570
571    fn cached_sym_to_item_expr(
572        syms: &HashMap<String, String>,
573        s: &String,
574        cache: &mut HashMap<String, ItemReference>,
575        refs: &HashSet<ItemReference>,
576    ) -> ItemExpr {
577        if let Some(v) = syms.get(s) {
578            ItemExpr::Value(v.clone())
579        } else {
580            if let Some(v) = cache.get(s) {
581                ItemExpr::ItemReference(v.clone())
582            } else {
583                if let Some(v) = refs.get(&ItemReference::Menu(s.clone())) {
584                    cache.insert(s.clone(), v.clone());
585                    ItemExpr::ItemReference(v.clone())
586                } else {
587                    if let Some(v) = refs.get(&ItemReference::MenuConfig(s.clone())) {
588                        cache.insert(s.clone(), v.clone());
589                        ItemExpr::ItemReference(v.clone())
590                    } else {
591                        if let Some(v) = refs.get(&ItemReference::Config(s.clone())) {
592                            cache.insert(s.clone(), v.clone());
593                            ItemExpr::ItemReference(v.clone())
594                        } else {
595                            ItemExpr::Unresolved(s.clone())
596                        }
597                    }
598                }
599            }
600        }
601    }
602
603    fn expr_to_item_expr(
604        exprs: &BTreeSet<Expr>,
605        refs: &HashSet<ItemReference>,
606        syms: &HashMap<String, String>,
607        cache: &mut HashMap<String, ItemReference>,
608    ) -> BTreeSet<ItemExpr> {
609        exprs
610            .iter()
611            .map(|e| Self::resolve(e, refs, syms, cache))
612            .collect()
613    }
614
615    fn itemrefs(&self) -> BTreeSet<ItemReference> {
616        let mut set: BTreeSet<ItemReference> = BTreeSet::new();
617
618        match self {
619            ItemExpr::Value(_) | ItemExpr::Unresolved(_) => set,
620            ItemExpr::ItemReference(itemref) => {
621                set.insert(itemref.clone());
622                set
623            }
624            ItemExpr::Eq(exprs)
625            | ItemExpr::Ne(exprs)
626            | ItemExpr::Lt(exprs)
627            | ItemExpr::Gt(exprs)
628            | ItemExpr::Lte(exprs)
629            | ItemExpr::Gte(exprs)
630            | ItemExpr::And(exprs)
631            | ItemExpr::Or(exprs) => {
632                for r in exprs.iter().map(|s| s.itemrefs()) {
633                    set.extend(r)
634                }
635                set
636            }
637            ItemExpr::Sub(expr) | ItemExpr::Not(expr) => {
638                set.extend(expr.itemrefs());
639                set
640            }
641        }
642    }
643}
644
645impl Display for ItemExpr {
646    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
647        match self {
648            Self::Value(s) => f.write_str(s)?,
649            Self::ItemReference(itemref) => {
650                f.write_str("&")?;
651                match itemref {
652                    ItemReference::Config(s) => {
653                        f.write_str("config => ")?;
654                        f.write_str(s)?
655                    }
656                    ItemReference::Menu(s) => {
657                        f.write_str("menu => ")?;
658                        f.write_str(s)?
659                    }
660                    ItemReference::MenuConfig(s) => {
661                        f.write_str("menuconfig => ")?;
662                        f.write_str(s)?
663                    }
664                }
665            }
666
667            Self::Eq(exprs) => {
668                f.write_str("eq {")?;
669                for expr in exprs {
670                    expr.fmt(f)?;
671                }
672                f.write_str("}")?;
673            }
674
675            Self::Ne(exprs) => {
676                f.write_str("ne {")?;
677                for expr in exprs {
678                    expr.fmt(f)?;
679                }
680                f.write_str("}")?;
681            }
682
683            Self::Lt(exprs) => {
684                f.write_str("lt {")?;
685                for expr in exprs {
686                    expr.fmt(f)?;
687                }
688                f.write_str("}")?;
689            }
690
691            Self::Gt(exprs) => {
692                f.write_str("gt {")?;
693                for expr in exprs {
694                    expr.fmt(f)?;
695                }
696                f.write_str("}")?;
697            }
698
699            Self::Lte(exprs) => {
700                f.write_str("lte {")?;
701                for expr in exprs {
702                    expr.fmt(f)?;
703                }
704                f.write_str("}")?;
705            }
706
707            Self::Gte(exprs) => {
708                f.write_str("gte {")?;
709                for expr in exprs {
710                    expr.fmt(f)?;
711                }
712                f.write_str("}")?;
713            }
714
715            Self::And(exprs) => {
716                f.write_str("and {")?;
717                for expr in exprs {
718                    expr.fmt(f)?;
719                }
720                f.write_str("}")?;
721            }
722
723            Self::Or(exprs) => {
724                f.write_str("or {")?;
725                for expr in exprs {
726                    expr.fmt(f)?;
727                }
728                f.write_str("}")?;
729            }
730
731            Self::Sub(expr) => {
732                f.write_str("(")?;
733                expr.fmt(f)?;
734                f.write_str(")")?;
735            }
736
737            Self::Not(expr) => {
738                f.write_str("not (")?;
739                expr.fmt(f)?;
740                f.write_str(")")?;
741            }
742
743            Self::Unresolved(s) => {
744                f.write_str("#")?;
745                f.write_str(s)?
746            }
747        };
748
749        Ok(())
750    }
751}
752
753#[derive(Clone, Eq, PartialEq, Debug, Hash)]
754pub enum ItemDependency {
755    Heavy(ResolvedExpr),
756    Normal(ResolvedExpr),
757    Trivial(ResolvedExpr),
758}
759
760impl ItemDependency {
761    pub fn expr<'a>(&'a self) -> &'a ResolvedExpr {
762        match self {
763            ItemDependency::Heavy(expr)
764            | ItemDependency::Normal(expr)
765            | ItemDependency::Trivial(expr) => expr,
766        }
767    }
768}