1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
pub( crate ) mod private
{
  use crate::*;
  use wtools;
  use wtools::{ error::Result, err };

  /// Available types that can be converted to a `Value`
  ///
  /// Uses for configure subjects and properties types to validate it after parsing.
  ///
  /// ```
  /// # use wca::{ Type, Value, TryCast };
  /// # fn main() -> Result< (), Box< dyn std::error::Error > > {
  /// let raw_value = "3".to_string();
  /// let kind = Type::Number;
  ///
  /// let value = kind.try_cast( raw_value )?;
  /// assert_eq!( Value::Number( 3.0 ), value );
  /// # Ok( () ) }
  /// ```
  ///
  /// In the above example, the `Type` enum is used to represent the expected type of the value for a property. The `Number` type is chosen, and the raw value is parsed and validated to ensure it matches this type.
  ///
  #[ derive( Debug, Clone, PartialEq, Eq ) ]
  pub enum Type
  {
    /// String
    String,
    /// Number
    Number,
    /// Path
    Path,
    /// Bool
    Bool,
    /// List of some type values separated a delimiter character
    List( Box< Type >, char ),
  }

  /// Can be implemented for something that represents a type of value
  pub trait TryCast< T >
  {
    /// return casted value
    fn try_cast( &self, value : String ) -> Result< T >;
  }

  /// Container for a `Value` of a specific type
  ///
  /// Uses for represent of subjects and properties in Commands( E.g. `VerifiedCommand`, `ExecutableCommand_` )
  /// With `wca::Type` enum and `TryCast` you can cast raw string into specific Type.
  /// You can also convert to a type that can be converted from the internal Value type.
  ///
  /// # Example:
  ///
  /// ```
  /// # use wca::{ VerifiedCommand, Value };
  /// # use std::collections::HashMap;
  /// let command = VerifiedCommand
  /// {
  ///   phrase : "command".to_string(),
  ///   // Here is numeric value used
  ///   subjects : vec![ Value::Number( 3.14 ) ],
  ///   properties : HashMap::from_iter(
  ///   [
  ///     // Here is string value used
  ///     ( "string_prop".to_string(), Value::String( "value".to_string() ) ),
  ///   ])
  /// };
  ///
  /// let number : f32 = command.subjects[ 0 ].clone().into();
  /// assert_eq!( 3.14, number );
  ///
  /// let number : i32 = command.subjects[ 0 ].clone().into();
  /// assert_eq!( 3, number );
  /// ```
  #[ derive( Debug, Clone, PartialEq ) ]
  pub enum Value
  {
    /// String value
    String( String ),
    /// Number value(float number but can be casted to another types)
    Number( f64 ),
    /// Path
    Path( std::path::PathBuf ),
    /// Bool
    Bool( bool ),
    /// List
    List( Vec< Value > ),
  }

  macro_rules! value_into_impl
  {
    ( $( $value_kind : path => $( $kind : ty => $cast : expr ),+ );+ ) =>
    {
      $( $(
        impl From< Value > for $kind
        {
          fn from( value : Value ) -> Self
          {
            match value
            {
              #[ allow( clippy::redundant_closure_call ) ] // ok because of it improve understanding what is `value` at macro call
              $value_kind( value ) => ( $cast )( value ),
              _ => panic!( "Unknown cast variant. Got `{value:?}` and try to cast to `{}`", stringify!( $kind ) )
            }
          }
        }
      )+ )+
    };
  }

  // makes from Value variant an native value
  value_into_impl!
  {
    Value::Number =>
      u32 => | value | value as u32,
      u64 => | value | value as u64,
      i32 => | value | value as i32,
      i64 => | value | value as i64,
      f32 => | value | value as f32,
      f64 => | value | value;
    Value::Bool =>
      bool => | value | value;
    Value::String =>
      String => String::from,
      &'static str => | value : String | Box::leak( value.into_boxed_str() );
    Value::Path =>
      std::path::PathBuf => | value | value
  }

  impl< T : From< Value > > From< Value > for Vec< T >
  {
    fn from( value : Value ) -> Self
    {
      match value
      {
        Value::List( value ) => value.into_iter().map( | x | x.into() ).collect(),
        _ => panic!( "Unknown cast variant. Got `{value:?}` and try to cast to `Vec<{}>`", std::any::type_name::< T >() )
      }
    }
  }

  impl TryCast< Value > for Type
  {
    fn try_cast( &self, value : String ) -> Result< Value >
    {
      match self
      {
        Self::String => Ok( Value::String( value ) ),
        Self::Number => value.parse().map_err( | _ | err!( "Can not parse number from `{}`", value ) ).map( Value::Number ),
        Self::Path => Ok( Value::Path( value.into() ) ),
        Self::Bool => Ok( Value::Bool( match value.as_str() { "1" | "true" => true, "0" | "false" => false, _ => return Err( err!( "Can not parse bool from `{}`", value ) ) } ) ),
        Self::List( kind, delimeter ) =>
        {
          let values = value
          .split( *delimeter )
          .map( | val | kind.try_cast( val.into() ) )
          .collect::< Result< Vec< Value > > >()?;
          Ok( Value::List( values ) )
        },
      }
    }
  }
}

//

crate::mod_interface!
{
  exposed use Type;
  exposed use Value;
  exposed use TryCast;
}