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}