tauri_utils/acl/
value.rs

1// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5//! A [`Value`] that is used instead of [`toml::Value`] or [`serde_json::Value`]
6//! to support both formats.
7
8use std::collections::BTreeMap;
9use std::fmt::Debug;
10
11use serde::{Deserialize, Serialize};
12
13/// A valid ACL number.
14#[derive(Debug, PartialEq, Serialize, Deserialize, Copy, Clone, PartialOrd)]
15#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
16#[serde(untagged)]
17pub enum Number {
18  /// Represents an [`i64`].
19  Int(i64),
20
21  /// Represents a [`f64`].
22  Float(f64),
23}
24
25impl From<i64> for Number {
26  #[inline(always)]
27  fn from(value: i64) -> Self {
28    Self::Int(value)
29  }
30}
31
32impl From<f64> for Number {
33  #[inline(always)]
34  fn from(value: f64) -> Self {
35    Self::Float(value)
36  }
37}
38
39/// All supported ACL values.
40#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, PartialOrd)]
41#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
42#[serde(untagged)]
43pub enum Value {
44  /// Represents a null JSON value.
45  Null,
46
47  /// Represents a [`bool`].
48  Bool(bool),
49
50  /// Represents a valid ACL [`Number`].
51  Number(Number),
52
53  /// Represents a [`String`].
54  String(String),
55
56  /// Represents a list of other [`Value`]s.
57  List(Vec<Value>),
58
59  /// Represents a map of [`String`] keys to [`Value`]s.
60  Map(BTreeMap<String, Value>),
61}
62
63impl From<Value> for serde_json::Value {
64  fn from(value: Value) -> Self {
65    match value {
66      Value::Null => serde_json::Value::Null,
67      Value::Bool(b) => serde_json::Value::Bool(b),
68      Value::Number(Number::Float(f)) => {
69        serde_json::Value::Number(serde_json::Number::from_f64(f).unwrap())
70      }
71      Value::Number(Number::Int(i)) => serde_json::Value::Number(i.into()),
72      Value::String(s) => serde_json::Value::String(s),
73      Value::List(list) => serde_json::Value::Array(list.into_iter().map(Into::into).collect()),
74      Value::Map(map) => serde_json::Value::Object(
75        map
76          .into_iter()
77          .map(|(key, value)| (key, value.into()))
78          .collect(),
79      ),
80    }
81  }
82}
83
84impl From<serde_json::Value> for Value {
85  fn from(value: serde_json::Value) -> Self {
86    match value {
87      serde_json::Value::Null => Value::Null,
88      serde_json::Value::Bool(b) => Value::Bool(b),
89      serde_json::Value::Number(n) => Value::Number(if let Some(f) = n.as_f64() {
90        Number::Float(f)
91      } else if let Some(n) = n.as_u64() {
92        Number::Int(n as i64)
93      } else if let Some(n) = n.as_i64() {
94        Number::Int(n)
95      } else {
96        Number::Int(0)
97      }),
98      serde_json::Value::String(s) => Value::String(s),
99      serde_json::Value::Array(list) => Value::List(list.into_iter().map(Into::into).collect()),
100      serde_json::Value::Object(map) => Value::Map(
101        map
102          .into_iter()
103          .map(|(key, value)| (key, value.into()))
104          .collect(),
105      ),
106    }
107  }
108}
109
110impl From<bool> for Value {
111  #[inline(always)]
112  fn from(value: bool) -> Self {
113    Self::Bool(value)
114  }
115}
116
117impl<T: Into<Number>> From<T> for Value {
118  #[inline(always)]
119  fn from(value: T) -> Self {
120    Self::Number(value.into())
121  }
122}
123
124impl From<String> for Value {
125  #[inline(always)]
126  fn from(value: String) -> Self {
127    Value::String(value)
128  }
129}
130
131impl From<toml::Value> for Value {
132  #[inline(always)]
133  fn from(value: toml::Value) -> Self {
134    use toml::Value as Toml;
135
136    match value {
137      Toml::String(s) => s.into(),
138      Toml::Integer(i) => i.into(),
139      Toml::Float(f) => f.into(),
140      Toml::Boolean(b) => b.into(),
141      Toml::Datetime(d) => d.to_string().into(),
142      Toml::Array(a) => Value::List(a.into_iter().map(Value::from).collect()),
143      Toml::Table(t) => Value::Map(t.into_iter().map(|(k, v)| (k, v.into())).collect()),
144    }
145  }
146}
147
148#[cfg(feature = "build")]
149mod build {
150  use std::convert::identity;
151
152  use crate::tokens::*;
153
154  use super::*;
155  use proc_macro2::TokenStream;
156  use quote::{quote, ToTokens, TokenStreamExt};
157
158  impl ToTokens for Number {
159    fn to_tokens(&self, tokens: &mut TokenStream) {
160      let prefix = quote! { ::tauri::utils::acl::Number };
161
162      tokens.append_all(match self {
163        Self::Int(i) => {
164          quote! { #prefix::Int(#i) }
165        }
166        Self::Float(f) => {
167          quote! { #prefix::Float (#f) }
168        }
169      });
170    }
171  }
172
173  impl ToTokens for Value {
174    fn to_tokens(&self, tokens: &mut TokenStream) {
175      let prefix = quote! { ::tauri::utils::acl::Value };
176
177      tokens.append_all(match self {
178        Value::Null => quote! { #prefix::Null },
179        Value::Bool(bool) => quote! { #prefix::Bool(#bool) },
180        Value::Number(number) => quote! { #prefix::Number(#number) },
181        Value::String(str) => {
182          let s = str_lit(str);
183          quote! { #prefix::String(#s) }
184        }
185        Value::List(vec) => {
186          let items = vec_lit(vec, identity);
187          quote! { #prefix::List(#items) }
188        }
189        Value::Map(map) => {
190          let map = map_lit(
191            quote! { ::std::collections::BTreeMap },
192            map,
193            str_lit,
194            identity,
195          );
196          quote! { #prefix::Map(#map) }
197        }
198      });
199    }
200  }
201}