donet_core/
dckeyword.rs

1/*
2    This file is part of Donet.
3
4    Copyright © 2024-2025 Max Rodriguez <[email protected]>
5
6    Donet is free software; you can redistribute it and/or modify
7    it under the terms of the GNU Affero General Public License,
8    as published by the Free Software Foundation, either version 3
9    of the License, or (at your option) any later version.
10
11    Donet is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14    GNU Affero General Public License for more details.
15
16    You should have received a copy of the GNU Affero General Public
17    License along with Donet. If not, see <https://www.gnu.org/licenses/>.
18*/
19
20//! Representation of arbitrary and historical
21//! keywords as defined in the DC file.
22
23use crate::hashgen::*;
24
25#[derive(Debug, PartialEq, Eq)]
26pub struct DCKeyword(String);
27
28impl std::fmt::Display for DCKeyword {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        write!(f, "keyword ")?;
31        f.write_str(&self.0)?;
32        write!(f, ";")
33    }
34}
35
36impl LegacyDCHash for DCKeyword {
37    fn generate_hash(&self, hashgen: &mut DCHashGenerator) {
38        hashgen.add_string(self.0.clone());
39    }
40}
41
42impl DCKeyword {
43    #[inline]
44    pub fn get_name(&self) -> String {
45        self.0.clone()
46    }
47}
48
49/// Represents the two types of inputs that `DCKeywordList.has_keyword`
50/// accepts for looking up a Keyword. In Panda and Astron, the
51/// `has_keyword` method is overloaded instead.
52pub enum IdentifyKeyword {
53    ByStruct(DCKeyword),
54    ByName(String),
55}
56
57/// This is a list of [`DCKeyword`] structures, which represent
58/// communication keywords that may be set on a particular field.
59#[derive(Debug)]
60pub struct DCKeywordList {
61    keywords: Vec<DCKeyword>,
62}
63/* TODO!
64impl std::cmp::PartialEq for DCKeywordList {
65    fn eq(&self, other: &Self) -> bool {
66        let target_kw_map: KeywordName2Keyword = other._get_keywords_by_name_map();
67
68        // If our maps are different sizes, they are already not the same.
69        if self.kw_name_2_keyword.len() != target_kw_map.len() {
70            return false;
71        }
72
73        // Since MultiMap does not implement the Eq trait,
74        // we have to iterate through both maps and compare.
75        for key in self.kw_name_2_keyword.keys() {
76            if !target_kw_map.contains_key(key) {
77                return false;
78            }
79        }
80        true // no differences found
81    }
82}*/
83
84impl std::fmt::Display for DCKeywordList {
85    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86        for (i, kw) in self.keywords.iter().enumerate() {
87            // We do not call the fmt::Display impl of [`DCKeyword`] here,
88            // as that formats it as a declaration, not use in a field's
89            // keyword list. So, we just need to format the kw identifier.
90            f.write_str(&kw.0)?;
91
92            if i != self.keywords.len() - 1 {
93                write!(f, " ")?;
94            }
95        }
96        writeln!(f, ";")
97    }
98}
99
100impl LegacyDCHash for DCKeywordList {
101    fn generate_hash(&self, hashgen: &mut DCHashGenerator) {
102        hashgen.add_int(self.keywords.len().try_into().unwrap());
103
104        for keyword in &self.keywords {
105            keyword.generate_hash(hashgen);
106        }
107    }
108}
109
110impl DCKeywordList {
111    /// Returns the number of keywords in this keyword list.
112    pub fn get_num_keywords(&self) -> usize {
113        self.keywords.len()
114    }
115
116    /// Returns `true` if given keyword identifier or struct
117    /// is present in this keyword list.
118    pub fn has_keyword(&self, kw: IdentifyKeyword) -> bool {
119        match kw {
120            IdentifyKeyword::ByName(_kw_id) => todo!("TODO!"),
121            IdentifyKeyword::ByStruct(kw_obj) => {
122                for keyword in &self.keywords {
123                    if *keyword == kw_obj {
124                        return true;
125                    }
126                }
127                false // no match found
128            }
129        }
130    }
131
132    /// Returns [`DCKeyword`] reference by index, wrapped in an Option.
133    pub fn get_keyword(&self, index: usize) -> Option<&DCKeyword> {
134        self.keywords.get(index)
135    }
136}
137
138pub(crate) mod semantics {
139    /* TODO!
140    pub fn add_keyword(element: &mut DCKeywordList, keyword: DCKeyword) -> Result<(), ()> {
141        let kw_name: String = keyword.name.clone(); // avoid moving 'name'
142
143        if self.kw_name_2_keyword.get(&kw_name).is_some() {
144            return Err(()); // keyword is already in our list!
145        }
146
147        self.keywords.push(Rc::new(keyword));
148        self.kw_name_2_keyword
149            .insert(kw_name, self.keywords.last().unwrap().clone());
150        Ok(())
151    }
152
153    /// Overwrites the DCKeywords of this list with the target's DCKeywords.
154    pub fn copy_keywords(element: &mut DCKeywordList, target: &DCKeywordList) {
155        let target_kw_array: Vec<Rc<DCKeyword>> = target._get_keyword_list();
156        let target_kw_map: MultiMap<String, Rc<DCKeyword>> = target._get_keywords_by_name_map();
157
158        self.keywords = target_kw_array; // old vec will be dropped from memory
159        self.kw_name_2_keyword = target_kw_map;
160    }*/
161}