referrerpolicy=no-referrer-when-downgrade

frame_support_procedural/pallet/parse/
view_functions.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governsing permissions and
16// limitations under the License.
17
18use frame_support_procedural_tools::get_doc_literals;
19use inflector::Inflector;
20use syn::spanned::Spanned;
21
22/// Parsed representation of an impl block annotated with `pallet::view_functions`.
23pub struct ViewFunctionsImplDef {
24	/// The where_clause used.
25	pub where_clause: Option<syn::WhereClause>,
26	/// The span of the pallet::view_functions attribute.
27	pub attr_span: proc_macro2::Span,
28	/// The view function definitions.
29	pub view_functions: Vec<ViewFunctionDef>,
30}
31
32impl ViewFunctionsImplDef {
33	pub fn try_from(attr_span: proc_macro2::Span, item: &mut syn::Item) -> syn::Result<Self> {
34		let syn::Item::Impl(item_impl) = item else {
35			return Err(syn::Error::new(
36				item.span(),
37				"Invalid pallet::view_functions, expected item impl",
38			))
39		};
40		let mut view_functions = Vec::new();
41		for item in &mut item_impl.items {
42			if let syn::ImplItem::Fn(method) = item {
43				if !matches!(method.vis, syn::Visibility::Public(_)) {
44					let msg = "Invalid pallet::view_functions, view function must be public: \
45						`pub fn`";
46
47					let span = match method.vis {
48						syn::Visibility::Inherited => method.sig.span(),
49						_ => method.vis.span(),
50					};
51
52					return Err(syn::Error::new(span, msg))
53				}
54
55				let view_fn_def = ViewFunctionDef::try_from(method.clone())?;
56				view_functions.push(view_fn_def)
57			} else {
58				return Err(syn::Error::new(
59					item.span(),
60					"Invalid pallet::view_functions, expected a function",
61				))
62			}
63		}
64		Ok(Self {
65			view_functions,
66			attr_span,
67			where_clause: item_impl.generics.where_clause.clone(),
68		})
69	}
70}
71
72/// Parsed representation of a view function definition.
73#[derive(Clone)]
74pub struct ViewFunctionDef {
75	pub name: syn::Ident,
76	pub docs: Vec<syn::Expr>,
77	pub args: Vec<syn::FnArg>,
78	pub return_type: syn::Type,
79}
80
81impl TryFrom<syn::ImplItemFn> for ViewFunctionDef {
82	type Error = syn::Error;
83	fn try_from(method: syn::ImplItemFn) -> Result<Self, Self::Error> {
84		let syn::ReturnType::Type(_, type_) = method.sig.output else {
85			return Err(syn::Error::new(method.sig.span(), "view functions must return a value"))
86		};
87
88		Ok(Self {
89			name: method.sig.ident.clone(),
90			docs: get_doc_literals(&method.attrs),
91			args: method.sig.inputs.iter().cloned().collect::<Vec<_>>(),
92			return_type: *type_.clone(),
93		})
94	}
95}
96
97impl ViewFunctionDef {
98	pub fn view_function_struct_ident(&self) -> syn::Ident {
99		syn::Ident::new(
100			&format!("{}ViewFunction", self.name.to_string().to_pascal_case()),
101			self.name.span(),
102		)
103	}
104
105	pub fn view_function_id_suffix_bytes(&self) -> Result<[u8; 16], syn::Error> {
106		let mut output = [0u8; 16];
107
108		// concatenate the signature string
109		let arg_types = self
110			.args_names_types()?
111			.1
112			.iter()
113			.map(|ty| quote::quote!(#ty).to_string().replace(" ", ""))
114			.collect::<Vec<_>>()
115			.join(",");
116		let return_type = &self.return_type;
117		let return_type = quote::quote!(#return_type).to_string().replace(" ", "");
118		let view_fn_signature = format!(
119			"{view_function_name}({arg_types}) -> {return_type}",
120			view_function_name = &self.name,
121		);
122
123		// hash the signature string
124		let hash = sp_crypto_hashing::twox_128(view_fn_signature.as_bytes());
125		output.copy_from_slice(&hash[..]);
126		Ok(output)
127	}
128
129	pub fn args_names_types(&self) -> Result<(Vec<syn::Ident>, Vec<syn::Type>), syn::Error> {
130		Ok(self
131			.args
132			.iter()
133			.map(|arg| {
134				let syn::FnArg::Typed(pat_type) = arg else {
135					return Err(syn::Error::new(
136						arg.span(),
137						"Unsupported argument in view function",
138					));
139				};
140				let syn::Pat::Ident(ident) = &*pat_type.pat else {
141					return Err(syn::Error::new(
142						pat_type.pat.span(),
143						"Unsupported pattern in view function argument",
144					));
145				};
146				Ok((ident.ident.clone(), *pat_type.ty.clone()))
147			})
148			.collect::<Result<Vec<(syn::Ident, syn::Type)>, syn::Error>>()?
149			.into_iter()
150			.unzip())
151	}
152}