Skip to main content

agent_first_http/shared/
ids.rs

1//! Newtype wrappers for identifiers used across the protocol — request IDs
2//! returned to the agent, tab IDs scoped to a host's browser, and profile
3//! names. Each type owns its own validation/generation so we can't accidentally
4//! pass a profile name where a tab ID is expected.
5
6use serde::{Deserialize, Serialize};
7
8/// A per-fetch identifier, generated by the SDK on each `Client::fetch(...)`.
9/// Goes into the response `request_id` field and into the default `--out`
10/// path (`$TMPDIR/afhttp-out/<request_id>/`, platform equivalent).
11#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
12#[serde(transparent)]
13pub struct RequestId(pub String);
14
15impl RequestId {
16    #[must_use]
17    pub fn new_v4() -> Self {
18        Self(uuid::Uuid::new_v4().to_string())
19    }
20
21    #[must_use]
22    pub fn as_str(&self) -> &str {
23        &self.0
24    }
25}
26
27impl std::fmt::Display for RequestId {
28    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29        f.write_str(&self.0)
30    }
31}
32
33/// A browser tab identifier, assigned by the host's CDP target manager.
34/// Architecture.md `§5` (`--tab new|<id>`) makes this the unit of fetch
35/// affinity for reusing browser state across requests.
36#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
37#[serde(transparent)]
38pub struct TabId(pub String);
39
40impl TabId {
41    #[must_use]
42    pub fn new(id: impl Into<String>) -> Self {
43        Self(id.into())
44    }
45
46    #[must_use]
47    pub fn as_str(&self) -> &str {
48        &self.0
49    }
50}
51
52impl std::fmt::Display for TabId {
53    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54        f.write_str(&self.0)
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn request_id_is_uuid_v4_shaped() {
64        let id = RequestId::new_v4();
65        assert_eq!(id.as_str().len(), 36); // 8-4-4-4-12
66        assert_eq!(id.as_str().matches('-').count(), 4);
67    }
68
69    #[test]
70    fn ids_serialize_as_bare_strings() {
71        let rid = RequestId("abc".into());
72        let tid = TabId("page-42".into());
73        assert_eq!(serde_json::to_string(&rid).unwrap_or_default(), "\"abc\"");
74        assert_eq!(
75            serde_json::to_string(&tid).unwrap_or_default(),
76            "\"page-42\""
77        );
78    }
79}