tokens.rs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. // Copyright 2019-2023 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. //! Utilities to implement [`ToTokens`] for a type.
  5. use std::path::Path;
  6. use proc_macro2::TokenStream;
  7. use quote::{quote, ToTokens};
  8. use serde_json::Value as JsonValue;
  9. use url::Url;
  10. /// Write a `TokenStream` of the `$struct`'s fields to the `$tokens`.
  11. ///
  12. /// All fields must represent a binding of the same name that implements `ToTokens`.
  13. #[macro_export]
  14. macro_rules! literal_struct {
  15. ($tokens:ident, $struct:path, $($field:ident),+) => {
  16. $tokens.append_all(quote! {
  17. $struct {
  18. $($field: #$field),+
  19. }
  20. })
  21. };
  22. }
  23. /// Create a `String` constructor `TokenStream`.
  24. ///
  25. /// e.g. `"Hello World" -> String::from("Hello World").
  26. /// This takes a `&String` to reduce casting all the `&String` -> `&str` manually.
  27. pub fn str_lit(s: impl AsRef<str>) -> TokenStream {
  28. let s = s.as_ref();
  29. quote! { #s.into() }
  30. }
  31. /// Create an `Option` constructor `TokenStream`.
  32. pub fn opt_lit(item: Option<&impl ToTokens>) -> TokenStream {
  33. match item {
  34. None => quote! { ::core::option::Option::None },
  35. Some(item) => quote! { ::core::option::Option::Some(#item) },
  36. }
  37. }
  38. /// Create an `Option` constructor `TokenStream` over an owned [`ToTokens`] impl type.
  39. pub fn opt_lit_owned(item: Option<impl ToTokens>) -> TokenStream {
  40. match item {
  41. None => quote! { ::core::option::Option::None },
  42. Some(item) => quote! { ::core::option::Option::Some(#item) },
  43. }
  44. }
  45. /// Helper function to combine an `opt_lit` with `str_lit`.
  46. pub fn opt_str_lit(item: Option<impl AsRef<str>>) -> TokenStream {
  47. opt_lit(item.map(str_lit).as_ref())
  48. }
  49. /// Helper function to combine an `opt_lit` with a list of `str_lit`
  50. pub fn opt_vec_lit<Raw, Tokens>(
  51. item: Option<impl IntoIterator<Item = Raw>>,
  52. map: impl Fn(Raw) -> Tokens,
  53. ) -> TokenStream
  54. where
  55. Tokens: ToTokens,
  56. {
  57. opt_lit(item.map(|list| vec_lit(list, map)).as_ref())
  58. }
  59. /// Create a `Vec` constructor, mapping items with a function that spits out `TokenStream`s.
  60. pub fn vec_lit<Raw, Tokens>(
  61. list: impl IntoIterator<Item = Raw>,
  62. map: impl Fn(Raw) -> Tokens,
  63. ) -> TokenStream
  64. where
  65. Tokens: ToTokens,
  66. {
  67. let items = list.into_iter().map(map);
  68. quote! { vec![#(#items),*] }
  69. }
  70. /// Create a `PathBuf` constructor `TokenStream`.
  71. ///
  72. /// e.g. `"Hello World" -> String::from("Hello World").
  73. pub fn path_buf_lit(s: impl AsRef<Path>) -> TokenStream {
  74. let s = s.as_ref().to_string_lossy().into_owned();
  75. quote! { ::std::path::PathBuf::from(#s) }
  76. }
  77. /// Creates a `Url` constructor `TokenStream`.
  78. pub fn url_lit(url: &Url) -> TokenStream {
  79. let url = url.as_str();
  80. quote! { #url.parse().unwrap() }
  81. }
  82. /// Create a map constructor, mapping keys and values with other `TokenStream`s.
  83. ///
  84. /// This function is pretty generic because the types of keys AND values get transformed.
  85. pub fn map_lit<Map, Key, Value, TokenStreamKey, TokenStreamValue, FuncKey, FuncValue>(
  86. map_type: TokenStream,
  87. map: Map,
  88. map_key: FuncKey,
  89. map_value: FuncValue,
  90. ) -> TokenStream
  91. where
  92. <Map as IntoIterator>::IntoIter: ExactSizeIterator,
  93. Map: IntoIterator<Item = (Key, Value)>,
  94. TokenStreamKey: ToTokens,
  95. TokenStreamValue: ToTokens,
  96. FuncKey: Fn(Key) -> TokenStreamKey,
  97. FuncValue: Fn(Value) -> TokenStreamValue,
  98. {
  99. let ident = quote::format_ident!("map");
  100. let map = map.into_iter();
  101. if map.len() > 0 {
  102. let items = map.map(|(key, value)| {
  103. let key = map_key(key);
  104. let value = map_value(value);
  105. quote! { #ident.insert(#key, #value); }
  106. });
  107. quote! {{
  108. let mut #ident = #map_type::new();
  109. #(#items)*
  110. #ident
  111. }}
  112. } else {
  113. quote! { #map_type::new() }
  114. }
  115. }
  116. /// Create a `serde_json::Value` variant `TokenStream` for a number
  117. pub fn json_value_number_lit(num: &serde_json::Number) -> TokenStream {
  118. // See https://docs.rs/serde_json/1/serde_json/struct.Number.html for guarantees
  119. let prefix = quote! { ::serde_json::Value };
  120. if num.is_u64() {
  121. // guaranteed u64
  122. let num = num.as_u64().unwrap();
  123. quote! { #prefix::Number(#num.into()) }
  124. } else if num.is_i64() {
  125. // guaranteed i64
  126. let num = num.as_i64().unwrap();
  127. quote! { #prefix::Number(#num.into()) }
  128. } else if num.is_f64() {
  129. // guaranteed f64
  130. let num = num.as_f64().unwrap();
  131. quote! { #prefix::Number(::serde_json::Number::from_f64(#num).unwrap(/* safe to unwrap, guaranteed f64 */)) }
  132. } else {
  133. // invalid number
  134. quote! { #prefix::Null }
  135. }
  136. }
  137. /// Create a `serde_json::Value` constructor `TokenStream`
  138. pub fn json_value_lit(jv: &JsonValue) -> TokenStream {
  139. let prefix = quote! { ::serde_json::Value };
  140. match jv {
  141. JsonValue::Null => quote! { #prefix::Null },
  142. JsonValue::Bool(bool) => quote! { #prefix::Bool(#bool) },
  143. JsonValue::Number(number) => json_value_number_lit(number),
  144. JsonValue::String(str) => {
  145. let s = str_lit(str);
  146. quote! { #prefix::String(#s) }
  147. }
  148. JsonValue::Array(vec) => {
  149. let items = vec.iter().map(json_value_lit);
  150. quote! { #prefix::Array(vec![#(#items),*]) }
  151. }
  152. JsonValue::Object(map) => {
  153. let map = map_lit(quote! { ::serde_json::Map }, map, str_lit, json_value_lit);
  154. quote! { #prefix::Object(#map) }
  155. }
  156. }
  157. }