Skip to content

Commit eb9f2d1

Browse files
committed
feat!: replace subdir by prefix
Signed-off-by: Roman Volosatovs <[email protected]>
1 parent 76010e9 commit eb9f2d1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+293
-334
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Optional `prefix` attribute is now supported by the manifest, which allows specifying a prefix in the archive, from which to extract the WIT definitions.
13+
For example, `clocks = { url = "https://github.com/WebAssembly/wasi/archive/main.tar.gz", prefix = "WASI-main/proposals/clocks" }`
14+
15+
### Removed
16+
17+
- `subdir` attribute is no longer supported
18+
1019
## [0.5.0] - 2025-01-16
1120

1221
### Added

Cargo.lock

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

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "wit-deps-cli"
3-
version = "0.5.0"
3+
version = "0.6.0"
44
description = "WIT dependency manager"
55

66
authors.workspace = true
@@ -69,4 +69,4 @@ tracing-subscriber = { version = "0.3", default-features = false }
6969
url = { version = "2", default-features = false }
7070
urlencoding = { version = "2.1", default-features = false }
7171
wit-bindgen = { version = "0.37", default-features = false }
72-
wit-deps = { path = "./crates/wit-deps", version = "0.5" }
72+
wit-deps = { path = "./crates/wit-deps", version = "0.6" }

crates/wit-deps/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "wit-deps"
3-
version = "0.5.0"
3+
version = "0.6.0"
44
description = "WIT dependency management"
55
readme = "../../README.md"
66

crates/wit-deps/src/lib.rs

Lines changed: 16 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ pub use manifest::{Entry as ManifestEntry, Manifest};
1616
pub use futures;
1717
pub use tokio;
1818

