agent_first_http/cli/
connect.rs1use agent_first_data::ValueSource;
15use agent_first_data::value_source::SecretString;
16
17use crate::cli::token_source;
18use crate::sdk::Client;
19use crate::shared::error::Error;
20
21#[derive(Debug, Clone, Default)]
23pub struct Connection {
24 pub endpoint: Option<String>,
26 pub token: Option<ValueSource>,
29}
30
31#[derive(Debug, Clone)]
33pub struct Resolved {
34 pub endpoint: String,
35 pub token: Option<SecretString>,
36}
37
38impl Connection {
39 pub fn new(endpoint: Option<String>, token: Option<ValueSource>) -> Self {
40 Self { endpoint, token }
41 }
42
43 pub fn is_explicit(&self) -> bool {
47 self.endpoint.is_some()
48 }
49
50 pub async fn resolve(&self) -> Result<Resolved, Error> {
56 let token = self.token.as_ref().map(token_source::read).transpose()?;
57 match self.endpoint.as_deref() {
58 Some(endpoint) => Ok(Resolved {
59 endpoint: endpoint.to_string(),
60 token,
61 }),
62 None => {
63 let host = crate::cli::cmd::container::discover_default_local_host(
64 token.as_ref().map(SecretString::expose_secret),
65 )
66 .await?;
67 Ok(Resolved {
68 endpoint: host.endpoint,
69 token: host.token_secret,
70 })
71 }
72 }
73 }
74
75 pub async fn client(&self) -> Result<Client, Error> {
78 self.resolve().await?.client()
79 }
80}
81
82impl Resolved {
83 pub fn client(&self) -> Result<Client, Error> {
84 let client = Client::connect(&self.endpoint)?;
85 Ok(match &self.token {
86 Some(token) => client.with_token(token.expose_secret()),
87 None => client,
88 })
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[tokio::test]
97 async fn a_named_endpoint_resolves_without_discovery() {
98 let connection = Connection::new(
101 Some("ws://127.0.0.1:9222".to_string()),
102 Some(ValueSource::Literal("t0ken".to_string())),
103 );
104 assert!(connection.is_explicit());
105 let resolved = connection.resolve().await.expect("explicit host resolves");
106 assert_eq!(resolved.endpoint, "ws://127.0.0.1:9222");
107 assert_eq!(
108 resolved.token.as_ref().map(SecretString::expose_secret),
109 Some("t0ken")
110 );
111 }
112
113 #[tokio::test]
114 async fn an_unreadable_token_source_fails_before_the_host_is_touched() {
115 let connection = Connection::new(
116 Some("ws://127.0.0.1:9222".to_string()),
117 Some(ValueSource::Env("AFHTTP_TEST_ABSENT_TOKEN".to_string())),
118 );
119 let error = connection.resolve().await.expect_err("unset env source");
120 assert!(error.detail.contains("--token-secret"), "{}", error.detail);
121 }
122}