Skip to content

Commit d635326

Browse files
committed
feat: avoid parsing source code multiple times when patching imports
1 parent bbcaab5 commit d635326

2 files changed

Lines changed: 58 additions & 25 deletions

File tree

go-runner/src/builder/patcher.rs

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Patches the imports to use codspeed rather than the official "testing" package.
22
33
use crate::prelude::*;
4+
use itertools::Itertools;
45
use rayon::prelude::*;
56
use std::fs;
67
use std::path::Path;
@@ -71,19 +72,22 @@ pub fn patch_imports<P: AsRef<Path>>(folder: P) -> anyhow::Result<()> {
7172

7273
/// Internal function to apply import patterns to Go source code
7374
pub fn patch_imports_for_source(source: &str) -> String {
74-
let replace_import = |mut source: String, import_path: &str, replacement: &str| -> String {
75+
let mut source = source.to_string();
76+
77+
// If we can't parse the source, skip this replacement
78+
// This can happen with template files or malformed Go code
79+
let parsed = match gosyn::parse_source(&source) {
80+
Ok(p) => p,
81+
Err(_) => return source,
82+
};
83+
84+
let mut replacements = vec![];
85+
let mut find_replace_range = |import_path: &str, replacement: &str| {
7586
// Optimization: check if the import path exists in the source before parsing
7687
if !source.contains(import_path) {
77-
return source;
88+
return;
7889
}
7990

80-
// If we can't parse the source, skip this replacement
81-
// This can happen with template files or malformed Go code
82-
let parsed = match gosyn::parse_source(&source) {
83-
Ok(p) => p,
84-
Err(_) => return source,
85-
};
86-
8791
if let Some(import) = parsed
8892
.imports
8993
.iter()
@@ -92,39 +96,43 @@ pub fn patch_imports_for_source(source: &str) -> String {
9296
let start_pos = import.path.pos;
9397
let end_pos = start_pos + import.path.value.len();
9498

95-
source.replace_range(start_pos..end_pos, replacement);
99+
replacements.push((start_pos..end_pos, replacement.to_string()));
96100
}
97-
98-
source
99101
};
100102

101-
let mut source = replace_import(
102-
source.to_string(),
103-
"testing",
104-
"testing \"github.com/CodSpeedHQ/codspeed-go/testing/testing\"",
105-
);
106-
107103
// Then replace sub-packages like "testing/synctest"
108104
for testing_pkg in &["fstest", "iotest", "quick", "slogtest", "synctest"] {
109-
source = replace_import(
110-
source.to_string(),
105+
find_replace_range(
111106
&format!("testing/{}", testing_pkg),
112107
&format!(
113108
"{testing_pkg} \"github.com/CodSpeedHQ/codspeed-go/testing/testing/{testing_pkg}\""
114109
),
115110
);
116111
}
117112

118-
let source = replace_import(
119-
source,
113+
find_replace_range(
114+
"testing",
115+
"testing \"github.com/CodSpeedHQ/codspeed-go/testing/testing\"",
116+
);
117+
find_replace_range(
120118
"github.com/thejerf/slogassert",
121119
"\"github.com/CodSpeedHQ/codspeed-go/pkg/slogassert\"",
122120
);
123-
replace_import(
124-
source,
121+
find_replace_range(
125122
"github.com/frankban/quicktest",
126123
"\"github.com/CodSpeedHQ/codspeed-go/pkg/quicktest\"",
127-
)
124+
);
125+
126+
// Apply replacements in reverse order to avoid shifting positions
127+
for (range, replacement) in replacements
128+
.into_iter()
129+
.sorted_by_key(|(range, _)| range.start)
130+
.rev()
131+
{
132+
source.replace_range(range, &replacement);
133+
}
134+
135+
source
128136
}
129137

130138
/// Patches imports and package in specific test files
@@ -390,6 +398,17 @@ func BenchmarkExample(b *testing.B) {
390398
func TestExample(t *testing.T) {
391399
s := "package main"
392400
}
401+
"#;
402+
403+
const MANY_TESTING_IMPORTS: &str = r#"package subpackages
404+
import (
405+
"bytes"
406+
"io"
407+
"testing"
408+
"testing/fstest"
409+
"testing/iotest"
410+
"testing/synctest"
411+
)
393412
"#;
394413

395414
#[rstest]
@@ -408,6 +427,7 @@ func TestExample(t *testing.T) {
408427
MULTILINE_IMPORT_WITH_TESTING_STRING
409428
)]
410429
#[case("package_main", PACKAGE_MAIN)]
430+
#[case("many_testing_imports", MANY_TESTING_IMPORTS)]
411431
fn test_patch_go_source(#[case] test_name: &str, #[case] source: &str) {
412432
let result = patch_imports_for_source(source);
413433
let result = patch_package_for_source(result).unwrap();
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
source: go-runner/src/builder/patcher.rs
3+
expression: result
4+
---
5+
package subpackages
6+
import (
7+
"bytes"
8+
"io"
9+
testing "github.com/CodSpeedHQ/codspeed-go/testing/testing"
10+
fstest "github.com/CodSpeedHQ/codspeed-go/testing/testing/fstest"
11+
iotest "github.com/CodSpeedHQ/codspeed-go/testing/testing/iotest"
12+
synctest "github.com/CodSpeedHQ/codspeed-go/testing/testing/synctest"
13+
)

0 commit comments

Comments
 (0)