1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//! ink! source file IR.

use ink_analyzer_macro::FromAST;
use ra_ap_syntax::{AstNode, SourceFile};

use crate::traits::FromAST;
use crate::tree::utils;
use crate::{ChainExtension, Contract, InkE2ETest, InkTest, StorageItem, TraitDefinition};

/// An ink! source file.
#[derive(Debug, Clone, PartialEq, Eq, FromAST)]
pub struct InkFile {
    /// ink! contracts in source file.
    contracts: Vec<Contract>,
    /// ink! trait definitions in source file.
    trait_definitions: Vec<TraitDefinition>,
    /// ink! chain extensions in source file.
    chain_extensions: Vec<ChainExtension>,
    /// ink! storage items in source file.
    storage_items: Vec<StorageItem>,
    /// ink! tests in source file.
    tests: Vec<InkTest>,
    /// ink! e2e tests in source file.
    e2e_tests: Vec<InkE2ETest>,
    /// AST Node for ink! source file.
    ast: SourceFile,
}

impl From<SourceFile> for InkFile {
    fn from(file: SourceFile) -> Self {
        Self {
            contracts: utils::ink_closest_descendants(file.syntax()).collect(),
            trait_definitions: utils::ink_contract_peekable_quasi_closest_descendants(
                file.syntax(),
            )
            .collect(),
            chain_extensions: utils::ink_contract_peekable_quasi_closest_descendants(file.syntax())
                .collect(),
            storage_items: utils::ink_contract_peekable_quasi_closest_descendants(file.syntax())
                .collect(),
            tests: utils::ink_closest_descendants(file.syntax()).collect(),
            e2e_tests: utils::ink_closest_descendants(file.syntax()).collect(),
            ast: file,
        }
    }
}

impl InkFile {
    /// Parses ink! IR from source code.
    pub fn parse(code: &str) -> Self {
        Self::from(SourceFile::parse(code).tree())
    }

    /// Returns ink! contracts in source file.
    pub fn contracts(&self) -> &[Contract] {
        &self.contracts
    }

    /// Returns ink! trait definitions in source file.
    pub fn trait_definitions(&self) -> &[TraitDefinition] {
        &self.trait_definitions
    }

    /// Returns ink! chain extensions in source file.
    pub fn chain_extensions(&self) -> &[ChainExtension] {
        &self.chain_extensions
    }

    /// Returns ink! storage items in source file.
    pub fn storage_items(&self) -> &[StorageItem] {
        &self.storage_items
    }

    /// Returns ink! tests in source file.
    pub fn tests(&self) -> &[InkTest] {
        &self.tests
    }

    /// Returns ink! e2e tests in source file.
    pub fn e2e_tests(&self) -> &[InkE2ETest] {
        &self.e2e_tests
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use test_utils::quote_as_str;

    #[test]
    fn parse_works() {
        let file = InkFile::parse(quote_as_str! {
            #[ink::contract]
            mod my_contract {
            }

            #[ink::trait_definition]
            pub trait MyTrait {
            }

            #[ink::chain_extension]
            pub trait MyChainExtension {
            }

            #[ink::storage_item]
            struct MyStorageItem {
            }

            #[ink::storage_item]
            struct MyStorageItem2 {
            }

            #[cfg(test)]
            mod tests {
                #[ink::test]
                fn it_works {
                }

                #[ink::test]
                fn it_works2 {
                }
            }
        });

        // 1 contract.
        assert_eq!(file.contracts().len(), 1);

        // 1 trait definition.
        assert_eq!(file.trait_definitions().len(), 1);

        // 1 chain extension.
        assert_eq!(file.chain_extensions().len(), 1);

        // 2 storage items.
        assert_eq!(file.storage_items().len(), 2);

        // 2 tests.
        assert_eq!(file.tests().len(), 2);
    }
}