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).map(Self::Message)
73                    }
74                    ir::AttributeArg::Constructor => {
75                        <Constructor as TryFrom<_>>::try_from(fn_item)
76                            .map(Self::Constructor)
77                    }
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            }
87            other_item => {
88                // This is an error if the `impl` item contains any unexpected
89                // ink! attributes. Otherwise it is a normal Rust item.
90                if ir::contains_ink_attributes(other_item.attrs()) {
91                    let (ink_attrs, _) =
92                        ir::partition_attributes(other_item.attrs().iter().cloned())?;
93                    assert!(!ink_attrs.is_empty());
94                    fn into_err(attr: &ir::InkAttribute) -> syn::Error {
95                        format_err!(attr.span(), "encountered unexpected ink! attribute",)
96                    }
97                    return Err(ink_attrs[1..]
98                        .iter()
99                        .map(into_err)
100                        .fold(into_err(&ink_attrs[0]), |fst, snd| fst.into_combine(snd)))
101                }
102                Ok(Self::Other(other_item))
103            }
104        }
105    }
106}
107
108impl ImplItem {
109    /// Returns `true` if the `impl` block item is an ink! message.
110    pub fn is_message(&self) -> bool {
111        self.filter_map_message().is_some()
112    }
113
114    /// Returns `Some` if `self` is an ink! message.
115    ///
116    /// Otherwise, returns `None`.
117    pub fn filter_map_message(&self) -> Option<&Message> {
118        match self {
119            ImplItem::Message(message) => Some(message),
120            _ => None,
121        }
122    }
123
124    /// Returns `true` if the `impl` block item is an ink! message.
125    pub fn is_constructor(&self) -> bool {
126        self.filter_map_constructor().is_some()
127    }
128
129    /// Returns `Some` if `self` is an ink! constructor.
130    ///
131    /// Otherwise, returns `None`.
132    pub fn filter_map_constructor(&self) -> Option<&Constructor> {
133        match self {
134            ImplItem::Constructor(constructor) => Some(constructor),
135            _ => None,
136        }
137    }
138
139    /// Returns `true` if the `impl` block item is a non ink! specific item.
140    pub fn is_other_item(&self) -> bool {
141        self.filter_map_other_item().is_some()
142    }
143
144    /// Returns `Some` if `self` is a not an ink! specific item.
145    ///
146    /// Otherwise, returns `None`.
147    pub fn filter_map_other_item(&self) -> Option<&syn::ImplItem> {
148        match self {
149            ImplItem::Other(rust_item) => Some(rust_item),
150            _ => None,
151        }
152    }
153}