bench: Add JSON support to `xtask bench`
MODIFY
Cargo.lock
Cargo.lock
@@ -2169,9 +2169,9 @@ dependencies = [
[[package]]
name = "smallvec"
-version = "1.9.0"
+version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
+checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
name = "socket2"
@@ -2954,6 +2954,9 @@ dependencies = [
"rome_js_formatter",
"rome_js_parser",
"rome_js_syntax",
+ "rome_json_formatter",
+ "rome_json_parser",
+ "rome_json_syntax",
"rome_parser",
"tikv-jemallocator",
"timing",

MODIFY
crates/rome_js_parser/src/lib.rs
crates/rome_js_parser/src/lib.rs
@@ -160,4 +160,5 @@ impl SyntaxFeature for JsSyntaxFeature {
}
}
-pub type JsLosslessTreeSink<'source> = LosslessTreeSink<'source, JsLanguage, JsSyntaxFactory>;
+pub(crate) type JsLosslessTreeSink<'source> =
+ LosslessTreeSink<'source, JsLanguage, JsSyntaxFactory>;

MODIFY
crates/rome_js_parser/src/parse.rs
crates/rome_js_parser/src/parse.rs
@@ -116,7 +116,7 @@ impl<T: AstNode<Language = JsLanguage>> Parse<T> {
}
}
-pub fn parse_common(
+fn parse_common(
text: &str,
file_id: FileId,
source_type: SourceType,

MODIFY
xtask/bench/Cargo.toml
xtask/bench/Cargo.toml
@@ -9,6 +9,9 @@ xtask = { path = '../', version = "0.0" }
rome_js_syntax = { path = "../../crates/rome_js_syntax" }
rome_console = { path = "../../crates/rome_console" }
rome_js_parser = { path = "../../crates/rome_js_parser" }
+rome_json_formatter = { path = "../../crates/rome_json_formatter" }
+rome_json_parser = { path = "../../crates/rome_json_parser" }
+rome_json_syntax = { path = "../../crates/rome_json_syntax" }
rome_parser = { path = "../../crates/rome_parser" }
rome_diagnostics = { path = "../../crates/rome_diagnostics" }
rome_formatter = { path = "../../crates/rome_formatter"}
@@ -16,6 +19,7 @@ rome_js_formatter = { path = "../../crates/rome_js_formatter"}
rome_analyze = { path = "../../crates/rome_analyze"}
rome_js_analyze = { path = "../../crates/rome_js_analyze"}
+
pico-args = { version = "0.5.0", features=["eq-separator"] }
timing = "0.2.3"
criterion = "0.3.5"

MODIFY
xtask/bench/src/features/analyzer.rs
xtask/bench/src/features/analyzer.rs
+use crate::language::Analyze;
+use crate::test_case::TestCase;
use crate::BenchmarkSummary;
-use criterion::black_box;
-use rome_analyze::{AnalysisFilter, AnalyzerOptions, ControlFlow, Never, RuleCategories};
-use rome_diagnostics::location::FileId;
-use rome_js_analyze::analyze;
-use rome_js_syntax::JsAnyRoot;
use std::fmt::{Display, Formatter};
use std::time::Duration;
@@ -12,30 +9,17 @@ pub struct AnalyzerMeasurement {
id: String,
analysis: Duration,
}
-pub fn benchmark_analyze_lib(id: &str, root: &JsAnyRoot) -> BenchmarkSummary {
+pub fn benchmark_analyze_lib(case: &TestCase, analyze: &Analyze) -> BenchmarkSummary {
let analyzer_timer = timing::start();
- run_analyzer(root);
+ analyze.analyze();
let analyzer_duration = analyzer_timer.stop();
BenchmarkSummary::Analyzer(AnalyzerMeasurement {
- id: id.to_string(),
+ id: case.filename().to_string(),
analysis: analyzer_duration,
})
}
-pub fn run_analyzer(root: &JsAnyRoot) {
- let filter = AnalysisFilter {
- categories: RuleCategories::SYNTAX | RuleCategories::LINT,
- ..AnalysisFilter::default()
- };
- let options = AnalyzerOptions::default();
- analyze(FileId::zero(), root, filter, &options, |event| {
- black_box(event.diagnostic());
- black_box(event.actions());
- ControlFlow::<Never>::Continue(())
- });
-}
-
impl AnalyzerMeasurement {
fn total(&self) -> Duration {
self.analysis

MODIFY
xtask/bench/src/features/formatter.rs
xtask/bench/src/features/formatter.rs
#[cfg(feature = "dhat-heap")]
-use crate::features::print_diff;
+use crate::features::print_stats;
+use crate::language::FormatNode;
use crate::BenchmarkSummary;
use rome_formatter::Printed;
-use rome_js_formatter::context::JsFormatOptions;
-use rome_js_formatter::format_node;
-use rome_js_syntax::{JsSyntaxNode, SourceType};
use std::fmt::{Display, Formatter};
use std::time::Duration;
@@ -13,13 +11,9 @@ pub struct FormatterMeasurement {
id: String,
formatting: Duration,
}
-pub fn benchmark_format_lib(
- id: &str,
- root: &JsSyntaxNode,
- source_type: SourceType,
-) -> BenchmarkSummary {
+pub fn benchmark_format_lib(id: &str, format_node: &FormatNode) -> BenchmarkSummary {
let formatter_timer = timing::start();
- run_format(root, source_type);
+ criterion::black_box(run_format(format_node));
let formatter_duration = formatter_timer.stop();
BenchmarkSummary::Formatter(FormatterMeasurement {
@@ -28,19 +22,19 @@ pub fn benchmark_format_lib(
})
}
-pub fn run_format(root: &JsSyntaxNode, source_type: SourceType) -> Printed {
+pub fn run_format(format_node: &FormatNode) -> Printed {
#[cfg(feature = "dhat-heap")]
let stats = {
println!("Start");
- dhat::HeapStats::get()
+ print_stats(dhat::HeapStats::get(), None)
};
- let formatted = format_node(JsFormatOptions::new(source_type), root).unwrap();
+ let formatted = format_node.format_node().unwrap();
#[cfg(feature = "dhat-heap")]
let stats = {
println!("Formatted");
- print_diff(stats, dhat::HeapStats::get())
+ print_stats(dhat::HeapStats::get(), Some(stats))
};
let printed = formatted.print();
@@ -49,7 +43,7 @@ pub fn run_format(root: &JsSyntaxNode, source_type: SourceType) -> Printed {
#[cfg(feature = "dhat-heap")]
{
println!("Printed");
- print_diff(stats, dhat::HeapStats::get());
+ print_stats(dhat::HeapStats::get(), Some(stats));
}
printed.expect("Document to be valid")

MODIFY
xtask/bench/src/features/mod.rs
xtask/bench/src/features/mod.rs
@@ -3,7 +3,7 @@ pub mod formatter;
pub mod parser;
#[cfg(feature = "dhat-heap")]
-fn print_diff(before: dhat::HeapStats, current: dhat::HeapStats) -> dhat::HeapStats {
+fn print_stats(current: dhat::HeapStats, before: Option<dhat::HeapStats>) -> dhat::HeapStats {
use humansize::{file_size_opts as options, FileSize};
println!("\tMemory");
@@ -18,13 +18,21 @@ fn print_diff(before: dhat::HeapStats, current: dhat::HeapStats) -> dhat::HeapSt
current.max_bytes.file_size(options::CONVENTIONAL).unwrap()
);
+ if let Some(before) = before {
+ let new_blocks = current.total_blocks - before.total_blocks;
+ let new_bytes = current.total_bytes - before.total_bytes;
+ println!("\t\tNew Blocks: {new_blocks}",);
println!(
- "\t\tTotal Blocks: {}",
- current.total_blocks - before.total_blocks
+ "\t\tNew Bytes: {}",
+ new_bytes.file_size(options::CONVENTIONAL).unwrap()
);
+ }
+
+ println!("\t\tTotal Blocks: {}", current.total_blocks);
println!(
"\t\tTotal Bytes: {}",
- (current.total_bytes - before.total_bytes)
+ current
+ .total_bytes
.file_size(options::CONVENTIONAL)
.unwrap()
);

MODIFY
xtask/bench/src/features/parser.rs
xtask/bench/src/features/parser.rs
#[cfg(feature = "dhat-heap")]
-use crate::features::print_diff;
+use crate::features::print_stats;
+use crate::language::Parse;
+use crate::test_case::TestCase;
use crate::BenchmarkSummary;
use itertools::Itertools;
use rome_diagnostics::console::fmt::Termcolor;
use rome_diagnostics::console::markup;
-use rome_diagnostics::location::FileId;
use rome_diagnostics::termcolor::Buffer;
use rome_diagnostics::DiagnosticExt;
use rome_diagnostics::PrintDiagnostic;
-use rome_js_parser::{parse_common, Parse};
-use rome_js_syntax::{JsAnyRoot, SourceType};
use rome_parser::diagnostic::ParseDiagnostic;
use std::fmt::{Display, Formatter};
-use std::ops::Add;
use std::time::Duration;
#[derive(Debug, Clone)]
-
pub struct ParseMeasurement {
id: String,
code: String,
- parsing: Duration,
- tree_sink: Duration,
+ duration: Duration,
diagnostics: Vec<ParseDiagnostic>,
}
-pub fn benchmark_parse_lib(id: &str, code: &str, source_type: SourceType) -> BenchmarkSummary {
+pub fn benchmark_parse_lib(case: &TestCase, parse: &Parse) -> BenchmarkSummary {
#[cfg(feature = "dhat-heap")]
println!("Start");
#[cfg(feature = "dhat-heap")]
- let stats = dhat::HeapStats::get();
+ let stats = print_stats(dhat::HeapStats::get(), None);
let parser_timer = timing::start();
- let (events, diagnostics, trivia) = parse_common(code, FileId::zero(), source_type);
+ let parsed = parse.parse();
let parse_duration = parser_timer.stop();
#[cfg(feature = "dhat-heap")]
println!("Parsed");
#[cfg(feature = "dhat-heap")]
- let stats = print_diff(stats, dhat::HeapStats::get());
-
- let tree_sink_timer = timing::start();
- let mut tree_sink = rome_js_parser::JsLosslessTreeSink::new(code, &trivia);
- rome_parser::event::process(&mut tree_sink, events, diagnostics);
- let (_green, diagnostics) = tree_sink.finish();
- let tree_sink_duration = tree_sink_timer.stop();
-
- #[cfg(feature = "dhat-heap")]
- println!("Tree-Sink");
- #[cfg(feature = "dhat-heap")]
- print_diff(stats, dhat::HeapStats::get());
+ print_stats(dhat::HeapStats::get(), Some(stats));
BenchmarkSummary::Parser(ParseMeasurement {
- id: id.to_string(),
- code: code.to_string(),
- parsing: parse_duration,
- tree_sink: tree_sink_duration,
- diagnostics,
+ id: case.filename().to_string(),
+ code: case.code().to_string(),
+ duration: parse_duration,
+ diagnostics: parsed.into_diagnostics(),
})
}
-pub fn run_parse(code: &str, source_type: SourceType) -> Parse<JsAnyRoot> {
- rome_js_parser::parse(code, FileId::zero(), source_type)
-}
-
impl ParseMeasurement {
- fn total(&self) -> Duration {
- self.parsing.add(self.tree_sink)
- }
-
pub(crate) fn summary(&self) -> String {
- format!(
- "{}, Total Time: {:?}, parsing: {:?}, tree_sink: {:?}",
- self.id,
- self.total(),
- self.parsing,
- self.tree_sink,
- )
+ format!("{}, Total Time: {:?}", self.id, self.duration,)
}
}
impl Display for ParseMeasurement {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
- let _ = writeln!(f, "\tParsing: {:>10?}", self.parsing);
- let _ = writeln!(f, "\tTree_sink: {:>10?}", self.tree_sink);
- let _ = writeln!(f, "\t ----------");
- let _ = writeln!(f, "\tTotal: {:>10?}", self.total());
+ let _ = writeln!(f, "\tDuration: {:>10?}", self.duration);
let _ = writeln!(f, "\tDiagnostics");

ADD
xtask/bench/src/language.rs
+use crate::test_case::TestCase;
+use criterion::black_box;
+use rome_analyze::{AnalysisFilter, AnalyzerOptions, ControlFlow, Never, RuleCategories};
+use rome_diagnostics::FileId;
+use rome_formatter::{FormatResult, Formatted, PrintResult, Printed};
+use rome_js_analyze::analyze;
+use rome_js_formatter::context::{JsFormatContext, JsFormatOptions};
+use rome_js_syntax::{JsAnyRoot, JsSyntaxNode, SourceType};
+use rome_json_formatter::context::{JsonFormatContext, JsonFormatOptions};
+use rome_json_syntax::JsonSyntaxNode;
+use rome_parser::prelude::ParseDiagnostic;
+
+pub enum Parse<'a> {
+ JavaScript(SourceType, &'a str),
+ Json(&'a str),
+}
+
+impl<'a> Parse<'a> {
+ pub fn try_from_case(case: &TestCase) -> Option<Parse> {
+ match SourceType::try_from(case.path()) {
+ Ok(source_type) => Some(Parse::JavaScript(source_type, case.code())),
+ Err(_) => match case.extension() {
+ "json" => Some(Parse::Json(case.code())),
+ _ => None,
+ },
+ }
+ }
+
+ pub fn parse(&self) -> Parsed {
+ match self {
+ Parse::JavaScript(source_type, code) => Parsed::JavaScript(
+ rome_js_parser::parse(code, FileId::zero(), *source_type),
+ *source_type,
+ ),
+ Parse::Json(code) => Parsed::Json(rome_json_parser::parse_json(code, FileId::zero())),
+ }
+ }
+}
+
+pub enum Parsed {
+ JavaScript(rome_js_parser::Parse<JsAnyRoot>, SourceType),
+ Json(rome_json_parser::JsonParse),
+}
+
+impl Parsed {
+ pub fn format_node(&self) -> Option<FormatNode> {
+ match self {
+ Parsed::JavaScript(parse, source_type) => {
+ Some(FormatNode::JavaScript(parse.syntax(), *source_type))
+ }
+ Parsed::Json(parse) => Some(FormatNode::Json(parse.syntax())),
+ }
+ }
+
+ pub fn analyze(&self) -> Option<Analyze> {
+ match self {
+ Parsed::JavaScript(parse, _) => Some(Analyze::JavaScript(parse.tree())),
+ Parsed::Json(_) => None,
+ }
+ }
+
+ pub fn into_diagnostics(self) -> Vec<ParseDiagnostic> {
+ match self {
+ Parsed::JavaScript(parse, _) => parse.into_diagnostics(),
+ Parsed::Json(parse) => parse.into_diagnostics(),
+ }
+ }
+}
+
+pub enum FormatNode {
+ JavaScript(JsSyntaxNode, SourceType),
+ Json(JsonSyntaxNode),
+}
+
+impl FormatNode {
+ pub fn format_node(&self) -> FormatResult<FormattedNode> {
+ match self {
+ Self::JavaScript(root, source_type) => {
+ rome_js_formatter::format_node(JsFormatOptions::new(*source_type), root)
+ .map(FormattedNode::JavaScript)
+ }
+ FormatNode::Json(root) => {
+ rome_json_formatter::format_node(JsonFormatOptions::default(), root)
+ .map(FormattedNode::Json)
+ }
+ }
+ }
+}
+
+pub enum FormattedNode {
+ JavaScript(Formatted<JsFormatContext>),
+ Json(Formatted<JsonFormatContext>),
+}
+
+impl FormattedNode {
+ pub fn print(&self) -> PrintResult<Printed> {
+ match self {
+ FormattedNode::JavaScript(formatted) => formatted.print(),
+ FormattedNode::Json(formatted) => formatted.print(),
+ }
+ }
+}
+
+pub enum Analyze {
+ JavaScript(JsAnyRoot),
+}
+
+impl Analyze {
+ pub fn analyze(&self) {
+ match self {
+ Analyze::JavaScript(root) => {
+ let filter = AnalysisFilter {
+ categories: RuleCategories::SYNTAX | RuleCategories::LINT,
+ ..AnalysisFilter::default()
+ };
+ let options = AnalyzerOptions::default();
+ analyze(FileId::zero(), root, filter, &options, |event| {
+ black_box(event.diagnostic());
+ black_box(event.actions());
+ ControlFlow::<Never>::Continue(())
+ });
+ }
+ }
+ }
+}

MODIFY
xtask/bench/src/lib.rs
xtask/bench/src/lib.rs
mod features;
-mod utils;
+mod language;
+mod test_case;
-use rome_diagnostics::location::FileId;
-use rome_js_parser::parse;
-use rome_js_syntax::SourceType;
use std::collections::HashMap;
-use std::convert::TryInto;
use std::fmt::{Display, Formatter};
-use std::path::Path;
use std::str::FromStr;
use std::time::Duration;
pub use crate::features::analyzer::benchmark_analyze_lib;
-use crate::features::analyzer::{run_analyzer, AnalyzerMeasurement};
+use crate::features::analyzer::AnalyzerMeasurement;
pub use crate::features::formatter::benchmark_format_lib;
use crate::features::formatter::{run_format, FormatterMeasurement};
pub use crate::features::parser::benchmark_parse_lib;
-use crate::features::parser::{run_parse, ParseMeasurement};
-pub use utils::get_code;
+use crate::features::parser::ParseMeasurement;
+use crate::language::Parse;
+use crate::test_case::TestCase;
/// What feature to benchmark
#[derive(Eq, PartialEq)]
@@ -103,6 +100,7 @@ pub fn run(args: RunArgs) {
} else {
all_suites.insert("js", include_str!("libs-js.txt"));
all_suites.insert("ts", include_str!("libs-ts.txt"));
+ all_suites.insert("json", include_str!("libs-json.txt"));
}
let mut libs = vec![];
@@ -110,14 +108,14 @@ pub fn run(args: RunArgs) {
for suite in suites_to_run {
match suite {
"*" => {
- libs.extend(all_suites["js"].lines());
- libs.extend(all_suites["ts"].lines());
+ libs.extend(all_suites.values().flat_map(|suite| suite.lines()));
}
- "js" => libs.extend(all_suites["js"].lines()),
- "ts" => libs.extend(all_suites["ts"].lines()),
- unknown => {
- eprintln!("Unknown suite: {}", unknown);
+ key => match all_suites.get(key) {
+ Some(suite) => libs.extend(suite.lines()),
+ None => {
+ eprintln!("Unknown suite: {key}");
}
+ },
}
}
@@ -128,13 +126,13 @@ pub fn run(args: RunArgs) {
continue;
}
- let code = get_code(lib);
+ let test_case = TestCase::try_from(lib);
- match code {
- Ok((id, code)) => {
- let code = code.as_str();
+ match test_case {
+ Ok(test_case) => {
+ let parse = Parse::try_from_case(&test_case).expect("Supported language");
- let source_type: SourceType = Path::new(&id).try_into().unwrap();
+ let code = test_case.code();
// Do all steps with criterion now
if args.criterion {
@@ -147,35 +145,66 @@ pub fn run(args: RunArgs) {
let mut group = criterion.benchmark_group(args.feature.to_string());
group.throughput(criterion::Throughput::Bytes(code.len() as u64));
- group.bench_function(&id, |b| match args.feature {
- FeatureToBenchmark::Parser => b.iter(|| {
- criterion::black_box(run_parse(code, source_type));
- }),
+ match args.feature {
+ FeatureToBenchmark::Parser => {
+ group.bench_function(test_case.filename(), |b| {
+ b.iter(|| {
+ criterion::black_box(parse.parse());
+ })
+ });
+ }
FeatureToBenchmark::Formatter => {
- let root = parse(code, FileId::zero(), source_type).syntax();
+ let parsed = parse.parse();
+
+ match parsed.format_node() {
+ None => {
+ continue;
+ }
+ Some(format_node) => {
+ group.bench_function(test_case.filename(), |b| {
b.iter(|| {
- criterion::black_box(run_format(&root, source_type));
+ criterion::black_box(run_format(&format_node));
})
+ });
+ }
+ }
}
FeatureToBenchmark::Analyzer => {
- let root = parse(code, FileId::zero(), source_type).tree();
+ let parsed = parse.parse();
+
+ match parsed.analyze() {
+ None => {
+ continue;
+ }
+ Some(analyze) => {
+ group.bench_function(test_case.filename(), |b| {
b.iter(|| {
- run_analyzer(&root);
+ analyze.analyze();
+ criterion::black_box(());
})
- }
});
+ }
+ }
+ }
+ }
+
group.finish();
}
let result = match args.feature {
- FeatureToBenchmark::Parser => benchmark_parse_lib(&id, code, source_type),
+ FeatureToBenchmark::Parser => benchmark_parse_lib(&test_case, &parse),
FeatureToBenchmark::Formatter => {
- let root = parse(code, FileId::zero(), source_type).syntax();
- benchmark_format_lib(&id, &root, source_type)
+ let parsed = parse.parse();
+ let format_node = parsed
+ .format_node()
+ .expect("Expect formatting to be supported");
+
+ benchmark_format_lib(test_case.filename(), &format_node)
}
FeatureToBenchmark::Analyzer => {
- let root = parse(code, FileId::zero(), source_type).tree();
- benchmark_analyze_lib(&id, &root)
+ let parsed = parse.parse();
+ let analyze = parsed.analyze().expect("Expect analyze to be supported");
+ benchmark_analyze_lib(&test_case, &analyze)
}
};

ADD
xtask/bench/src/libs-json.txt
+https://raw.githubusercontent.com/pys5205/docker-pys/d1d026f3f3789f31f753661af0a0235776294287/package-lock.json
+https://raw.githubusercontent.com/miloyip/nativejson-benchmark/master/data/canada.json
+https://raw.githubusercontent.com/jshttp/mime-db/master/db.json
+https://raw.githubusercontent.com/ashtuchkin/iconv-lite/master/encodings/tables/big5-added.json
+https://raw.githubusercontent.com/ashtuchkin/iconv-lite/master/encodings/tables/eucjp.json
\ No newline at end of file

ADD
xtask/bench/src/test_case.rs
+use crate::err_to_string;
+use ansi_rgb::{red, Foreground};
+use std::env;
+use std::path::{Path, PathBuf};
+use std::str::FromStr;
+
+pub struct TestCase {
+ code: String,
+ id: String,
+ path: PathBuf,
+}
+
+impl TestCase {
+ pub fn try_from(test_case: &str) -> Result<TestCase, String> {
+ let url = url::Url::from_str(test_case).map_err(err_to_string)?;
+ let segments = url
+ .path_segments()
+ .ok_or_else(|| "lib url has no segments".to_string())?;
+ let filename = segments
+ .last()
+ .ok_or_else(|| "lib url has no segments".to_string())?;
+
+ let path = Path::new(
+ &env::var("CARGO_MANIFEST_DIR")
+ .unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()),
+ )
+ .ancestors()
+ .nth(2)
+ .unwrap()
+ .join("target")
+ .join(filename);
+
+ let content = std::fs::read_to_string(&path)
+ .map_err(err_to_string)
+ .or_else(|_| {
+ println!(
+ "[{}] - Downloading [{}] to [{}]",
+ filename,
+ test_case,
+ path.display()
+ );
+ match ureq::get(test_case).call() {
+ Ok(response) => {
+ let mut reader = response.into_reader();
+
+ let mut writer = std::fs::File::create(&path).map_err(err_to_string)?;
+ if let Err(err) = std::io::copy(&mut reader, &mut writer) {
+ drop(writer);
+ std::fs::remove_file(&path).ok();
+ return Err(err_to_string(err));
+ }
+ std::fs::read_to_string(&path).map_err(err_to_string)
+ }
+ Err(e) => Err(err_to_string(e)),
+ }
+ });
+
+ content.map(|code| {
+ println!("[{}] - using [{}]", filename.fg(red()), path.display());
+ TestCase {
+ id: filename.to_string(),
+ code,
+ path,
+ }
+ })
+ }
+
+ pub fn filename(&self) -> &str {
+ &self.id
+ }
+
+ pub fn path(&self) -> &Path {
+ self.path.as_path()
+ }
+
+ pub fn code(&self) -> &str {
+ &self.code
+ }
+
+ pub fn extension(&self) -> &str {
+ self.path
+ .extension()
+ .expect("Expected test case to have extension")
+ .to_str()
+ .expect("Expected extension to be valid UTF8")
+ }
+}

DELETE
xtask/bench/src/utils.rs
-use crate::err_to_string;
-use ansi_rgb::{red, Foreground};
-use std::env;
-use std::path::Path;
-use std::str::FromStr;
-
-pub fn get_code(lib: &str) -> Result<(String, String), String> {
- let url = url::Url::from_str(lib).map_err(err_to_string)?;
- let segments = url
- .path_segments()
- .ok_or_else(|| "lib url has no segments".to_string())?;
- let filename = segments
- .last()
- .ok_or_else(|| "lib url has no segments".to_string())?;
-
- let file = Path::new(
- &env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()),
- )
- .ancestors()
- .nth(2)
- .unwrap()
- .join("target")
- .join(filename);
-
- match std::fs::read_to_string(&file) {
- Ok(code) => {
- println!("[{}] - using [{}]", filename.fg(red()), file.display());
- Ok((filename.to_string(), code))
- }
- Err(_) => {
- println!(
- "[{}] - Downloading [{}] to [{}]",
- filename,
- lib,
- file.display()
- );
- match ureq::get(lib).call() {
- Ok(response) => {
- let mut reader = response.into_reader();
-
- let mut writer = std::fs::File::create(&file).map_err(err_to_string)?;
- if let Err(err) = std::io::copy(&mut reader, &mut writer) {
- drop(writer);
- std::fs::remove_file(&file).ok();
- return Err(err_to_string(err));
- }
- std::fs::read_to_string(&file)
- .map_err(err_to_string)
- .map(|code| (filename.to_string(), code))
- }
- Err(e) => Err(format!("{:?}", e)),
- }
- }
- }
-}

bench: Add JSON support to `xtask bench` (#3845)