const WORLD_APP_USER_AGENT_PRODUCT: &str = "WorldApp";
const WORLD_ID_APP_USER_AGENT_PRODUCT: &str = "WorldID";
const WORLD_ID_ANDROID_CLIENT_NAME: &str = "android-id";
const WORLD_ID_IOS_CLIENT_NAME: &str = "ios-id";
#[derive(Debug, Clone, uniffi::Object)]
pub struct UserAgent(pub String);
impl std::fmt::Display for UserAgent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)?;
Ok(())
}
}
#[uniffi::export]
impl UserAgent {
#[must_use]
pub fn header_value(&self) -> String {
self.0.clone()
}
}
#[derive(Debug, Clone, uniffi::Object)]
pub struct UserAgentBuilder {
segments: Vec<String>,
}
#[uniffi::export]
impl UserAgentBuilder {
#[uniffi::constructor]
#[must_use]
pub const fn new() -> Self {
Self {
segments: Vec::new(),
}
}
#[must_use]
pub fn with_segment(&self, name: &str, version: &str) -> Self {
let mut next = self.clone();
next.segments.push(format!("{name}/{version}"));
next
}
#[must_use]
pub fn with_app_segment_for_client(
&self,
app_version: &str,
client_name: &str,
) -> Self {
self.with_segment(user_agent_product_for_client(client_name), app_version)
}
#[must_use]
pub fn with_walletkit_segment(&self) -> Self {
let mut next = self.clone();
let crate_version = env!("CARGO_PKG_VERSION");
next.segments
.push(format!("walletkit-core/{crate_version}"));
next
}
#[must_use]
pub fn with_client_segment(&self, client_name: &str, os_version: &str) -> Self {
self.with_segment(client_name, os_version)
}
#[must_use]
pub fn build(&self) -> UserAgent {
UserAgent(self.segments.join(" "))
}
}
impl Default for UserAgentBuilder {
fn default() -> Self {
Self::new()
}
}
fn user_agent_product_for_client(client_name: &str) -> &'static str {
match client_name {
WORLD_ID_ANDROID_CLIENT_NAME | WORLD_ID_IOS_CLIENT_NAME => {
WORLD_ID_APP_USER_AGENT_PRODUCT
}
_ => WORLD_APP_USER_AGENT_PRODUCT,
}
}
#[cfg(test)]
mod tests {
use super::*;
use test_case::test_case;
#[test_case(
&UserAgentBuilder::new().with_walletkit_segment(),
concat!("walletkit-core/", env!("CARGO_PKG_VERSION"));
"walletkit_only"
)]
#[test_case(
&UserAgentBuilder::new()
.with_segment("WorldApp", "1.0")
.with_walletkit_segment(),
concat!("WorldApp/1.0 walletkit-core/", env!("CARGO_PKG_VERSION"));
"world_app_then_walletkit"
)]
#[test_case(
&UserAgentBuilder::new()
.with_segment("WorldApp", "2.1")
.with_walletkit_segment()
.with_segment("iOS", "17.0"),
concat!("WorldApp/2.1 walletkit-core/", env!("CARGO_PKG_VERSION"), " iOS/17.0");
"native_style_world_walletkit_os"
)]
#[test_case(
&UserAgentBuilder::new()
.with_walletkit_segment()
.with_segment("CLI", "1.2.3"),
concat!("walletkit-core/", env!("CARGO_PKG_VERSION"), " CLI/1.2.3");
"walletkit_then_cli"
)]
#[test_case(
&UserAgentBuilder::new()
.with_app_segment_for_client("4.0.2500", "android")
.with_walletkit_segment()
.with_client_segment("android", "15"),
concat!("WorldApp/4.0.2500 walletkit-core/", env!("CARGO_PKG_VERSION"), " android/15");
"world_app_android_client"
)]
#[test_case(
&UserAgentBuilder::new()
.with_app_segment_for_client("4.0.2500", "ios")
.with_walletkit_segment()
.with_client_segment("ios", "26.4.2"),
concat!("WorldApp/4.0.2500 walletkit-core/", env!("CARGO_PKG_VERSION"), " ios/26.4.2");
"world_app_ios_client"
)]
#[test_case(
&UserAgentBuilder::new()
.with_app_segment_for_client("1.0.100", "android-id")
.with_walletkit_segment()
.with_client_segment("android-id", "15"),
concat!("WorldID/1.0.100 walletkit-core/", env!("CARGO_PKG_VERSION"), " android-id/15");
"world_id_android_client"
)]
#[test_case(
&UserAgentBuilder::new()
.with_app_segment_for_client("1.0.100", "ios-id")
.with_walletkit_segment()
.with_client_segment("ios-id", "26.4.2"),
concat!("WorldID/1.0.100 walletkit-core/", env!("CARGO_PKG_VERSION"), " ios-id/26.4.2");
"world_id_ios_client"
)]
fn user_agent_builder_expected(builder: &UserAgentBuilder, expected: &'static str) {
assert_eq!(builder.build().to_string(), expected);
}
#[test]
fn user_agent_exposes_header_value_for_ffi_consumers() {
let user_agent = UserAgentBuilder::new()
.with_app_segment_for_client("1.0.100", "android-id")
.with_walletkit_segment()
.with_client_segment("android-id", "15")
.build();
assert_eq!(
user_agent.header_value(),
concat!(
"WorldID/1.0.100 walletkit-core/",
env!("CARGO_PKG_VERSION"),
" android-id/15"
)
);
}
}