foundry_compilers_core/utils/
re.rs1use regex::{Match, Regex};
2use std::sync::LazyLock as Lazy;
3
4pub static RE_SOL_IMPORT: Lazy<Regex> = Lazy::new(|| {
8 Regex::new(r#"import\s+(?:(?:"(?P<p1>.*)"|'(?P<p2>.*)')(?:\s+as\s+\w+)?|(?:(?:\w+(?:\s+as\s+\w+)?|\*\s+as\s+\w+|\{\s*(?:\w+(?:\s+as\s+\w+)?(?:\s*,\s*)?)+\s*\})\s+from\s+(?:"(?P<p3>.*)"|'(?P<p4>.*)')))\s*;"#).unwrap()
9});
10
11pub static RE_SOL_IMPORT_ALIAS: Lazy<Regex> =
13 Lazy::new(|| Regex::new(r#"(?:(?P<target>\w+)|\*|'|")\s+as\s+(?P<alias>\w+)"#).unwrap());
14
15pub static RE_SOL_PRAGMA_VERSION: Lazy<Regex> =
20 Lazy::new(|| Regex::new(r"pragma\s+solidity\s+(?P<version>.+?);").unwrap());
21
22pub static RE_SOL_SDPX_LICENSE_IDENTIFIER: Lazy<Regex> =
25 Lazy::new(|| Regex::new(r"///?\s*SPDX-License-Identifier:\s*(?P<license>.+)").unwrap());
26
27pub static RE_THREE_OR_MORE_NEWLINES: Lazy<Regex> = Lazy::new(|| Regex::new("\n{3,}").unwrap());
29
30pub static RE_TWO_OR_MORE_SPACES: Lazy<Regex> = Lazy::new(|| Regex::new(" {2,}").unwrap());
32
33pub static RE_VYPER_VERSION: Lazy<Regex> =
35 Lazy::new(|| Regex::new(r"#(?:pragma version|@version)\s+(?P<version>.+)").unwrap());
36
37pub static RE_CONTRACT_NAMES: Lazy<Regex> = Lazy::new(|| {
39 Regex::new(r"\b(?:contract|library|abstract\s+contract|interface)\s+([\w$]+)").unwrap()
40});
41
42pub fn create_contract_or_lib_name_regex(name: &str) -> Regex {
44 Regex::new(&format!(r#"(?:using\s+(?P<n1>{name})\s+|is\s+(?:\w+\s*,\s*)*(?P<n2>{name})(?:\s*,\s*\w+)*|(?:(?P<ignore>(?:function|error|as)\s+|\n[^\n]*(?:"([^"\n]|\\")*|'([^'\n]|\\')*))|\W+)(?P<n3>{name})(?:\.|\(| ))"#)).unwrap()
45}
46
47pub fn find_import_paths(contract: &str) -> impl Iterator<Item = Match<'_>> {
52 RE_SOL_IMPORT.captures_iter(contract).filter_map(|cap| {
53 cap.name("p1")
54 .or_else(|| cap.name("p2"))
55 .or_else(|| cap.name("p3"))
56 .or_else(|| cap.name("p4"))
57 })
58}
59
60pub fn find_version_pragma(contract: &str) -> Option<Match<'_>> {
63 RE_SOL_PRAGMA_VERSION.captures(contract)?.name("version")
64}
65
66pub fn capture_outer_and_inner<'a>(
74 content: &'a str,
75 regex: ®ex::Regex,
76 names: &[&str],
77) -> Vec<(regex::Match<'a>, regex::Match<'a>)> {
78 regex
79 .captures_iter(content)
80 .filter_map(|cap| {
81 let cap_match = names.iter().find_map(|name| cap.name(name));
82 cap_match.and_then(|m| cap.get(0).map(|outer| (outer.to_owned(), m)))
83 })
84 .collect()
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[test]
92 fn can_find_import_paths() {
93 let s = r#"//SPDX-License-Identifier: Unlicense
94pragma solidity ^0.8.0;
95import "hardhat/console.sol";
96import "../contract/Contract.sol";
97import { T } from "../Test.sol";
98import { T } from '../Test2.sol';
99"#;
100 assert_eq!(
101 vec!["hardhat/console.sol", "../contract/Contract.sol", "../Test.sol", "../Test2.sol"],
102 find_import_paths(s).map(|m| m.as_str()).collect::<Vec<&str>>()
103 );
104 }
105
106 #[test]
107 fn can_find_version() {
108 let s = r"//SPDX-License-Identifier: Unlicense
109pragma solidity ^0.8.0;
110";
111 assert_eq!(Some("^0.8.0"), find_version_pragma(s).map(|s| s.as_str()));
112 }
113
114 #[test]
115 fn can_parse_curly_bracket_imports() {
116 let s =
117 r#"import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";"#;
118 let imports: Vec<_> = find_import_paths(s).map(|m| m.as_str()).collect();
119 assert_eq!(imports, vec!["@openzeppelin/contracts/utils/ReentrancyGuard.sol"])
120 }
121
122 #[test]
123 fn can_find_single_quote_imports() {
124 let content = r"
125// SPDX-License-Identifier: MIT
126pragma solidity 0.8.6;
127
128import '@openzeppelin/contracts/access/Ownable.sol';
129import '@openzeppelin/contracts/utils/Address.sol';
130
131import './../interfaces/IJBDirectory.sol';
132import './../libraries/JBTokens.sol';
133 ";
134 let imports: Vec<_> = find_import_paths(content).map(|m| m.as_str()).collect();
135
136 assert_eq!(
137 imports,
138 vec![
139 "@openzeppelin/contracts/access/Ownable.sol",
140 "@openzeppelin/contracts/utils/Address.sol",
141 "./../interfaces/IJBDirectory.sol",
142 "./../libraries/JBTokens.sol",
143 ]
144 );
145 }
146}