kconfig_represent/
variant.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 implements a variant type, which encapsulates the known types
60//! used by Kconfig, which are the bool, tristate, string, hex and int types.
61
62use crate::Hex;
63use crate::Tristate;
64use kconfig_parser::ast::ConfigType;
65use kconfig_parser::escape_string;
66use kconfig_parser::parse_string;
67use std::cmp::Ordering;
68use std::fmt::Display;
69use std::ops::Not;
70
71/// An evaluated value represents the value of a an expression after evaluation.
72#[derive(PartialEq, Eq, Clone, Debug)]
73pub struct Variant {
74    data: Data,
75}
76
77impl Variant {
78    pub fn datatype(&self) -> VariantDataType {
79        match &self.data {
80            Data::String(_) => VariantDataType::String,
81            Data::Bool(_) => VariantDataType::Bool,
82            Data::Hex(_) => VariantDataType::Hex,
83            Data::Int(_) => VariantDataType::Int,
84            Data::Tristate(_) => VariantDataType::Tristate,
85        }
86    }
87
88    pub fn new_typed(vdt: &VariantDataType) -> Self {
89        Self {
90            data: match vdt {
91                &VariantDataType::String => Data::String(String::default()),
92                &VariantDataType::Bool => Data::Bool(bool::default()),
93                &VariantDataType::Hex => Data::Hex(Hex::default()),
94                &VariantDataType::Int => Data::Int(i64::default()),
95                &VariantDataType::Tristate => Data::Tristate(Tristate::default()),
96            },
97        }
98    }
99
100    pub fn convert_into(&self, vdt: &VariantDataType) -> Self {
101        Self {
102            data: match vdt {
103                &VariantDataType::String => Data::String(self.clone().into()),
104                &VariantDataType::Bool => Data::Bool(self.clone().into()),
105                &VariantDataType::Hex => Data::Hex(self.clone().into()),
106                &VariantDataType::Int => Data::Int(self.clone().into()),
107                &VariantDataType::Tristate => Data::Tristate(self.clone().into()),
108            },
109        }
110    }
111
112    /// Converts the variant into a bool.
113    ///
114    /// If the variant contains a string value, the "n" or "N" string convert into
115    /// a false boolean, other strings convert into a true boolean.
116    ///
117    /// If the variant contains a numeric value, either an integer or hexadecimal
118    /// value, a 0 is converted into a false boolean, any other value into a true
119    /// boolean.
120    ///
121    /// If the variant contains a tristate value, only the false tristate value
122    /// will be converted into  a false boolean, either a true or maybe tristate
123    /// value will be converted into a true value.
124    ///
125    /// If the variant contains a boolean value, it will be returned as is.
126    pub fn to_bool(&self) -> bool {
127        match &self.data {
128            Data::String(s) => {
129                if s == "y" || s == "Y" {
130                    true
131                } else {
132                    false
133                }
134            }
135            Data::Bool(b) => *b,
136            Data::Tristate(t) => match t {
137                &Tristate::FALSE => false,
138                _ => true,
139            },
140            Data::Hex(h) => *h != Hex::from(0),
141            Data::Int(i) => *i != 0,
142        }
143    }
144
145    /// Converts the variant into a tristate.
146    ///
147    /// If the variant contains a string value, the "n" or "N" string convert into
148    /// a false tristate, the "m" or "M" strings convert into a maybe tristate,
149    /// any other strings convert into a true tristate.
150    ///
151    /// If the variant contains a numeric value, either an integer or hexadecimal
152    /// value, a 0 is converted into a false tristate, any other value into a true
153    /// tristate.
154    ///
155    /// If the variant contains a tristate value, it will be returned as is.
156    ///
157    /// If the variant contains a boolean value, it will be converted as tristate.
158    pub fn to_tristate(&self) -> Tristate {
159        match &self.data {
160            Data::String(s) => {
161                if s == "y" || s == "Y" {
162                    Tristate::TRUE
163                } else if s == "m" || s == "M" {
164                    Tristate::MAYBE
165                } else {
166                    Tristate::FALSE
167                }
168            }
169            Data::Bool(b) => Tristate::from(*b),
170            Data::Tristate(t) => *t,
171            Data::Hex(h) => {
172                if *h != Hex::from(0) {
173                    Tristate::TRUE
174                } else {
175                    Tristate::FALSE
176                }
177            }
178            Data::Int(i) => {
179                if *i != 0 {
180                    Tristate::TRUE
181                } else {
182                    Tristate::FALSE
183                }
184            }
185        }
186    }
187
188    /// Converts the variant into a hexadecimal value.
189    ///
190    /// If the variant contains a string value, an attempt will be made to convert
191    /// the string in to an integer. If that fails, and the string contains a "n"
192    /// or "N" value, it is converted into 0x00, otherwise it is interpreted as
193    /// 0x01.
194    ///
195    /// If the variant contains a numeric value, either an integer or hexadecimal
196    /// value, it will be returned as is, as hexadecimal representation. If the value
197    /// is an integer, the sign will be converted.
198    ///
199    /// If the variant contains a tristate value, if the tristate is a false value,
200    /// it will be converted as 0x00, otherwise it will be converted as 0x01.
201    ///
202    /// If the variant contains a boolean value, if the boolean is a false value,
203    /// it will be converted as 0x00, otherwise it will be converted as 0x01.
204    pub fn to_hex(&self) -> Hex {
205        match &self.data {
206            Data::String(s) => match Self::parse_hex(s) {
207                Some(Data::Hex(h)) => h,
208                _ => {
209                    if s == "y" || s == "Y" {
210                        Hex::from(1)
211                    } else {
212                        Hex::from(0)
213                    }
214                }
215            },
216            Data::Bool(b) => match b {
217                true => Hex::from(1),
218                false => Hex::from(0),
219            },
220            Data::Tristate(b) => match b {
221                &Tristate::TRUE => Hex::from(1),
222                &Tristate::FALSE => Hex::from(0),
223                &Tristate::MAYBE => Hex::from(1),
224            },
225            Data::Hex(h) => *h,
226            Data::Int(i) => Hex::from(*i as u64),
227        }
228    }
229
230    /// Converts the variant into an integer value.
231    ///
232    /// If the variant contains a string value, an attempt will be made to convert
233    /// the string in to an integer. If that fails, and the string contains a "n"
234    /// or "N" value, it is converted into 0, otherwise it is interpreted as
235    /// 1.
236    ///
237    /// If the variant contains a numeric value, either an integer or hexadecimal
238    /// value, it will be returned as is, as hexadecimal representation. If the value
239    /// is a hexadecimal, the sign will be converted.
240    ///
241    /// If the variant contains a tristate value, if the tristate is a false value,
242    /// it will be converted as 0, otherwise it will be converted as 1.
243    ///
244    /// If the variant contains a boolean value, if the boolean is a false value,
245    /// it will be converted as 0, otherwise it will be converted as 1.
246    pub fn to_int(&self) -> i64 {
247        match &self.data {
248            Data::String(s) => match Self::parse_int(s) {
249                Some(Data::Int(i)) => i,
250                _ => {
251                    if s == "y" || s == "Y" {
252                        1i64
253                    } else {
254                        0i64
255                    }
256                }
257            },
258            Data::Bool(b) => match b {
259                true => 1,
260                false => 0,
261            },
262            Data::Tristate(b) => match b {
263                &Tristate::TRUE => 1,
264                &Tristate::FALSE => 0,
265                &Tristate::MAYBE => 1,
266            },
267            Data::Hex(h) => {
268                let value: u64 = (*h).into();
269                value as i64
270            }
271            Data::Int(i) => *i,
272        }
273    }
274
275    /// Converts the variant into an string.
276    ///
277    /// If the variant contains a string value, it is returned as is.
278    ///
279    /// If the variant contains a numeric value, either an integer or hexadecimal
280    /// value, it will be converted into a string, either as numeric rendering or
281    /// hexadecimal rendering.
282    ///
283    /// If the variant contains a tristate value, if the tristate is a false value,
284    /// it will be converted as "n", if it is a maybe value, it will be converted
285    /// as "m", otherwise it will be converted as "y".
286    ///
287    /// If the variant contains a boolean value, if the boolean is a false value,
288    /// it will be converted as "n", otherwise it will be converted as "y".
289    fn to_string(&self) -> String {
290        match &self.data {
291            Data::String(s) => s.clone(),
292            Data::Bool(b) => match b {
293                &true => "y".to_owned(),
294                &false => "n".to_owned(),
295            },
296            Data::Tristate(t) => match t {
297                &Tristate::TRUE => "y".to_owned(),
298                &Tristate::FALSE => "n".to_owned(),
299                &Tristate::MAYBE => "m".to_owned(),
300            },
301            Data::Hex(h) => h.to_string(),
302            Data::Int(i) => i.to_string(),
303        }
304    }
305
306    fn parse_hex(v: &str) -> Option<Data> {
307        let v = v.replace("_", "").replace(" ", "");
308        let mut chars = v.chars();
309
310        let mut result = 0u64;
311
312        if let Some(next_char) = chars.next() {
313            if next_char == '0' {
314                // This possibly is a hexadecimal character
315                if let Some(next_char) = chars.next() {
316                    if next_char == 'x' || next_char == 'X' {
317                        // More likely a hexadecimal character
318                        let mut repeat = true;
319
320                        while repeat {
321                            repeat = if let Some(c) = chars.next() {
322                                result <<= 4u64;
323                                match c.to_digit(16) {
324                                    Some(v) => result |= v as u64,
325                                    None => return None,
326                                }
327                                true
328                            } else {
329                                false
330                            }
331                        }
332
333                        return Some(Data::Hex(Hex::from(result)));
334                    }
335                }
336            }
337        }
338
339        None
340    }
341
342    fn parse_int(v: &str) -> Option<Data> {
343        let v = v.replace("_", "").replace(" ", "");
344        let chars = v.chars();
345
346        let mut result = 0i64;
347
348        let mut is_negative = false;
349        let mut is_first = true;
350
351        for c in chars {
352            if is_first && c == '-' {
353                is_negative = true;
354                continue;
355            }
356            is_first = false;
357            result *= 10;
358            match c.to_digit(10) {
359                Some(v) => result += v as i64,
360                None => return None,
361            }
362        }
363
364        if is_negative {
365            result = 0 - result;
366        }
367
368        return Some(Data::Int(result));
369    }
370
371    /// Parses a given string, and attempts to return the closest matching
372    /// variant belonging to the string. If the string contains a "y" or "Y"
373    /// value, it is converted to a boolean true value, if the string contains
374    /// a "n" or "N" value, it is converted to a boolean false value, if the
375    /// string contains a "m" or "M" value, it is converted into a tristate
376    /// maybe value.
377    ///
378    /// If none of the above are true, an attempt is made to parse the string
379    /// as hexadecimal and integer values. If unsuccessful, it is considered
380    /// a normal string.
381    pub fn parse(v: &str) -> Self {
382        // If v == "y" or v == "Y" the current value is a boolean true
383        let data = if v == "y" || v == "Y" {
384            Data::Bool(true)
385        }
386        // If v == "n" or v == "N" the current value is a boolean false
387        else if v == "n" || v == "N" {
388            Data::Bool(false)
389        // If v == "m" or v == "M" the current value is a tristate maybe
390        } else if v == "m" || v == "M" {
391            Data::Tristate(Tristate::MAYBE)
392        } else {
393            // Attempt to convert the value to an integer (when possible)
394            if let Some(result) = Self::parse_int(v) {
395                result
396            // Attempt to convert the value to a hexadecimal integer (when possible)
397            } else if let Some(result) = Self::parse_hex(v) {
398                result
399            // Fall back to a string
400            } else {
401                Data::String(v.to_string())
402            }
403        };
404
405        Self { data: data }
406    }
407
408    /// Converts a dot config value back into a Variant
409    pub fn from_dot_config(v: &str) -> Self {
410        match parse_string(v) {
411            Ok(s) => Self {
412                data: Data::String(s),
413            },
414            Err(_) => Self::parse(v),
415        }
416    }
417
418    /// Converts the representation of the variant from a normal value to a .config
419    /// representation, as used by the Kconfig intermediate format.
420    pub fn dot_config(&self) -> String {
421        match &self.data {
422            Data::Bool(b) => match b {
423                true => "y".to_owned(),
424                false => "n".to_owned(),
425            },
426            Data::Tristate(t) => match t {
427                &Tristate::TRUE => "y".to_owned(),
428                &Tristate::MAYBE => "m".to_owned(),
429                &Tristate::FALSE => "n".to_owned(),
430            },
431            Data::String(s) => escape_string(&s),
432            Data::Int(i) => i.to_string(),
433            Data::Hex(h) => h.to_string(),
434        }
435    }
436
437    /// Transforms the variant from the current variant type, into the given variant type
438    pub fn transform(&self, vdt: VariantDataType) -> Variant {
439        match vdt {
440            VariantDataType::Bool => Variant::from(self.to_bool()),
441            VariantDataType::Hex => Variant::from(self.to_hex()),
442            VariantDataType::Int => Variant::from(self.to_int()),
443            VariantDataType::Tristate => Variant::from(self.to_tristate()),
444            VariantDataType::String => Variant::from(self.to_string()),
445        }
446    }
447}
448
449impl Display for Variant {
450    /// Formats the display of the variant as value
451    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
452        f.write_str(&self.to_string())
453    }
454}
455
456impl From<&str> for Variant {
457    /// Creates a variant from a string
458    fn from(v: &str) -> Self {
459        Self {
460            data: Data::String(v.to_string()),
461        }
462    }
463}
464
465impl From<String> for Variant {
466    /// Creates a variant from a string
467    fn from(v: String) -> Self {
468        Self {
469            data: Data::String(v),
470        }
471    }
472}
473
474impl From<bool> for Variant {
475    /// Creates a variant from a boolean value
476    fn from(b: bool) -> Self {
477        Self {
478            data: Data::Bool(b),
479        }
480    }
481}
482
483impl From<Tristate> for Variant {
484    /// Creates a variant from a tristate value
485    fn from(t: Tristate) -> Self {
486        Self {
487            data: Data::Tristate(t),
488        }
489    }
490}
491
492impl From<i64> for Variant {
493    /// Creates a variant from an integer value
494    fn from(i: i64) -> Self {
495        Self { data: Data::Int(i) }
496    }
497}
498
499impl From<Hex> for Variant {
500    /// Creates a variant form a hexadecimal value
501    fn from(h: Hex) -> Self {
502        Self { data: Data::Hex(h) }
503    }
504}
505
506impl Not for Variant {
507    type Output = Variant;
508
509    /// Inverts the variant. If the variant is a boolean,
510    /// inverts it directly. If the variant is not a boolean,
511    /// it is converted to a tristate value, then it is inverted,
512    /// such that the maybe tristate value, retains its properties.
513    fn not(self) -> Self::Output {
514        if let Data::Bool(v) = &self.data {
515            Variant {
516                data: Data::Bool(!v),
517            }
518        } else {
519            let v = self.to_tristate();
520            Variant {
521                data: Data::Tristate(!v),
522            }
523        }
524    }
525}
526
527impl Into<bool> for Variant {
528    /// Converts the variant into a boolean value, using the
529    /// [`Self::to_bool()`] function.
530    fn into(self) -> bool {
531        self.to_bool()
532    }
533}
534
535impl Into<String> for Variant {
536    /// Converts the variant into a string, using the
537    /// [`Self::to_string()`] function.
538    fn into(self) -> String {
539        self.to_string()
540    }
541}
542
543impl Into<Tristate> for Variant {
544    /// Converts the variant into a tristate value, using the
545    /// [`Self::to_tristate()`] function.
546    fn into(self) -> Tristate {
547        self.to_tristate()
548    }
549}
550
551impl Into<i64> for Variant {
552    /// Converts the variant into an integer value, using the
553    /// [`Self::to_int()`] function.
554    fn into(self) -> i64 {
555        self.to_int()
556    }
557}
558
559impl Into<Hex> for Variant {
560    /// Converts the variant into a hexadecimal value, using the
561    /// [`Self::to_hex()`] function.
562    fn into(self) -> Hex {
563        self.to_hex()
564    }
565}
566
567impl Ord for Variant {
568    /// If self and other are both of integer or hexadecimal type,
569    /// then comparison will be performed by integer comparison.
570    ///
571    /// If self and other are both of boolean or tristate type,
572    /// then comparison will be performed on tristate value,
573    /// thus y > m > n.
574    ///
575    /// If one is of type boolean/tristate and the other is of
576    /// type integer/hexadecimal, the integer/hexadecimal type
577    /// will be converted to it's boolean equivalent, then
578    /// compared
579    ///
580    /// Everything else will be compared as if it were two
581    /// string types.
582    fn cmp(&self, other: &Self) -> Ordering {
583        if self.datatype().numberlike() && other.datatype().numberlike() {
584            let me: i64 = self.clone().into();
585            let other: i64 = other.clone().into();
586            return me.cmp(&other);
587        }
588
589        if self.datatype().boollike() && other.datatype().boollike() {
590            let me: Tristate = self.clone().into();
591            let other: Tristate = other.clone().into();
592            return me.cmp(&other);
593        }
594
595        if self.datatype().numberlike() && other.datatype().boollike()
596            || self.datatype().boollike() && other.datatype().numberlike()
597        {
598            let me: Tristate = self.clone().into();
599            let other: Tristate = other.clone().into();
600            return me.cmp(&other);
601        }
602
603        let me: String = self.clone().into();
604        let other: String = self.clone().into();
605        me.cmp(&other)
606    }
607}
608
609impl PartialOrd for Variant {
610    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
611        Some(self.cmp(other))
612    }
613}
614
615#[derive(PartialEq, Eq, Clone, Debug)]
616enum Data {
617    String(String),
618    Bool(bool),
619    Tristate(Tristate),
620    Hex(Hex),
621    Int(i64),
622}
623
624#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
625pub enum VariantDataType {
626    /// The data type underpinning the variant is a character string
627    String,
628    /// The data type underpinning the variant is a boolean value
629    Bool,
630    /// The data type underpinning the variant is a tristate value
631    Tristate,
632    /// The data type underpinning the variant is a hexadecimal value
633    Hex,
634    /// The data type underpinning the variant is an integer value
635    Int,
636}
637
638impl VariantDataType {
639    pub(self) fn numberlike(&self) -> bool {
640        match self {
641            Self::Hex | Self::Int => true,
642            _ => false,
643        }
644    }
645
646    pub(self) fn boollike(&self) -> bool {
647        match self {
648            Self::Bool | Self::Tristate => true,
649            _ => false,
650        }
651    }
652}
653
654impl From<&ConfigType> for VariantDataType {
655    /// Converts a given ConfigType value to the associated VariantDataType value
656    fn from(ct: &ConfigType) -> Self {
657        match ct {
658            ConfigType::Bool => Self::Bool,
659            ConfigType::String => Self::String,
660            ConfigType::Tristate => Self::Tristate,
661            ConfigType::Hex => Self::Hex,
662            ConfigType::Int => Self::Int,
663        }
664    }
665}
666
667impl Into<ConfigType> for VariantDataType {
668    /// Converts a given VariantDataType back into a ConfigType
669    fn into(self) -> ConfigType {
670        match self {
671            Self::Bool => ConfigType::Bool,
672            Self::String => ConfigType::String,
673            Self::Tristate => ConfigType::Tristate,
674            Self::Hex => ConfigType::Hex,
675            Self::Int => ConfigType::Int,
676        }
677    }
678}
679
680#[cfg(test)]
681mod tests {
682    use crate::Hex;
683    use crate::Tristate;
684    use crate::{Error, Variant, VariantDataType};
685    use kconfig_parser::ast::ConfigType;
686
687    #[test]
688    fn bool_variant_false() -> Result<(), Error> {
689        let result: bool = Variant::from(false).into();
690        assert_eq!(false, result);
691        let result: String = Variant::from(false).into();
692        assert_eq!("n".to_owned(), result);
693        let result: Tristate = Variant::from(false).into();
694        assert_eq!(Tristate::FALSE, result);
695        let result: i64 = Variant::from(false).into();
696        assert_eq!(0, result);
697        let result: Hex = Variant::from(false).into();
698        assert_eq!(Hex::from(0), result);
699        Ok(())
700    }
701
702    #[test]
703    fn bool_variant_true() -> Result<(), Error> {
704        let result: bool = Variant::from(true).into();
705        assert_eq!(true, result);
706        let result: String = Variant::from(true).into();
707        assert_eq!("y".to_owned(), result);
708        let result: Tristate = Variant::from(true).into();
709        assert_eq!(Tristate::TRUE, result);
710        let result: i64 = Variant::from(true).into();
711        assert_eq!(1, result);
712        let result: Hex = Variant::from(true).into();
713        assert_eq!(Hex::from(1), result);
714        Ok(())
715    }
716
717    #[test]
718    fn string_variant_falsely() -> Result<(), Error> {
719        let result: bool = Variant::from("n").into();
720        assert_eq!(false, result);
721        let result: String = Variant::from("n").into();
722        assert_eq!("n".to_owned(), result);
723        let result: Tristate = Variant::from("n").into();
724        assert_eq!(Tristate::FALSE, result);
725        let result: i64 = Variant::from("n").into();
726        assert_eq!(0, result);
727        let result: Hex = Variant::from("n").into();
728        assert_eq!(Hex::from(0), result);
729        Ok(())
730    }
731
732    #[test]
733    fn string_variant_maybe() -> Result<(), Error> {
734        let result: Tristate = Variant::from("m").into();
735        assert_eq!(Tristate::MAYBE, result);
736        Ok(())
737    }
738
739    #[test]
740    fn string_variant_truth() -> Result<(), Error> {
741        let result: bool = Variant::from("y").into();
742        assert_eq!(true, result);
743        let result: String = Variant::from("y").into();
744        assert_eq!("y".to_owned(), result);
745        let result: Tristate = Variant::from("y").into();
746        assert_eq!(Tristate::TRUE, result);
747        let result: i64 = Variant::from("y").into();
748        assert_eq!(1, result);
749        let result: Hex = Variant::from("y").into();
750        assert_eq!(Hex::from(1), result);
751
752        let result: bool = Variant::from("foo").into();
753        assert_eq!(false, result);
754        let result: String = Variant::from("foo").into();
755        assert_eq!("foo".to_owned(), result);
756        let result: Tristate = Variant::from("foo").into();
757        assert_eq!(Tristate::FALSE, result);
758        let result: i64 = Variant::from("foo").into();
759        assert_eq!(0, result);
760        let result: Hex = Variant::from("foo").into();
761        assert_eq!(Hex::from(0), result);
762        Ok(())
763    }
764
765    #[test]
766    fn tristate_variant_falsely() -> Result<(), Error> {
767        let result: bool = Variant::from(Tristate::FALSE).into();
768        assert_eq!(false, result);
769        let result: String = Variant::from(Tristate::FALSE).into();
770        assert_eq!("n".to_owned(), result);
771        let result: Tristate = Variant::from(Tristate::FALSE).into();
772        assert_eq!(Tristate::FALSE, result);
773        let result: i64 = Variant::from(Tristate::FALSE).into();
774        assert_eq!(0, result);
775        let result: Hex = Variant::from(Tristate::FALSE).into();
776        assert_eq!(Hex::from(0), result);
777        Ok(())
778    }
779
780    #[test]
781    fn tristate_variant_true() -> Result<(), Error> {
782        let result: bool = Variant::from(Tristate::TRUE).into();
783        assert_eq!(true, result);
784        let result: String = Variant::from(Tristate::TRUE).into();
785        assert_eq!("y".to_owned(), result);
786        let result: Tristate = Variant::from(Tristate::TRUE).into();
787        assert_eq!(Tristate::TRUE, result);
788        let result: i64 = Variant::from(Tristate::TRUE).into();
789        assert_eq!(1, result);
790        let result: Hex = Variant::from(Tristate::TRUE).into();
791        assert_eq!(Hex::from(1), result);
792
793        let result: bool = Variant::from(Tristate::MAYBE).into();
794        assert_eq!(true, result);
795        let result: String = Variant::from(Tristate::MAYBE).into();
796        assert_eq!("m".to_owned(), result);
797        let result: Tristate = Variant::from(Tristate::MAYBE).into();
798        assert_eq!(Tristate::MAYBE, result);
799        let result: i64 = Variant::from(Tristate::MAYBE).into();
800        assert_eq!(1, result);
801        let result: Hex = Variant::from(Tristate::MAYBE).into();
802        assert_eq!(Hex::from(1), result);
803        Ok(())
804    }
805
806    #[test]
807    fn int_variant_falsely() -> Result<(), Error> {
808        let result: bool = Variant::from(0).into();
809        assert_eq!(false, result);
810        let result: String = Variant::from(0).into();
811        assert_eq!("0".to_owned(), result);
812        let result: Tristate = Variant::from(0).into();
813        assert_eq!(Tristate::FALSE, result);
814        let result: i64 = Variant::from(0).into();
815        assert_eq!(0, result);
816        let result: Hex = Variant::from(0).into();
817        assert_eq!(Hex::from(0), result);
818        Ok(())
819    }
820
821    #[test]
822    fn int_variant_truth() -> Result<(), Error> {
823        for number in vec![1, 2, -1] {
824            let result: bool = Variant::from(number).into();
825            assert_eq!(true, result);
826            let result: String = Variant::from(number).into();
827            let s = number.to_string();
828            assert_eq!(s, result);
829            let result: Tristate = Variant::from(number).into();
830            assert_eq!(Tristate::TRUE, result);
831            let result: i64 = Variant::from(number).into();
832            assert_eq!(number, result);
833            let result: Hex = Variant::from(number).into();
834            if number > 0 {
835                assert_eq!(Hex::from(number as u64), result);
836            } else if number == -1 {
837                assert_eq!(Hex::from(0xffffffffffffffff), result);
838            }
839        }
840        Ok(())
841    }
842
843    #[test]
844    fn hex_variant_falsely() -> Result<(), Error> {
845        let number = Hex::from(0);
846        let result: bool = Variant::from(number).into();
847        assert_eq!(false, result);
848        let result: String = Variant::from(number).into();
849        assert_eq!("0x0".to_owned(), result);
850        let result: Tristate = Variant::from(number).into();
851        assert_eq!(Tristate::FALSE, result);
852        let result: i64 = Variant::from(number).into();
853        assert_eq!(0, result);
854        let result: Hex = Variant::from(number).into();
855        assert_eq!(Hex::from(0), result);
856        Ok(())
857    }
858
859    #[test]
860    fn hex_variant_truth() -> Result<(), Error> {
861        for number in vec![Hex::from(1), Hex::from(2), Hex::from(0xffffffffffffffff)] {
862            let result: bool = Variant::from(number).into();
863            assert_eq!(true, result);
864            let result: String = Variant::from(number).into();
865            let s = number.to_string();
866            assert_eq!(s, result);
867            let result: Tristate = Variant::from(number).into();
868            assert_eq!(Tristate::TRUE, result);
869            let result: i64 = Variant::from(number).into();
870            if number <= Hex::from(2) {
871                let value: u64 = number.into();
872                assert_eq!(value as i64, result);
873            } else if number == Hex::from(0xffffffffffffffff) {
874                assert_eq!(-1, result);
875            }
876            let result: Hex = Variant::from(number).into();
877            assert_eq!(number, result);
878        }
879        Ok(())
880    }
881
882    #[test]
883    fn datatype() -> Result<(), Error> {
884        assert_eq!(VariantDataType::Bool, Variant::from(false).datatype());
885        assert_eq!(VariantDataType::Bool, Variant::from(true).datatype());
886        assert_eq!(
887            VariantDataType::Tristate,
888            Variant::from(Tristate::FALSE).datatype()
889        );
890        assert_eq!(
891            VariantDataType::Tristate,
892            Variant::from(Tristate::TRUE).datatype()
893        );
894        assert_eq!(
895            VariantDataType::Tristate,
896            Variant::from(Tristate::MAYBE).datatype()
897        );
898        for number in vec![0, 1, 2, -1] {
899            assert_eq!(VariantDataType::Int, Variant::from(number).datatype());
900        }
901        for number in vec![Hex::from(1), Hex::from(2), Hex::from(0xffffffffffffffff)] {
902            assert_eq!(VariantDataType::Hex, Variant::from(number).datatype());
903        }
904        assert_eq!(VariantDataType::String, Variant::from("").datatype());
905        Ok(())
906    }
907
908    #[test]
909    fn new_types() -> Result<(), Error> {
910        assert_eq!(
911            "".to_owned(),
912            Variant::new_typed(&VariantDataType::String).to_string()
913        );
914        assert_eq!(false, Variant::new_typed(&VariantDataType::Bool).to_bool());
915        assert_eq!(
916            Tristate::FALSE,
917            Variant::new_typed(&VariantDataType::Tristate).to_tristate()
918        );
919        assert_eq!(0, Variant::new_typed(&VariantDataType::Int).to_int());
920        assert_eq!(
921            Hex::from(0),
922            Variant::new_typed(&VariantDataType::Hex).to_hex()
923        );
924        Ok(())
925    }
926
927    #[test]
928    fn to_hex() -> Result<(), Error> {
929        let value = Variant::from("123456789");
930        let result: i64 = value.into();
931        assert_eq!(123456789, result);
932
933        // Negative
934        let value = Variant::from("-123456789");
935        let result: i64 = value.into();
936        assert_eq!(-123456789, result);
937
938        // Allow spaces
939        let value = Variant::from(" 123 456 789 ");
940        let result: i64 = value.into();
941        assert_eq!(123456789, result);
942
943        // Allow underscores
944        let value = Variant::from("_123_456_789_");
945        let result: i64 = value.into();
946        assert_eq!(123456789, result);
947
948        let value = Variant::from("0x123456789abcdef");
949        let result: Hex = value.into();
950        assert_eq!(Hex::from(0x123456789abcdef), result);
951
952        // Allow spaces
953        let value = Variant::from(" 0 x 1234 5678 9abc def0 ");
954        let result: Hex = value.into();
955        assert_eq!(Hex::from(0x123456789abcdef0), result);
956
957        // Allow underscores
958        let value = Variant::from("_0_x_1234_5678_9abc_def0_");
959        let result: Hex = value.into();
960        assert_eq!(Hex::from(0x123456789abcdef0), result);
961
962        Ok(())
963    }
964
965    #[test]
966    fn not() -> Result<(), Error> {
967        let value = Variant::from(true);
968        let inverse = !value;
969        assert_eq!(Variant::from(false), inverse);
970
971        let value = Variant::from(false);
972        let inverse = !value;
973        assert_eq!(Variant::from(true), inverse);
974
975        let value = Variant::from(Tristate::TRUE);
976        let inverse = !value;
977        assert_eq!(Variant::from(Tristate::FALSE), inverse);
978
979        let value = Variant::from(Tristate::MAYBE);
980        let inverse = !value;
981        assert_eq!(Variant::from(Tristate::MAYBE), inverse);
982
983        let value = Variant::from(Tristate::FALSE);
984        let inverse = !value;
985        assert_eq!(Variant::from(Tristate::TRUE), inverse);
986
987        Ok(())
988    }
989
990    #[test]
991    fn datatype_2_config_type() -> Result<(), Error> {
992        let config_type: ConfigType = VariantDataType::Bool.into();
993        assert_eq!(ConfigType::Bool, config_type);
994
995        let config_type: ConfigType = VariantDataType::Int.into();
996        assert_eq!(ConfigType::Int, config_type);
997
998        let config_type: ConfigType = VariantDataType::String.into();
999        assert_eq!(ConfigType::String, config_type);
1000
1001        let config_type: ConfigType = VariantDataType::Hex.into();
1002        assert_eq!(ConfigType::Hex, config_type);
1003
1004        let config_type: ConfigType = VariantDataType::Tristate.into();
1005        assert_eq!(ConfigType::Tristate, config_type);
1006
1007        Ok(())
1008    }
1009
1010    #[test]
1011    fn config_type_2_datatype() -> Result<(), Error> {
1012        let datatype = VariantDataType::from(&ConfigType::Bool);
1013        assert_eq!(VariantDataType::Bool, datatype);
1014
1015        let datatype = VariantDataType::from(&ConfigType::Tristate);
1016        assert_eq!(VariantDataType::Tristate, datatype);
1017
1018        let datatype = VariantDataType::from(&ConfigType::Hex);
1019        assert_eq!(VariantDataType::Hex, datatype);
1020
1021        let datatype = VariantDataType::from(&ConfigType::String);
1022        assert_eq!(VariantDataType::String, datatype);
1023
1024        let datatype = VariantDataType::from(&ConfigType::Int);
1025        assert_eq!(VariantDataType::Int, datatype);
1026
1027        Ok(())
1028    }
1029}