Skip to content

Commit 3f484ae

Browse files
authored
feat(js): add Gateway object (#1130)
1 parent 4a6ede9 commit 3f484ae

15 files changed

+828
-12
lines changed

Cargo.lock

-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js-rattler/Cargo.lock

+14
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js-rattler/Cargo.toml

+5
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ path = "crate/lib.rs"
1313
default = ["console_error_panic_hook"]
1414

1515
[dependencies]
16+
serde = { version = "1.0", features = ["derive"] }
17+
serde-wasm-bindgen = "0.6.5"
18+
1619
wasm-bindgen = "0.2.95"
1720
wasm-bindgen-futures = "0.4.50"
1821

@@ -35,6 +38,8 @@ rattler_conda_types = { path = "../crates/rattler_conda_types" }
3538
rattler_repodata_gateway = { path = "../crates/rattler_repodata_gateway", features = ["gateway"] }
3639
rattler_solve = { path = "../crates/rattler_solve", default-features = false, features = ["resolvo"] }
3740

41+
url = "2.5.4"
42+
3843
# By adding the `libbz2-rs-sys` feature we ensure that bzip2 is using the rust
3944
# implementation. This is important because the C implementation is not
4045
# compatible with wasm.

js-rattler/crate/error.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use rattler_conda_types::version_spec::ParseVersionSpecError;
22
use rattler_conda_types::{
3-
ParseChannelError, ParseMatchSpecError, ParsePlatformError, ParseVersionError,
4-
VersionBumpError, VersionExtendError,
3+
InvalidPackageNameError, ParseChannelError, ParseMatchSpecError, ParsePlatformError,
4+
ParseVersionError, VersionBumpError, VersionExtendError,
55
};
66
use rattler_repodata_gateway::GatewayError;
77
use rattler_solve::SolveError;
@@ -28,6 +28,10 @@ pub enum JsError {
2828
GatewayError(#[from] GatewayError),
2929
#[error(transparent)]
3030
SolveError(#[from] SolveError),
31+
#[error(transparent)]
32+
Serde(#[from] serde_wasm_bindgen::Error),
33+
#[error(transparent)]
34+
PackageNameError(#[from] InvalidPackageNameError),
3135
}
3236

3337
pub type JsResult<T> = Result<T, JsError>;
@@ -44,6 +48,8 @@ impl From<JsError> for JsValue {
4448
JsError::ParseMatchSpec(error) => JsValue::from_str(&error.to_string()),
4549
JsError::GatewayError(error) => JsValue::from_str(&error.to_string()),
4650
JsError::SolveError(error) => JsValue::from_str(&error.to_string()),
51+
JsError::PackageNameError(error) => JsValue::from_str(&error.to_string()),
52+
JsError::Serde(error) => error.into(),
4753
}
4854
}
4955
}

js-rattler/crate/gateway.rs

+151
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
use std::{collections::HashMap, path::PathBuf, str::FromStr};
2+
3+
use rattler_conda_types::{Channel, Platform};
4+
use rattler_repodata_gateway::{fetch::CacheAction, ChannelConfig, Gateway, SourceConfig};
5+
use serde::Deserialize;
6+
use url::Url;
7+
use wasm_bindgen::prelude::*;
8+
9+
use crate::JsResult;
10+
11+
#[wasm_bindgen]
12+
#[repr(transparent)]
13+
#[derive(Clone)]
14+
pub struct JsGateway {
15+
inner: Gateway,
16+
}
17+
18+
impl From<Gateway> for JsGateway {
19+
fn from(value: Gateway) -> Self {
20+
JsGateway { inner: value }
21+
}
22+
}
23+
24+
impl From<JsGateway> for Gateway {
25+
fn from(value: JsGateway) -> Self {
26+
value.inner
27+
}
28+
}
29+
30+
impl AsRef<Gateway> for JsGateway {
31+
fn as_ref(&self) -> &Gateway {
32+
&self.inner
33+
}
34+
}
35+
36+
#[derive(Default, Deserialize)]
37+
#[serde(rename_all = "camelCase")]
38+
struct JsGatewayOptions {
39+
max_concurrent_requests: Option<usize>,
40+
41+
#[serde(default)]
42+
channel_config: JsChannelConfig,
43+
}
44+
45+
#[derive(Default, Deserialize)]
46+
#[serde(rename_all = "camelCase")]
47+
struct JsChannelConfig {
48+
#[serde(default)]
49+
default: JsSourceConfig,
50+
#[serde(default)]
51+
per_channel: HashMap<Url, JsSourceConfig>,
52+
}
53+
54+
impl From<JsChannelConfig> for ChannelConfig {
55+
fn from(value: JsChannelConfig) -> Self {
56+
ChannelConfig {
57+
default: value.default.into(),
58+
per_channel: value
59+
.per_channel
60+
.into_iter()
61+
.map(|(key, value)| (key, value.into()))
62+
.collect(),
63+
}
64+
}
65+
}
66+
67+
fn yes() -> bool {
68+
true
69+
}
70+
71+
#[derive(Deserialize)]
72+
#[serde(rename_all = "camelCase")]
73+
struct JsSourceConfig {
74+
#[serde(default = "yes")]
75+
zstd_enabled: bool,
76+
77+
#[serde(default = "yes")]
78+
bz2_enabled: bool,
79+
80+
#[serde(default = "yes")]
81+
sharded_enabled: bool,
82+
}
83+
84+
impl Default for JsSourceConfig {
85+
fn default() -> Self {
86+
Self {
87+
zstd_enabled: true,
88+
bz2_enabled: true,
89+
sharded_enabled: true,
90+
}
91+
}
92+
}
93+
94+
impl From<JsSourceConfig> for SourceConfig {
95+
fn from(value: JsSourceConfig) -> Self {
96+
Self {
97+
jlap_enabled: false,
98+
zstd_enabled: value.zstd_enabled,
99+
bz2_enabled: value.bz2_enabled,
100+
sharded_enabled: value.sharded_enabled,
101+
cache_action: CacheAction::default(),
102+
}
103+
}
104+
}
105+
106+
#[wasm_bindgen]
107+
impl JsGateway {
108+
#[wasm_bindgen(constructor)]
109+
pub fn new(input: JsValue) -> JsResult<Self> {
110+
let mut builder = Gateway::builder();
111+
let options: Option<JsGatewayOptions> = serde_wasm_bindgen::from_value(input)?;
112+
if let Some(options) = options {
113+
if let Some(max_concurrent_requests) = options.max_concurrent_requests {
114+
builder.set_max_concurrent_requests(max_concurrent_requests);
115+
}
116+
builder.set_channel_config(options.channel_config.into());
117+
};
118+
119+
Ok(Self {
120+
inner: builder.finish(),
121+
})
122+
}
123+
124+
pub async fn names(
125+
&self,
126+
channels: Vec<String>,
127+
platforms: Vec<String>,
128+
) -> Result<Vec<String>, JsError> {
129+
// TODO: Dont hardcode
130+
let channel_config =
131+
rattler_conda_types::ChannelConfig::default_with_root_dir(PathBuf::from(""));
132+
133+
let channels = channels
134+
.into_iter()
135+
.map(|s| Channel::from_str(&s, &channel_config))
136+
.collect::<Result<Vec<_>, _>>()?;
137+
let platforms = platforms
138+
.into_iter()
139+
.map(|p| Platform::from_str(&p))
140+
.collect::<Result<Vec<_>, _>>()?;
141+
142+
Ok(self
143+
.inner
144+
.names(channels, platforms)
145+
.execute()
146+
.await?
147+
.into_iter()
148+
.map(|name| name.as_source().to_string())
149+
.collect())
150+
}
151+
}

js-rattler/crate/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod error;
2+
mod gateway;
23
pub mod solve;
34
mod utils;
45
mod version;

js-rattler/src/Gateway.test.ts

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { describe, expect, it } from "@jest/globals";
2+
import { Gateway } from "./Gateway";
3+
4+
describe("Gateway", () => {
5+
describe("constructor", () => {
6+
it("works without arguments", () => {
7+
expect(() => new Gateway()).not.toThrowError();
8+
expect(() => new Gateway(null)).not.toThrowError();
9+
expect(() => new Gateway(undefined)).not.toThrowError();
10+
});
11+
it("throws on invalid arguments", () => {
12+
expect(() => new Gateway(true as any)).toThrowError();
13+
});
14+
it("accepts an empty object", () => {
15+
expect(() => new Gateway({})).not.toThrowError();
16+
});
17+
it("accepts null for maxConcurrentRequests", () => {
18+
expect(
19+
() =>
20+
new Gateway({
21+
maxConcurrentRequests: null,
22+
}),
23+
).not.toThrowError();
24+
});
25+
it("accepts empty channelConfig", () => {
26+
expect(
27+
() =>
28+
new Gateway({
29+
channelConfig: {},
30+
}),
31+
).not.toThrowError();
32+
});
33+
it("accepts perChannel channelConfig", () => {
34+
expect(
35+
() =>
36+
new Gateway({
37+
channelConfig: {
38+
default: {},
39+
perChannel: {
40+
"https://prefix.dev": {
41+
bz2Enabled: false,
42+
shardedEnabled: false,
43+
zstdEnabled: false,
44+
},
45+
},
46+
},
47+
}),
48+
).not.toThrowError();
49+
});
50+
});
51+
describe("names", () => {
52+
const gateway = new Gateway();
53+
it("can query prefix.dev", () => {
54+
return gateway
55+
.names(
56+
["https://prefix.dev/emscripten-forge-dev"],
57+
["noarch", "emscripten-wasm32"],
58+
)
59+
.then((names) => {
60+
expect(names.length).toBeGreaterThanOrEqual(177);
61+
});
62+
});
63+
});
64+
});

0 commit comments

Comments
 (0)