1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use serde::de::DeserializeOwned;
use serde::{de, Deserialize, Deserializer};
use std::env;
use std::error::Error;
use std::ffi::OsStr;
use std::fs;
use std::path::PathBuf;
pub fn find() -> Option<Vec<String>> {
try_find().ok()
}
struct Ignored;
impl<E: Error> From<E> for Ignored {
fn from(_error: E) -> Self {
Ignored
}
}
#[derive(Deserialize)]
struct Build {
#[serde(deserialize_with = "from_json")]
features: Vec<String>,
}
fn try_find() -> Result<Vec<String>, Ignored> {
let test_binary = env::args_os().next().ok_or(Ignored)?;
let test_binary_lossy = test_binary.to_string_lossy();
let hash = test_binary_lossy
.get(test_binary_lossy.len() - 17..)
.ok_or(Ignored)?;
if !hash.starts_with('-') || !hash[1..].bytes().all(is_lower_hex_digit) {
return Err(Ignored);
}
let binary_path = PathBuf::from(&test_binary);
let up = binary_path
.parent()
.ok_or(Ignored)?
.parent()
.ok_or(Ignored)?;
let fingerprint_dir = up.join(".fingerprint");
if !fingerprint_dir.is_dir() {
return Err(Ignored);
}
let mut hash_matches = Vec::new();
for entry in fingerprint_dir.read_dir()? {
let entry = entry?;
let is_dir = entry.file_type()?.is_dir();
let matching_hash = entry.file_name().to_string_lossy().ends_with(hash);
if is_dir && matching_hash {
hash_matches.push(entry.path());
}
}
if hash_matches.len() != 1 {
return Err(Ignored);
}
let mut json_matches = Vec::new();
for entry in hash_matches[0].read_dir()? {
let entry = entry?;
let is_file = entry.file_type()?.is_file();
let is_json = entry.path().extension() == Some(OsStr::new("json"));
if is_file && is_json {
json_matches.push(entry.path());
}
}
if json_matches.len() != 1 {
return Err(Ignored);
}
let build_json = fs::read_to_string(&json_matches[0])?;
let build: Build = serde_json::from_str(&build_json)?;
Ok(build.features)
}
fn is_lower_hex_digit(byte: u8) -> bool {
byte >= b'0' && byte <= b'9' || byte >= b'a' && byte <= b'f'
}
fn from_json<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
T: DeserializeOwned,
D: Deserializer<'de>,
{
let json = String::deserialize(deserializer)?;
serde_json::from_str(&json).map_err(de::Error::custom)
}