Skip to content

Commit 52c60f6

Browse files
Add new rename-from field to ensure checks when source URL file differs from name
1 parent 5e2683e commit 52c60f6

File tree

2 files changed

+99
-24
lines changed

2 files changed

+99
-24
lines changed

src/main.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,21 @@ async fn add_file(args: AddFileArgs) -> anyhow::Result<()> {
167167
.append(true)
168168
.open(&args.toml_file)?;
169169

170+
let rename_from = if let Some(file_name) = args.url.path().split('/').last()
171+
&& let Some(path_name) = args.path.split('/').last()
172+
&& file_name != path_name
173+
{
174+
Some(file_name.to_string())
175+
} else {
176+
None
177+
};
178+
170179
let entry = ManifestFileManaged::new(
171180
args.path,
172181
to_hex(&hash),
173182
args.url,
174183
args.license.unwrap_or(String::new()),
184+
rename_from,
175185
);
176186
let entry = toml::to_string(&entry)?;
177187

src/manifest.rs

Lines changed: 89 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -44,44 +44,92 @@ struct LocationCache {
4444
pub(crate) fn load_manifests(load_from: &Path) -> Result<(Vec<MirrorFile>, Vec<String>), Error> {
4545
let mut result = Vec::new();
4646
let mut cache = LocationCache::default();
47+
let mut errors = Vec::new();
4748

4849
fn load_inner(
4950
load_from: &Path,
5051
result: &mut Vec<MirrorFile>,
5152
cache: &mut LocationCache,
53+
errors: &mut Vec<String>,
5254
) -> anyhow::Result<()> {
5355
for entry in load_from.read_dir()? {
5456
let path = entry?.path();
5557
if path.is_file() && path.extension().and_then(|s| s.to_str()) == Some("toml") {
56-
let manifest = std::fs::read_to_string(&path)
58+
let file_source = std::fs::read_to_string(&path)
59+
.map_err(Error::from)
60+
.with_context(|| format!("failed to read {}", path.display()))?;
61+
let manifest = toml::from_str::<Manifest>(&file_source)
5762
.map_err(Error::from)
58-
.and_then(|raw| toml::from_str::<Manifest>(&raw).map_err(Error::from))
5963
.with_context(|| format!("failed to read {}", path.display()))?;
6064
record_locations(&path, &manifest, cache);
6165

6266
for file in manifest.files {
63-
result.push(match file.into_inner() {
67+
let mirror_file = match file.into_inner() {
6468
ManifestFile::Legacy(legacy) => MirrorFile {
6569
name: legacy.name,
6670
sha256: legacy.sha256,
6771
source: Source::Legacy,
72+
rename_from: None,
6873
},
6974
ManifestFile::Managed(managed) => MirrorFile {
7075
name: managed.name,
7176
sha256: managed.sha256,
7277
source: Source::Url(managed.source),
78+
rename_from: managed.rename_from,
7379
},
74-
});
80+
};
81+
if let Source::Url(ref source) = mirror_file.source {
82+
if let Some(file_name) = source.path().split('/').last()
83+
&& let Some(path_name) = mirror_file.name.split('/').last()
84+
&& path_name != file_name
85+
{
86+
match mirror_file.rename_from {
87+
Some(ref rename_from) => {
88+
if rename_from != file_name {
89+
let location = cache
90+
.seen_paths
91+
.get(&mirror_file.name)
92+
.unwrap()
93+
.first()
94+
.unwrap();
95+
let (src_line, snippet) = span_info(&file_source, location);
96+
errors.push(format!(
97+
"`rename-from` field value doesn't match name from the URL `{source}` (`{file_name}` != `{rename_from}`):\n\
98+
# {} (line {src_line})\n{snippet}\n",
99+
location.file.display()
100+
));
101+
}
102+
}
103+
None => {
104+
let location = cache
105+
.seen_paths
106+
.get(&mirror_file.name)
107+
.unwrap()
108+
.first()
109+
.unwrap();
110+
let (src_line, snippet) = span_info(&file_source, location);
111+
errors.push(format!(
112+
"The name from the URL `{source}` doesn't match the `name` field (`{file_name}` != `{path_name}`). \
113+
Add `rename-from = {file_name:?}` to fix this error:\n\
114+
# {} (line {src_line})\n{snippet}\n",
115+
location.file.display()
116+
));
117+
}
118+
}
119+
}
120+
}
121+
result.push(mirror_file);
75122
}
76123
} else if path.is_dir() {
77-
load_inner(&path, result, cache)?;
124+
load_inner(&path, result, cache, errors)?;
78125
}
79126
}
80127
Ok(())
81128
}
82129

83-
load_inner(load_from, &mut result, &mut cache)?;
84-
Ok((result, find_errors(cache)))
130+
load_inner(load_from, &mut result, &mut cache, &mut errors)?;
131+
find_errors(cache, &mut errors);
132+
Ok((result, errors))
85133
}
86134

87135
fn record_locations(toml_path: &Path, manifest: &Manifest, cache: &mut LocationCache) {
@@ -114,12 +162,32 @@ fn record_locations(toml_path: &Path, manifest: &Manifest, cache: &mut LocationC
114162
.or_default()
115163
.insert(location.clone());
116164
if let Some(url) = url {
117-
cache.seen_urls.entry(url).or_default().insert(location);
165+
cache
166+
.seen_urls
167+
.entry(url)
168+
.or_default()
169+
.insert(location.clone());
170+
}
171+
}
172+
}
173+
174+
fn span_info<'a>(content: &'a str, location: &Location) -> (usize, &'a str) {
175+
// Find the corresponding line number
176+
let mut accumulated_chars = 0;
177+
let mut src_line = 0;
178+
for (index, line) in content.lines().enumerate() {
179+
accumulated_chars += line.len() + 1; // +1 for newline
180+
if accumulated_chars > location.span.0.start {
181+
src_line = index + 1;
182+
break;
118183
}
119184
}
185+
186+
let snippet = &content[location.span.0.start..location.span.0.end];
187+
(src_line, snippet)
120188
}
121189

122-
fn find_errors(cache: LocationCache) -> Vec<String> {
190+
fn find_errors(cache: LocationCache, errors: &mut Vec<String>) {
123191
let mut file_cache: HashMap<PathBuf, String> = HashMap::new();
124192

125193
fn format_locations(
@@ -136,18 +204,7 @@ fn find_errors(cache: LocationCache) -> Vec<String> {
136204
})
137205
});
138206

139-
// Find the corresponding line number
140-
let mut accumulated_chars = 0;
141-
let mut src_line = 0;
142-
for (index, line) in content.lines().enumerate() {
143-
accumulated_chars += line.len() + 1; // +1 for newline
144-
if accumulated_chars > location.span.0.start {
145-
src_line = index + 1;
146-
break;
147-
}
148-
}
149-
150-
let snippet = &content[location.span.0.start..location.span.0.end];
207+
let (src_line, snippet) = span_info(&content, location);
151208
writeln!(
152209
output,
153210
"# {} (line {src_line})\n{snippet}\n",
@@ -159,7 +216,6 @@ fn find_errors(cache: LocationCache) -> Vec<String> {
159216
output
160217
}
161218

162-
let mut errors = Vec::new();
163219
for (path, locations) in cache.seen_paths {
164220
if locations.len() > 1 {
165221
errors.push(format!(
@@ -184,13 +240,13 @@ fn find_errors(cache: LocationCache) -> Vec<String> {
184240
));
185241
}
186242
}
187-
errors
188243
}
189244

190245
pub(crate) struct MirrorFile {
191246
pub(crate) name: String,
192247
pub(crate) sha256: String,
193248
pub(crate) source: Source,
249+
pub(crate) rename_from: Option<String>,
194250
}
195251

196252
pub(crate) enum Source {
@@ -233,15 +289,24 @@ pub struct ManifestFileManaged {
233289
// This field is not considered at all by the automation, we just enforce its presence so that
234290
// people adding new entries think about the licensing implications.
235291
license: String,
292+
#[serde(default, rename = "rename-from")]
293+
rename_from: Option<String>,
236294
}
237295

238296
impl ManifestFileManaged {
239-
pub fn new(name: String, sha256: String, source: Url, license: String) -> Self {
297+
pub fn new(
298+
name: String,
299+
sha256: String,
300+
source: Url,
301+
license: String,
302+
rename_from: Option<String>,
303+
) -> Self {
240304
Self {
241305
name,
242306
sha256,
243307
source,
244308
license,
309+
rename_from,
245310
}
246311
}
247312
}

0 commit comments

Comments
 (0)