Skip to content

Commit 25f0776

Browse files
use thiserror in third-party-licenses tool
1 parent 8caecb2 commit 25f0776

6 files changed

Lines changed: 78 additions & 92 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tools/third-party-licenses/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ desktop = ["dep:cef-dll-sys", "dep:scraper"]
1313
serde = { workspace = true }
1414
serde_json = { workspace = true }
1515
lzma-rust2 = { workspace = true }
16+
thiserror = { workspace = true }
1617

1718
# Optional workspace dependencies
1819
cef-dll-sys = { workspace = true, optional = true }

tools/third-party-licenses/src/cargo.rs

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
use crate::{LicenceSource, LicenseEntry, Package};
1+
use crate::{Error, LicenceSource, LicenseEntry, Package};
22
use serde::Deserialize;
33
use std::fs;
44
use std::hash::{Hash, Hasher};
55
use std::path::PathBuf;
6-
use std::process::{self, Command};
6+
use std::process::Command;
77

88
pub struct CargoLicenseSource {}
99

@@ -14,8 +14,8 @@ impl CargoLicenseSource {
1414
}
1515

1616
impl LicenceSource for CargoLicenseSource {
17-
fn licenses(&self) -> Vec<LicenseEntry> {
18-
parse(run())
17+
fn licenses(&self) -> Result<Vec<LicenseEntry>, Error> {
18+
Ok(parse(run()?))
1919
}
2020
}
2121

@@ -84,23 +84,18 @@ fn parse(parsed: Output) -> Vec<LicenseEntry> {
8484
.collect()
8585
}
8686

87-
fn run() -> Output {
87+
fn run() -> Result<Output, Error> {
8888
let output = Command::new("cargo")
8989
.args(["about", "generate", "--format", "json", "--frozen"])
9090
.current_dir(env!("CARGO_WORKSPACE_DIR"))
9191
.output()
92-
.unwrap_or_else(|e| {
93-
eprintln!("Failed to run cargo about generate: {e}");
94-
process::exit(1)
95-
});
92+
.map_err(|e| Error::Io(e, "Failed to run cargo about generate".into()))?;
9693

9794
if !output.status.success() {
98-
eprintln!("cargo about generate failed:\n{}", String::from_utf8_lossy(&output.stderr));
99-
process::exit(1)
95+
return Err(Error::Command(format!("cargo about generate failed:\n{}", String::from_utf8_lossy(&output.stderr))));
10096
}
10197

102-
serde_json::from_str(&String::from_utf8(output.stdout).expect("cargo about generate should return valid UTF-8")).unwrap_or_else(|e| {
103-
eprintln!("Failed to parse cargo about generate JSON: {e}");
104-
process::exit(1)
105-
})
98+
let stdout = String::from_utf8(output.stdout).map_err(|e| Error::Utf8(e, "cargo about generate returned invalid UTF-8".into()))?;
99+
100+
serde_json::from_str(&stdout).map_err(|e| Error::Json(e, "Failed to parse cargo about generate JSON".into()))
106101
}

tools/third-party-licenses/src/cef.rs

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use lzma_rust2::XzReader;
22
use scraper::{Html, Selector};
3+
use std::fs;
34
use std::hash::Hash;
45
use std::io::Read;
56
use std::path::PathBuf;
6-
use std::{fs, process};
77

8-
use crate::{LicenceSource, LicenseEntry, Package};
8+
use crate::{Error, LicenceSource, LicenseEntry, Package};
99

1010
pub struct CefLicenseSource;
1111

@@ -16,15 +16,15 @@ impl CefLicenseSource {
1616
}
1717

1818
impl LicenceSource for CefLicenseSource {
19-
fn licenses(&self) -> Vec<LicenseEntry> {
20-
let html = read();
21-
parse(&html)
19+
fn licenses(&self) -> Result<Vec<LicenseEntry>, Error> {
20+
let html = read()?;
21+
Ok(parse(&html))
2222
}
2323
}
2424

2525
impl Hash for CefLicenseSource {
2626
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
27-
read().hash(state)
27+
read().unwrap().hash(state)
2828
}
2929
}
3030

@@ -64,42 +64,29 @@ fn parse(html: &str) -> Vec<LicenseEntry> {
6464
.collect()
6565
}
6666

67-
fn read() -> String {
67+
fn read() -> Result<String, Error> {
6868
let cef_path = PathBuf::from(env!("CEF_PATH"));
6969
let cef_credits = std::fs::read_dir(&cef_path)
70-
.unwrap_or_else(|e| {
71-
eprintln!("Failed to read CEF_PATH directory {}: {e}", cef_path.display());
72-
process::exit(1);
73-
})
70+
.map_err(|e| Error::Io(e, format!("Failed to read CEF_PATH directory {}", cef_path.display())))?
7471
.filter_map(|entry| entry.ok())
7572
.find(|entry| {
7673
let name = entry.file_name();
7774
name.eq_ignore_ascii_case("credits.html") || name.eq_ignore_ascii_case("credits.html.xz")
7875
})
7976
.map(|entry| entry.path())
80-
.unwrap_or_else(|| {
81-
eprintln!("Could not find CREDITS.html or CREDITS.html.xz in {}", cef_path.display());
82-
process::exit(1);
83-
});
77+
.ok_or_else(|| Error::CefCreditsNotFound(cef_path.clone()))?;
8478

8579
let decompress_xz = cef_credits.extension().map(|ext| ext.eq_ignore_ascii_case("xz")).unwrap_or(false);
8680

8781
if decompress_xz {
88-
let file = fs::File::open(&cef_credits).unwrap_or_else(|e| {
89-
eprintln!("Failed to open CEF credits file {}: {e}", cef_credits.display());
90-
process::exit(1);
91-
});
82+
let file = fs::File::open(&cef_credits).map_err(|e| Error::Io(e, format!("Failed to open CEF credits file {}", cef_credits.display())))?;
9283
let mut reader = XzReader::new(file, false);
9384
let mut html = String::new();
94-
reader.read_to_string(&mut html).unwrap_or_else(|e| {
95-
eprintln!("Failed to decompress CEF credits file {}: {e}", cef_credits.display());
96-
process::exit(1);
97-
});
98-
html
85+
reader
86+
.read_to_string(&mut html)
87+
.map_err(|e| Error::Io(e, format!("Failed to decompress CEF credits file {}", cef_credits.display())))?;
88+
Ok(html)
9989
} else {
100-
fs::read_to_string(&cef_credits).unwrap_or_else(|e| {
101-
eprintln!("Failed to read CEF credits file {}: {e}", cef_credits.display());
102-
process::exit(1);
103-
})
90+
fs::read_to_string(&cef_credits).map_err(|e| Error::Io(e, format!("Failed to read CEF credits file {}", cef_credits.display())))
10491
}
10592
}

tools/third-party-licenses/src/main.rs

Lines changed: 43 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::collections::HashMap;
2+
use std::fs;
23
use std::hash::{DefaultHasher, Hash, Hasher};
34
use std::path::PathBuf;
4-
use std::{fs, process};
55

66
mod cargo;
77
#[cfg(feature = "desktop")]
@@ -13,8 +13,27 @@ use crate::cargo::CargoLicenseSource;
1313
use crate::cef::CefLicenseSource;
1414
use crate::npm::NpmLicenseSource;
1515

16+
#[derive(Debug, thiserror::Error)]
17+
pub enum Error {
18+
#[error("{1}: {0}")]
19+
Io(#[source] std::io::Error, String),
20+
21+
#[error("{1}: {0}")]
22+
Json(#[source] serde_json::Error, String),
23+
24+
#[error("{1}: {0}")]
25+
Utf8(#[source] std::string::FromUtf8Error, String),
26+
27+
#[error("{0}")]
28+
Command(String),
29+
30+
#[cfg(feature = "desktop")]
31+
#[error("Could not find CREDITS.html or CREDITS.html.xz in {0}")]
32+
CefCreditsNotFound(PathBuf),
33+
}
34+
1635
pub trait LicenceSource: std::hash::Hash {
17-
fn licenses(&self) -> Vec<LicenseEntry>;
36+
fn licenses(&self) -> Result<Vec<LicenseEntry>, Error>;
1837
}
1938

2039
pub struct LicenseEntry {
@@ -39,6 +58,13 @@ struct Run<'a> {
3958
}
4059

4160
fn main() {
61+
if let Err(e) = run() {
62+
eprintln!("Error: {e}");
63+
std::process::exit(1);
64+
}
65+
}
66+
67+
fn run() -> Result<(), Error> {
4268
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
4369
let workspace_dir = PathBuf::from(env!("CARGO_WORKSPACE_DIR"));
4470

@@ -71,32 +97,26 @@ fn main() {
7197

7298
if current_hash == fs::read_to_string(&current_hash_path).unwrap_or_default() {
7399
eprintln!("No changes in licenses detected, skipping generation.");
74-
return;
100+
return Ok(());
75101
}
76102
eprintln!("Changes in licenses detected, generating new license file.");
77103

78104
let licenses = merge_filter_dedup_and_sort(vec![
79-
cargo_source.licenses(),
80-
npm_source.licenses(),
105+
cargo_source.licenses()?,
106+
npm_source.licenses()?,
81107
#[cfg(feature = "desktop")]
82-
cef_source.licenses(),
108+
cef_source.licenses()?,
83109
]);
84110
let formatted = format_credits(&licenses);
85111

86112
#[cfg(feature = "desktop")]
87-
let output = compress(&formatted);
113+
let output = compress(&formatted)?;
88114
#[cfg(not(feature = "desktop"))]
89115
let output = formatted.as_bytes().to_vec();
90116
if let Some(parent) = output_path.parent() {
91-
fs::create_dir_all(parent).unwrap_or_else(|e| {
92-
eprintln!("Failed to create directory {}: {e}", parent.display());
93-
std::process::exit(1);
94-
});
117+
fs::create_dir_all(parent).map_err(|e| Error::Io(e, format!("Failed to create directory {}", parent.display())))?;
95118
}
96-
fs::write(&output_path, &output).unwrap_or_else(|e| {
97-
eprintln!("Failed to write {}: {e}", &output_path.display());
98-
std::process::exit(1);
99-
});
119+
fs::write(&output_path, &output).map_err(|e| Error::Io(e, format!("Failed to write {}", output_path.display())))?;
100120
run.output = &output;
101121

102122
let hash = {
@@ -105,10 +125,9 @@ fn main() {
105125
format!("{:016x}", hasher.finish())
106126
};
107127

108-
fs::write(&current_hash_path, hash).unwrap_or_else(|e| {
109-
eprintln!("Failed to write hash file {}: {e}", current_hash_path.display());
110-
process::exit(1);
111-
});
128+
fs::write(&current_hash_path, hash).map_err(|e| Error::Io(e, format!("Failed to write hash file {}", current_hash_path.display())))?;
129+
130+
Ok(())
112131
}
113132

114133
fn format_credits(licenses: &Vec<LicenseEntry>) -> String {
@@ -210,20 +229,11 @@ fn dedup_by_licence_text(vec: Vec<LicenseEntry>) -> Vec<LicenseEntry> {
210229
}
211230

212231
#[cfg(feature = "desktop")]
213-
fn compress(content: &str) -> Vec<u8> {
232+
fn compress(content: &str) -> Result<Vec<u8>, Error> {
214233
use std::io::Write;
215234
let mut buf = Vec::new();
216-
let mut writer = lzma_rust2::XzWriter::new(&mut buf, lzma_rust2::XzOptions::default()).unwrap_or_else(|e| {
217-
eprintln!("Failed to create XZ writer: {e}");
218-
std::process::exit(1);
219-
});
220-
writer.write_all(content.as_bytes()).unwrap_or_else(|e| {
221-
eprintln!("Failed to write compressed credits: {e}");
222-
std::process::exit(1);
223-
});
224-
writer.finish().unwrap_or_else(|e| {
225-
eprintln!("Failed to finish XZ compression: {e}");
226-
std::process::exit(1);
227-
});
228-
buf
235+
let mut writer = lzma_rust2::XzWriter::new(&mut buf, lzma_rust2::XzOptions::default()).map_err(|e| Error::Io(e, "Failed to create XZ writer".into()))?;
236+
writer.write_all(content.as_bytes()).map_err(|e| Error::Io(e, "Failed to write compressed credits".into()))?;
237+
writer.finish().map_err(|e| Error::Io(e, "Failed to finish XZ compression".into()))?;
238+
Ok(buf)
229239
}

tools/third-party-licenses/src/npm.rs

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
use std::collections::HashMap;
22
use std::fs;
33
use std::path::PathBuf;
4-
use std::process;
54
use std::process::Command;
65

7-
use crate::{LicenceSource, LicenseEntry, Package};
6+
use crate::{Error, LicenceSource, LicenseEntry, Package};
87

98
pub struct NpmLicenseSource {
109
dir: PathBuf,
@@ -16,8 +15,8 @@ impl NpmLicenseSource {
1615
}
1716

1817
impl LicenceSource for NpmLicenseSource {
19-
fn licenses(&self) -> Vec<LicenseEntry> {
20-
parse(run(&self.dir))
18+
fn licenses(&self) -> Result<Vec<LicenseEntry>, Error> {
19+
Ok(parse(run(&self.dir)?))
2120
}
2221
}
2322

@@ -66,28 +65,21 @@ fn parse(parsed: Output) -> Vec<LicenseEntry> {
6665
.collect()
6766
}
6867

69-
fn run(dir: &std::path::Path) -> Output {
68+
fn run(dir: &std::path::Path) -> Result<Output, Error> {
7069
#[cfg(not(target_os = "windows"))]
7170
let mut cmd = Command::new("npx");
7271
#[cfg(target_os = "windows")]
7372
let mut cmd = Command::new("npx.cmd");
7473
cmd.args(["license-checker-rseidelsohn", "--production", "--json"]);
7574
cmd.current_dir(dir);
7675

77-
let output = cmd.output().unwrap_or_else(|e| {
78-
eprintln!("Failed to run npx license-checker-rseidelsohn: {e}");
79-
process::exit(1);
80-
});
76+
let output = cmd.output().map_err(|e| Error::Io(e, "Failed to run npx license-checker-rseidelsohn".into()))?;
8177

8278
if !output.status.success() {
83-
eprintln!("npx license-checker-rseidelsohn failed:\n{}", String::from_utf8_lossy(&output.stderr));
84-
process::exit(1);
79+
return Err(Error::Command(format!("npx license-checker-rseidelsohn failed:\n{}", String::from_utf8_lossy(&output.stderr))));
8580
}
8681

87-
let json_str = String::from_utf8(output.stdout).expect("Invalid UTF-8 from license-checker");
82+
let json_str = String::from_utf8(output.stdout).map_err(|e| Error::Utf8(e, "Invalid UTF-8 from license-checker".into()))?;
8883

89-
serde_json::from_str(&json_str).unwrap_or_else(|e| {
90-
eprintln!("Failed to parse license-checker JSON: {e}");
91-
process::exit(1)
92-
})
84+
serde_json::from_str(&json_str).map_err(|e| Error::Json(e, "Failed to parse license-checker JSON".into()))
9385
}

0 commit comments

Comments
 (0)