git-gemini-forge 0.6.2

A simple Gemini server that serves a read-only view of public repositories from a Git forge.
use crate::constants::{PROJECT_NAME, PROJECT_VERSION, REPO_HTTP};
use const_str::verified_regex;
use std::sync::LazyLock;

/// Returns a user-agent string to send to upstream services.
pub fn user_agent() -> &'static str {
	static VERSION_STRING: LazyLock<String> = LazyLock::new(|| {
		// NOTE: This depends on the format for [`isahc::version`].
		let isahc_version = regex::Regex::new(verified_regex!(r"isahc/\d+\.\d+\.\d+"))
			.expect("Verified")
			.find(isahc::version())
			.expect("Version string")
			.as_str();

		// Note whether we're in debug mode
		let debug = if cfg!(debug_assertions) { "-debug" } else { "" };

		// e.g. "foo/0.0.0 (curl/0.0.0 isahc/0.0.0; +https://...)"
		// TODO: Add a way to use the production capsule instead, e.g. "+gemini://example.com)"
		let user_agent = format!(
			"{PROJECT_NAME}/{PROJECT_VERSION}{debug} (curl/{} {isahc_version}; +{REPO_HTTP})",
			curl::Version::get().version() // See https://github.com/sagebind/isahc/blob/5c533f1ef4d6bdf1fd291b5103c22110f41d0bf0/src/client.rs#L40
		);
		println!("User-Agent: {user_agent}");
		user_agent
	});

	&VERSION_STRING
}

// MARK: - Tests

#[cfg(test)]
mod tests {
	use super::*;
	use crate::{constants::Regex, network::user_agent};
	use regex_static::lazy_regex;

	// e.g. git-gemini-forge/0.6.0 (curl/8.1.0 isahc/1.7.2)
	const UA_REGEX: Regex = lazy_regex!(
		r"^git-gemini-forge/\d+\.\d+\.\d+(\-debug)? \(curl/\d+\.\d+\.\d+(-DEV)? isahc/\d+\.\d+\.\d+; \+https://[\w\./\-]+\)$"
	);

	#[test]
	fn test_user_agent_string_is_reasonable() {
		let actual = user_agent();
		assert!(UA_REGEX.is_match(actual), "Unexpected UA: {actual:?}");
		assert!(actual.contains(REPO_HTTP));
	}
}