From 33bdf088048030865e2e2e92aacd0df1588bdec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Tue, 10 Dec 2024 10:35:19 +0100 Subject: [PATCH] Add FromStr impl for UserAgent (#5236) * Add FromStr impl for UserAgent * Convert error type to struct --- common/http-api-client/src/user_agent.rs | 114 ++++++++++++++++++++++- 1 file changed, 113 insertions(+), 1 deletion(-) diff --git a/common/http-api-client/src/user_agent.rs b/common/http-api-client/src/user_agent.rs index d47cb570c4..eeec96e424 100644 --- a/common/http-api-client/src/user_agent.rs +++ b/common/http-api-client/src/user_agent.rs @@ -1,7 +1,7 @@ // Copyright 2024 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use std::fmt; +use std::{fmt, str::FromStr}; use http::HeaderValue; use nym_bin_common::build_information::{BinaryBuildInformation, BinaryBuildInformationOwned}; @@ -15,6 +15,36 @@ pub struct UserAgent { pub git_commit: String, } +#[derive(Clone, Debug, thiserror::Error)] +#[error("invalid user agent string: {0}")] +pub struct UserAgentError(String); + +impl FromStr for UserAgent { + type Err = UserAgentError; + + fn from_str(s: &str) -> Result { + let parts: Vec<&str> = s.split('/').collect(); + if parts.len() != 4 { + return Err(UserAgentError(s.to_string())); + } + + Ok(UserAgent { + application: parts[0].to_string(), + version: parts[1].to_string(), + platform: parts[2].to_string(), + git_commit: parts[3].to_string(), + }) + } +} + +impl TryFrom<&str> for UserAgent { + type Error = UserAgentError; + + fn try_from(s: &str) -> Result { + UserAgent::from_str(s) + } +} + impl fmt::Display for UserAgent { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let abbreviated_commit = self.git_commit.chars().take(7).collect::(); @@ -55,3 +85,85 @@ impl From for UserAgent { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parsing_valid_user_agent() { + let user_agent = "nym-mixnode/0.11.0/x86_64-unknown-linux-gnu/abcdefg"; + let parsed = UserAgent::from_str(user_agent).unwrap(); + assert_eq!( + parsed, + UserAgent { + application: "nym-mixnode".to_string(), + version: "0.11.0".to_string(), + platform: "x86_64-unknown-linux-gnu".to_string(), + git_commit: "abcdefg".to_string() + } + ); + } + + #[test] + fn parsing_invalid_user_agent() { + let user_agent = "nym-mixnode/0.11.0/x86_64-unknown-linux-gnu"; + assert!(UserAgent::from_str(user_agent).is_err()); + } + + #[test] + fn converting_user_agent_to_string() { + let user_agent = UserAgent { + application: "nym-mixnode".to_string(), + version: "0.11.0".to_string(), + platform: "x86_64-unknown-linux-gnu".to_string(), + git_commit: "abcdefg".to_string(), + }; + + assert_eq!( + user_agent.to_string(), + "nym-mixnode/0.11.0/x86_64-unknown-linux-gnu/abcdefg" + ); + } + + #[test] + fn converting_user_agent_to_display() { + let user_agent = UserAgent { + application: "nym-mixnode".to_string(), + version: "0.11.0".to_string(), + platform: "x86_64-unknown-linux-gnu".to_string(), + git_commit: "abcdefg".to_string(), + }; + + assert_eq!( + format!("{}", user_agent), + "nym-mixnode/0.11.0/x86_64-unknown-linux-gnu/abcdefg" + ); + } + + #[test] + fn converting_user_agent_to_header_value_fails() { + let user_agent = UserAgent { + application: "nym-mixnode".to_string(), + version: "0.11.0".to_string(), + platform: "x86_64-unknown-linux-gnu".to_string(), + git_commit: "abcdefg".to_string(), + }; + + let header_value: Result = user_agent.clone().try_into(); + assert!(header_value.is_ok()); + } + + #[test] + fn converting_user_agent_to_header_value_has_same_string_representation() { + let user_agent = UserAgent { + application: "nym-mixnode".to_string(), + version: "0.11.0".to_string(), + platform: "x86_64-unknown-linux-gnu".to_string(), + git_commit: "abcdefg".to_string(), + }; + + let header_value: HeaderValue = user_agent.clone().try_into().unwrap(); + assert_eq!(header_value.to_str().unwrap(), user_agent.to_string()); + } +}