Skip to content

Commit b21b86e

Browse files
authored
feat: support for loading files relative to recipe path (#1607)
1 parent ea03f64 commit b21b86e

File tree

9 files changed

+539
-24
lines changed

9 files changed

+539
-24
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,10 @@ criterion = { version = "0.5.1", features = ["html_reports"] }
195195
name = "line_breaks"
196196
harness = false
197197

198+
[[bench]]
199+
name = "parse_file"
200+
harness = false
201+
198202
[patch.crates-io]
199203
# rattler = { git = "https://github.com/wolfv/rattler", branch = "pub-schema" }
200204
# rattler_sandbox = { git = "https://github.com/wolfv/rattler", branch = "pub-schema" }

benches/parse_file.rs

Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
use anyhow::Result;
2+
use criterion::{Criterion, Throughput, black_box, criterion_group, criterion_main};
3+
use fs_err as fs;
4+
use memmap2::Mmap;
5+
use minijinja::Value as MiniJinjaValue;
6+
use serde_json::Value as JsonValue;
7+
use serde_yaml::Value as YamlValue;
8+
use std::io::BufReader;
9+
use std::path::Path;
10+
11+
// Parse YAML from reader
12+
fn parse_yaml_from_reader(file_path: &Path) -> Result<YamlValue> {
13+
let file = fs::File::open(file_path)?;
14+
let reader = BufReader::new(file);
15+
Ok(serde_yaml::from_reader(reader)?)
16+
}
17+
18+
// Parse YAML from string
19+
fn parse_yaml_from_string(file_path: &Path) -> Result<YamlValue> {
20+
let content = fs::read_to_string(file_path)?;
21+
Ok(serde_yaml::from_str(&content)?)
22+
}
23+
24+
// Parse JSON from reader
25+
fn parse_json_from_reader(file_path: &Path) -> Result<JsonValue> {
26+
let file = fs::File::open(file_path)?;
27+
let reader = BufReader::new(file);
28+
Ok(serde_json::from_reader(reader)?)
29+
}
30+
31+
// Parse JSON from string
32+
fn parse_json_from_string(file_path: &Path) -> Result<JsonValue> {
33+
let content = fs::read_to_string(file_path)?;
34+
Ok(serde_json::from_str(&content)?)
35+
}
36+
37+
// Helper function to determine the file format based on the file extension
38+
fn get_file_format(file_path: &Path) -> &'static str {
39+
file_path
40+
.extension()
41+
.and_then(|ext| ext.to_str())
42+
.map(|ext| match ext.to_lowercase().as_str() {
43+
"yaml" | "yml" => "yaml",
44+
"json" => "json",
45+
_ => "unknown",
46+
})
47+
.unwrap_or("unknown")
48+
}
49+
50+
// Read and parse the file based on its format
51+
fn read_and_parse_file(file_path: &Path) -> Result<MiniJinjaValue> {
52+
let file = fs::File::open(file_path)?;
53+
let reader = BufReader::new(file);
54+
55+
match get_file_format(file_path) {
56+
"yaml" => Ok(serde_yaml::from_reader(reader)?),
57+
"json" => Ok(serde_json::from_reader(reader)?),
58+
_ => {
59+
let content = fs::read_to_string(file_path)?;
60+
Ok(MiniJinjaValue::from(content))
61+
}
62+
}
63+
}
64+
65+
// Parse YAML to MiniJinja Value from reader
66+
fn parse_yaml_to_minijinja_from_reader(file_path: &Path) -> Result<MiniJinjaValue> {
67+
read_and_parse_file(file_path)
68+
}
69+
70+
// Parse YAML to MiniJinja Value from string
71+
fn parse_yaml_to_minijinja_from_string(file_path: &Path) -> Result<MiniJinjaValue> {
72+
read_and_parse_file(file_path)
73+
}
74+
75+
// Parse JSON to MiniJinja Value from reader
76+
fn parse_json_to_minijinja_from_reader(file_path: &Path) -> Result<MiniJinjaValue> {
77+
read_and_parse_file(file_path)
78+
}
79+
80+
// Parse JSON to MiniJinja Value from string
81+
fn parse_json_to_minijinja_from_string(file_path: &Path) -> Result<MiniJinjaValue> {
82+
read_and_parse_file(file_path)
83+
}
84+
85+
// Parse YAML using memory mapping
86+
fn parse_yaml_from_mmap(file_path: &Path) -> Result<YamlValue> {
87+
let file = fs::File::open(file_path)?;
88+
let mmap = unsafe { Mmap::map(&file)? };
89+
Ok(serde_yaml::from_slice(&mmap)?)
90+
}
91+
92+
// Parse JSON using memory mapping
93+
fn parse_json_from_mmap(file_path: &Path) -> Result<JsonValue> {
94+
let file = fs::File::open(file_path)?;
95+
let mmap = unsafe { Mmap::map(&file)? };
96+
Ok(serde_json::from_slice(&mmap)?)
97+
}
98+
99+
// Parse YAML to MiniJinja Value using memory mapping
100+
fn parse_yaml_to_minijinja_from_mmap(file_path: &Path) -> Result<MiniJinjaValue> {
101+
let file = fs::File::open(file_path)?;
102+
let mmap = unsafe { Mmap::map(&file)? };
103+
Ok(serde_yaml::from_slice(&mmap)?)
104+
}
105+
106+
// Parse JSON to MiniJinja Value using memory mapping
107+
fn parse_json_to_minijinja_from_mmap(file_path: &Path) -> Result<MiniJinjaValue> {
108+
let file = fs::File::open(file_path)?;
109+
let mmap = unsafe { Mmap::map(&file)? };
110+
Ok(serde_json::from_slice(&mmap)?)
111+
}
112+
113+
fn create_test_files() -> (tempfile::TempDir, std::path::PathBuf, std::path::PathBuf) {
114+
let temp_dir = tempfile::tempdir().unwrap();
115+
116+
// Create a test YAML file
117+
let yaml_path = temp_dir.path().join("test.yaml");
118+
let yaml_content = r#"
119+
name: test-package
120+
version: 1.0.0
121+
description: A test package for benchmarking
122+
dependencies:
123+
- dep1 >=1.0.0
124+
- dep2 >=2.0.0
125+
- dep3 >=3.0.0
126+
build:
127+
number: 0
128+
script:
129+
- echo "Building test package"
130+
- make install
131+
test:
132+
commands:
133+
- test -f $PREFIX/bin/test-package
134+
about:
135+
home: https://example.com
136+
license: MIT
137+
summary: Test package for benchmarking
138+
"#;
139+
fs::write(&yaml_path, yaml_content).unwrap();
140+
141+
// Create a test JSON file
142+
let json_path = temp_dir.path().join("test.json");
143+
let json_content = r#"
144+
{
145+
"name": "test-package",
146+
"version": "1.0.0",
147+
"description": "A test package for benchmarking",
148+
"dependencies": [
149+
"dep1 >=1.0.0",
150+
"dep2 >=2.0.0",
151+
"dep3 >=3.0.0"
152+
],
153+
"build": {
154+
"number": 0,
155+
"script": [
156+
"echo \"Building test package\"",
157+
"make install"
158+
]
159+
},
160+
"test": {
161+
"commands": [
162+
"test -f $PREFIX/bin/test-package"
163+
]
164+
},
165+
"about": {
166+
"home": "https://example.com",
167+
"license": "MIT",
168+
"summary": "Test package for benchmarking"
169+
}
170+
}
171+
"#;
172+
fs::write(&json_path, json_content).unwrap();
173+
174+
(temp_dir, yaml_path, json_path)
175+
}
176+
177+
fn benchmark_yaml_parsing(c: &mut Criterion) {
178+
let (temp_dir, yaml_path, _) = create_test_files();
179+
let mut group = c.benchmark_group("yaml_parsing");
180+
group.sample_size(5000);
181+
182+
let file_size = fs::metadata(&yaml_path).unwrap().len();
183+
group.throughput(Throughput::Bytes(file_size));
184+
185+
group.bench_function("from_reader", |b| {
186+
b.iter(|| {
187+
black_box(parse_yaml_from_reader(&yaml_path).unwrap());
188+
});
189+
});
190+
191+
group.bench_function("from_string", |b| {
192+
b.iter(|| {
193+
black_box(parse_yaml_from_string(&yaml_path).unwrap());
194+
});
195+
});
196+
197+
group.bench_function("from_mmap", |b| {
198+
b.iter(|| {
199+
black_box(parse_yaml_from_mmap(&yaml_path).unwrap());
200+
});
201+
});
202+
203+
group.finish();
204+
drop(temp_dir);
205+
}
206+
207+
fn benchmark_json_parsing(c: &mut Criterion) {
208+
let (temp_dir, _, json_path) = create_test_files();
209+
let mut group = c.benchmark_group("json_parsing");
210+
group.sample_size(5000);
211+
212+
let file_size = fs::metadata(&json_path).unwrap().len();
213+
group.throughput(Throughput::Bytes(file_size));
214+
215+
group.bench_function("from_reader", |b| {
216+
b.iter(|| {
217+
black_box(parse_json_from_reader(&json_path).unwrap());
218+
});
219+
});
220+
221+
group.bench_function("from_string", |b| {
222+
b.iter(|| {
223+
black_box(parse_json_from_string(&json_path).unwrap());
224+
});
225+
});
226+
227+
group.bench_function("from_mmap", |b| {
228+
b.iter(|| {
229+
black_box(parse_json_from_mmap(&json_path).unwrap());
230+
});
231+
});
232+
233+
group.finish();
234+
drop(temp_dir);
235+
}
236+
237+
fn benchmark_yaml_to_minijinja_parsing(c: &mut Criterion) {
238+
let (temp_dir, yaml_path, _) = create_test_files();
239+
let mut group = c.benchmark_group("yaml_to_minijinja_parsing");
240+
group.sample_size(5000);
241+
242+
let file_size = fs::metadata(&yaml_path).unwrap().len();
243+
group.throughput(Throughput::Bytes(file_size));
244+
245+
group.bench_function("from_reader", |b| {
246+
b.iter(|| {
247+
black_box(parse_yaml_to_minijinja_from_reader(&yaml_path).unwrap());
248+
});
249+
});
250+
251+
group.bench_function("from_string", |b| {
252+
b.iter(|| {
253+
black_box(parse_yaml_to_minijinja_from_string(&yaml_path).unwrap());
254+
});
255+
});
256+
257+
group.bench_function("from_mmap", |b| {
258+
b.iter(|| {
259+
black_box(parse_yaml_to_minijinja_from_mmap(&yaml_path).unwrap());
260+
});
261+
});
262+
263+
group.finish();
264+
drop(temp_dir);
265+
}
266+
267+
fn benchmark_json_to_minijinja_parsing(c: &mut Criterion) {
268+
let (temp_dir, _, json_path) = create_test_files();
269+
let mut group = c.benchmark_group("json_to_minijinja_parsing");
270+
group.sample_size(5000);
271+
272+
let file_size = fs::metadata(&json_path).unwrap().len();
273+
group.throughput(Throughput::Bytes(file_size));
274+
275+
group.bench_function("from_reader", |b| {
276+
b.iter(|| {
277+
black_box(parse_json_to_minijinja_from_reader(&json_path).unwrap());
278+
});
279+
});
280+
281+
group.bench_function("from_string", |b| {
282+
b.iter(|| {
283+
black_box(parse_json_to_minijinja_from_string(&json_path).unwrap());
284+
});
285+
});
286+
287+
group.bench_function("from_mmap", |b| {
288+
b.iter(|| {
289+
black_box(parse_json_to_minijinja_from_mmap(&json_path).unwrap());
290+
});
291+
});
292+
293+
group.finish();
294+
drop(temp_dir);
295+
}
296+
297+
criterion_group!(
298+
benches,
299+
benchmark_yaml_parsing,
300+
benchmark_json_parsing,
301+
benchmark_yaml_to_minijinja_parsing,
302+
benchmark_json_to_minijinja_parsing
303+
);
304+
criterion_main!(benches);

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ pub async fn get_build_output(
209209
experimental: build_data.common.experimental,
210210
// allow undefined while finding the variants
211211
allow_undefined: true,
212+
recipe_path: Some(recipe_path.to_path_buf()),
212213
};
213214

214215
let span = tracing::info_span!("Finding outputs from recipe");

src/metadata.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,7 @@ impl BuildConfiguration {
395395
hash: Some(self.hash.clone()),
396396
experimental: false,
397397
allow_undefined: false,
398+
recipe_path: None,
398399
}
399400
}
400401
}

0 commit comments

Comments
 (0)