19+
use core::array;
20+
1921
use std::collections::{BTreeSet, HashMap, HashSet};
2022
use std::ffi::{OsStr, OsString};
2123
use std::path::{Path, PathBuf};
@@ -190,7 +192,7 @@ pub async fn untar(
190192
tar: impl AsyncRead + Unpin,
191193
dst: impl AsRef<Path>,
192194
skip_deps: &HashSet<Identifier>,
193-
subdir: &str,
195+
prefix: &str,
194196
) -> std::io::Result<HashMap<Identifier, PathBuf>> {
195197
use std::io::{Error, Result};
196198

@@ -214,33 +216,24 @@ pub async fn untar(
214216
let path = e
215217
.path()
216218
.map_err(|e| Error::new(e.kind(), format!("failed to query entry path: {e}")))?;
217-
let mut path = path.into_iter().map(OsStr::to_str);
218-
match (
219-
path.next(),
220-
path.next(),
221-
path.next(),
222-
path.next(),
223-
path.next(),
224-
) {
225-
(Some(Some(name)), None, None, None, None)
226-
| (Some(_), Some(Some(name)), None, None, None)
227-
if is_wit(name) && subdir.is_empty() =>
228-
{
229-
let dst = dst.join(name);
230-
unpack(&mut e, &dst).await?;
231-
Ok(untared)
232-
}
233-
(Some(Some(dir)), Some(Some(name)), None, None, None)
234-
| (Some(_), Some(Some(dir)), Some(Some(name)), None, None)
235-
if is_wit(name) && dir == subdir =>
219+
let Ok(path) = path.strip_prefix(prefix) else {
220+
return Ok(untared);
221+
};
222+
let mut path = path.into_iter();
223+
match array::from_fn::<_, 6, _>(|_| path.next().and_then(OsStr::to_str)) {
224+
[Some(name), None, ..]
225+
| [Some("wit"), Some(name), None, ..]
226+
| [Some(_), Some("wit"), Some(name), None, ..]
227+
if is_wit(name) =>
236228
{
237229
let dst = dst.join(name);
238230
unpack(&mut e, &dst).await?;
239231
Ok(untared)
240232
}
241-
(Some(Some("deps")), Some(Some(id)), Some(Some(name)), None, None)
242-
| (Some(_), Some(Some("deps")), Some(Some(id)), Some(Some(name)), None)
243-
if !skip_deps.contains(id) && is_wit(name) && subdir.is_empty() =>
233+
[Some("deps"), Some(id), Some(name), None, ..]
234+
| [Some("wit"), Some("deps"), Some(id), Some(name), None, ..]
235+
| [Some(_), Some("wit"), Some("deps"), Some(id), Some(name), None]
236+
if !skip_deps.contains(id) && is_wit(name) =>
244237
{
245238
let id = Identifier::from(id);
246239
if let Some(base) = dst.parent() {
@@ -256,28 +249,6 @@ pub async fn untar(
256249
Ok(untared)
257250
}
258251
}
259-
(Some(Some(dir)), Some(Some("deps")), Some(Some(id)), Some(Some(name)), None)
260-
| (
261-
Some(_),
262-
Some(Some(dir)),
263-
Some(Some("deps")),
264-
Some(Some(id)),
265-
Some(Some(name)),
266-
) if !skip_deps.contains(id) && is_wit(name) && dir == subdir => {
267-
let id = Identifier::from(id);
268-
if let Some(base) = dst.parent() {
269-
let dst = base.join(&id);
270-
if !untared.contains_key(&id) {
271-
recreate_dir(&dst).await?;
272-
}
273-
let wit = dst.join(name);
274-
unpack(&mut e, &wit).await?;
275-
untared.insert(id, dst);
276-
Ok(untared)
277-
} else {
278-
Ok(untared)
279-
}
280-
}
281252
_ => Ok(untared),
282253
}
283254
})

crates/wit-deps/src/lock.rs

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,6 @@ use futures::io::sink;
1010
use serde::{Deserialize, Serialize};
1111
use url::Url;
1212

13-
fn default_subdir() -> Box<str> {
14-
"wit".into()
15-
}
16-
17-
fn is_default_subdir(s: &str) -> bool {
18-
s == "wit"
19-
}
20-
2113
/// Source of this dependency
2214
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
2315
#[serde(untagged)]
@@ -26,9 +18,9 @@ pub enum EntrySource {
2618
Url {
2719
/// URL
2820
url: Url,
29-
/// Subdirectory containing WIT definitions within the tarball
30-
#[serde(default = "default_subdir", skip_serializing_if = "is_default_subdir")]
31-
subdir: Box<str>,
21+
/// Prefix containing WIT definitions within the tarball
22+
#[serde(default, skip_serializing_if = "str::is_empty")]
23+
prefix: Box<str>,
3224
},
3325
/// Local path
3426
Path {
@@ -71,15 +63,15 @@ impl Entry {
7163
url: Url,
7264
path: impl AsRef<Path>,
7365
deps: BTreeSet<Identifier>,
74-
subdir: impl Into<Box<str>>,
66+
prefix: impl Into<Box<str>>,
7567
) -> anyhow::Result<Self> {
7668
let digest = Self::digest(path)
7769
.await
7870
.context("failed to compute digest")?;
7971
Ok(Self::new(
8072
Some(EntrySource::Url {
8173
url,
82-
subdir: subdir.into(),
74+
prefix: prefix.into(),
8375
}),
8476
digest,
8577
deps,
@@ -184,7 +176,7 @@ mod tests {
184176
Entry {
185177
source: Some(EntrySource::Url {
186178
url: FOO_URL.parse().expect("failed to parse `foo` URL"),
187-
subdir: "wit".into(),
179+
prefix: "".into(),
188180
}),
189181
digest: Digest {
190182
sha256: FromHex::from_hex(FOO_SHA256)

crates/wit-deps/src/manifest.rs

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ pub enum Entry {
3636
sha256: Option<[u8; 32]>,
3737
/// Optional sha512 digest of this resource
3838
sha512: Option<[u8; 64]>,
39-
/// Subdirectory within resource containing WIT, `wit` by default
40-
subdir: Box<str>,
39+
/// Prefix within resource containing WIT, empty by default
40+
prefix: Box<str>,
4141
},
4242
/// Dependency specification expressed as a local path to a directory containing WIT
4343
/// definitions
@@ -51,7 +51,7 @@ impl From<Url> for Entry {
5151
url,
5252
sha256: None,
5353
sha512: None,
54-
subdir: "wit".into(),
54+
prefix: "".into(),
5555
}
5656
}
5757
}
@@ -78,7 +78,7 @@ impl<'de> Deserialize<'de> for Entry {
7878
where
7979
D: serde::Deserializer<'de>,
8080
{
81-
const FIELDS: [&str; 4] = ["path", "sha256", "sha512", "url"];
81+
const FIELDS: [&str; 5] = ["path", "sha256", "sha512", "url", "prefix"];
8282

8383
struct Visitor;
8484
impl<'de> de::Visitor<'de> for Visitor {
@@ -102,7 +102,7 @@ impl<'de> Deserialize<'de> for Entry {
102102
let mut path = None;
103103
let mut sha256 = None;
104104
let mut sha512 = None;
105-
let mut subdir: Option<String> = None;
105+
let mut prefix: Option<String> = None;
106106
let mut url = None;
107107
while let Some((k, v)) = map.next_entry::<String, String>()? {
108108
match k.as_ref() {
@@ -130,12 +130,12 @@ impl<'de> Deserialize<'de> for Entry {
130130
de::Error::custom(format!("invalid `sha512` field value: {e}"))
131131
})?;
132132
}
133-
"subdir" => {
134-
if subdir.is_some() {
135-
return Err(de::Error::duplicate_field("subdir"));
133+
"prefix" => {
134+
if prefix.is_some() {
135+
return Err(de::Error::duplicate_field("prefix"));
136136
}
137-
subdir = v.parse().map(Some).map_err(|e| {
138-
de::Error::custom(format!("invalid `subdir` field value: {e}"))
137+
prefix = v.parse().map(Some).map_err(|e| {
138+
de::Error::custom(format!("invalid `prefix` field value: {e}"))
139139
})?;
140140
}
141141
"url" => {
@@ -149,23 +149,23 @@ impl<'de> Deserialize<'de> for Entry {
149149
k => return Err(de::Error::unknown_field(k, &FIELDS)),
150150
}
151151
}
152-
match (path, sha256, sha512, subdir, url) {
152+
match (path, sha256, sha512, prefix, url) {
153153
(Some(path), None, None, None, None) => Ok(Entry::Path(path)),
154154
(None, sha256, sha512, None, Some(url)) => Ok(Entry::Url {
155155
url,
156156
sha256,
157157
sha512,
158-
subdir: "wit".into(),
158+
prefix: "".into(),
159159
}),
160-
(None, sha256, sha512, Some(subdir), Some(url)) => Ok(Entry::Url {
160+
(None, sha256, sha512, Some(prefix), Some(url)) => Ok(Entry::Url {
161161
url,
162162
sha256,
163163
sha512,
164-
subdir: subdir.into_boxed_str(),
164+
prefix: prefix.into_boxed_str(),
165165
}),
166166
(Some(_), None | Some(_), None | Some(_), None | Some(_), None) => {
167167
Err(de::Error::custom(
168-
"`subdir`, `sha256` and `sha512` are not supported in combination with `path`",
168+
"`prefix`, `sha256` and `sha512` are not supported in combination with `path`",
169169
))
170170
}
171171
_ => Err(de::Error::custom("eiter `url` or `path` must be specified")),
@@ -257,16 +257,16 @@ impl Entry {
257257
// TODO: Check that transitive dependencies are in sync
258258
match (self, source) {
259259
(
260-
Self::Url { url, subdir, .. },
260+
Self::Url { url, prefix, .. },
261261
LockEntrySource::Url {
262262
url: lurl,
263-
subdir: lsubdir,
263+
prefix: lprefix,
264264
},
265-
) if url == *lurl && subdir == *lsubdir => {
265+
) if url == *lurl && prefix == *lprefix => {
266266
debug!("`{}` is already up-to-date, skip fetch", out.display());
267267
return Ok((
268268
LockEntry::new(
269-
Some(LockEntrySource::Url { url, subdir }),
269+
Some(LockEntrySource::Url { url, prefix }),
270270
digest,
271271
deps.keys().cloned().collect(),
272272
),
@@ -341,7 +341,7 @@ impl Entry {
341341
url,
342342
sha256,
343343
sha512,
344-
subdir,
344+
prefix,
345345
} => {
346346
let cache = if let Some(cache) = cache {
347347
match cache.get(&url).await {
@@ -353,7 +353,7 @@ impl Entry {
353353
GzipDecoder::new(BufReader::new(&mut hashed)),
354354
out,
355355
skip_deps,
356-
&subdir,
356+
&prefix,
357357
)
358358
.await
359359
{
@@ -364,7 +364,7 @@ impl Entry {
364364
url,
365365
out,
366366
deps.keys().cloned().collect(),
367-
subdir,
367+
prefix,
368368
)
369369
.await?;
370370
return Ok((entry, deps));
@@ -433,7 +433,7 @@ impl Entry {
433433
GzipDecoder::new(BufReader::new(&mut hashed)),
434434
out,
435435
skip_deps,
436-
&subdir,
436+
&prefix,
437437
)
438438
.await
439439
.with_context(|| format!("failed to unpack contents of `{url}`"))?;
@@ -484,7 +484,7 @@ expected: {}",
484484
let deps = lock_deps(deps).await?;
485485
trace!(?deps, "locked transitive dependencies of `{url}`");
486486
let entry =
487-
LockEntry::from_url(url, out, deps.keys().cloned().collect(), subdir).await?;
487+
LockEntry::from_url(url, out, deps.keys().cloned().collect(), prefix).await?;
488488
Ok((entry, deps))
489489
}
490490
}
@@ -606,7 +606,7 @@ baz = {{ url = "{BAZ_URL}", sha256 = "{BAZ_SHA256}", sha512 = "{BAZ_SHA512}" }}
606606
url: FOO_URL.parse().expect("failed to parse `foo` URL string"),
607607
sha256: None,
608608
sha512: None,
609-
subdir: "wit".into(),
609+
prefix: "".into(),
610610
},
611611
),
612612
(
@@ -617,7 +617,7 @@ baz = {{ url = "{BAZ_URL}", sha256 = "{BAZ_SHA256}", sha512 = "{BAZ_SHA512}" }}
617617
.map(Some)
618618
.expect("failed to decode `bar` sha256"),
619619
sha512: None,
620-
subdir: "wit".into(),
620+
prefix: "".into(),
621621
}
622622
),
623623
(
@@ -630,7 +630,7 @@ baz = {{ url = "{BAZ_URL}", sha256 = "{BAZ_SHA256}", sha512 = "{BAZ_SHA512}" }}
630630
sha512: FromHex::from_hex(BAZ_SHA512)
631631
.map(Some)
632632
.expect("failed to decode `baz` sha512"),
633-
subdir: "wit".into(),
633+
prefix: "".into(),
634634
}
635635
)
636636
])

0 commit comments

Comments
 (0)