ink_ir/ir/item_impl/impl_item.rs
1// Copyright (C) Use Ink (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use super::{
16 Constructor,
17 Message,
18};
19use crate::{
20 error::ExtError as _,
21 ir,
22 ir::attrs::Attrs as _,
23};
24use syn::spanned::Spanned as _;
25
26/// An item within an ink! implementation block.
27///
28/// Can be either
29/// - an ink! [`ir::Constructor`](`crate::ir::Constructor`)
30/// - an ink! [`ir::Message`](`crate::ir::Message`)
31/// - or any other non-ink! item.
32///
33/// # Note
34///
35/// Based on [`syn::ImplItem`] with special variants for ink! `impl` items.
36#[derive(Debug, PartialEq, Eq)]
37#[allow(clippy::large_enum_variant)]
38pub enum ImplItem {
39 /// A `#[ink(constructor)]` marked inherent function.
40 Constructor(Constructor),
41 /// A `#[ink(message)]` marked method.
42 Message(Message),
43 /// Any other implementation block item.
44 Other(syn::ImplItem),
45}
46
47impl quote::ToTokens for ImplItem {
48 /// We mainly implement this trait for this ink! type to have a derived
49 /// [`Spanned`](`syn::spanned::Spanned`) implementation for it.
50 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
51 match self {
52 Self::Constructor(constructor) => constructor.to_tokens(tokens),
53 Self::Message(message) => message.to_tokens(tokens),
54 Self::Other(other) => other.to_tokens(tokens),
55 }
56 }
57}
58
59impl TryFrom<syn::ImplItem> for ImplItem {
60 type Error = syn::Error;
61
62 fn try_from(impl_item: syn::ImplItem) -> Result<Self, Self::Error> {
63 match impl_item {
64 syn::ImplItem::Fn(fn_item) => {
65 if !ir::contains_ink_attributes(&fn_item.attrs) {
66 return Ok(Self::Other(fn_item.into()))
67 }
68 let attr = ir::first_ink_attribute(&fn_item.attrs)?
69 .expect("missing expected ink! attribute for fn");
70 match attr.first().kind() {
71 ir::AttributeArg::Message => {
72 <Message as TryFrom<_>>::try_from(fn_item)
73 .map(Self::Message)
74 }
75 ir::AttributeArg::Constructor => {
76 <Constructor as TryFrom<_>>::try_from(fn_item)
77 .map(Self::Constructor)
78 }
79 _ => Err(format_err_spanned!(
80 fn_item,
81 "encountered invalid ink! attribute at this point, expected either \
82 #[ink(message)] or #[ink(constructor) attributes"
83 )),
84 }
85 }
86 other_item => {
87 // This is an error if the `impl` item contains any unexpected
88 // ink! attributes. Otherwise it is a normal Rust item.
89 if ir::contains_ink_attributes(other_item.attrs()) {
90 let (ink_attrs, _) =
91 ir::partition_attributes(other_item.attrs().iter().cloned())?;
92 assert!(!ink_attrs.is_empty());
93 fn into_err(attr: &ir::InkAttribute) -> syn::Error {
94 format_err!(attr.span(), "encountered unexpected ink! attribute",)
95 }
96 return Err(ink_attrs[1..]
97 .iter()
98 .map(into_err)
99 .fold(into_err(&ink_attrs[0]), |fst, snd| fst.into_combine(snd)))
100 }
101 Ok(Self::Other(other_item))
102 }
103 }
104 }
105}
106
107impl ImplItem {
108 /// Returns `true` if the `impl` block item is an ink! message.
109 pub fn is_message(&self) -> bool {
110 self.filter_map_message().is_some()
111 }
112
113 /// Returns `Some` if `self` is an ink! message.
114 ///
115 /// Otherwise, returns `None`.
116 pub fn filter_map_message(&self) -> Option<&Message> {
117 match self {
118 ImplItem::Message(message) => Some(message),
119 _ => None,
120 }
121 }
122
123 /// Returns `true` if the `impl` block item is an ink! message.
124 pub fn is_constructor(&self) -> bool {
125 self.filter_map_constructor().is_some()
126 }
127
128 /// Returns `Some` if `self` is an ink! constructor.
129 ///
130 /// Otherwise, returns `None`.
131 pub fn filter_map_constructor(&self) -> Option<&Constructor> {
132 match self {
133 ImplItem::Constructor(constructor) => Some(constructor),
134 _ => None,
135 }
136 }
137
138 /// Returns `true` if the `impl` block item is a non ink! specific item.
139 pub fn is_other_item(&self) -> bool {
140 self.filter_map_other_item().is_some()
141 }
142
143 /// Returns `Some` if `self` is a not an ink! specific item.
144 ///
145 /// Otherwise, returns `None`.
146 pub fn filter_map_other_item(&self) -> Option<&syn::ImplItem> {
147 match self {
148 ImplItem::Other(rust_item) => Some(rust_item),
149 _ => None,
150 }
151 }
152}