1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
//! Parser and validator for the `agent://` URI scheme.
//!
//! This crate implements parsing, validation, and serialization of agent URIs
//! as defined in the Agent Identity URI Scheme specification.
//!
//! # Overview
//!
//! Agent URIs provide topology-independent identity for agents with
//! capability-based discovery. They have the structure:
//!
//! ```text
//! agent://<trust-root>/<capability-path>/<agent-id>[?query][#fragment]
//! ```
//!
//! # Quick Start
//!
//! ```rust
//! use agent_uri::AgentUri;
//!
//! // Parse an agent URI
//! let uri = AgentUri::parse(
//! "agent://anthropic.com/assistant/chat/llm_chat_01h455vb4pex5vsknk084sn02q"
//! ).unwrap();
//!
//! // Access components
//! assert_eq!(uri.trust_root().host_str(), "anthropic.com");
//! assert_eq!(uri.capability_path().as_str(), "assistant/chat");
//! assert_eq!(uri.agent_id().prefix().as_str(), "llm_chat");
//!
//! // Create new agent IDs
//! use agent_uri::AgentId;
//! let id = AgentId::new("llm_chat");
//! println!("New agent: {}", id);
//!
//! // `new` sanitizes a prefix the grammar refuses. Where the prefix did not
//! // come from your own source, use `try_new`, which reports it instead.
//! assert_eq!(AgentId::new("LLM Chat!").prefix().as_str(), "llmchat");
//! assert!(AgentId::try_new("LLM Chat!").is_err());
//! ```
//!
//! # Builder Pattern
//!
//! Use the typestate builder for compile-time enforced construction:
//!
//! ```rust
//! use agent_uri::{AgentUriBuilder, TrustRoot, CapabilityPath, AgentId};
//!
//! let uri = AgentUriBuilder::new()
//! .trust_root(TrustRoot::parse("anthropic.com").unwrap())
//! .capability_path(CapabilityPath::parse("assistant/chat").unwrap())
//! .agent_id(AgentId::new("llm_chat"))
//! .build()
//! .unwrap();
//!
//! assert_eq!(uri.trust_root().host_str(), "anthropic.com");
//! ```
//!
//! # Length Constraints
//!
//! | Component | Max Length |
//! |-----------|------------|
//! | Total URI | 512 chars |
//! | Trust root | 128 chars |
//! | Capability path | 256 chars |
//! | Path segments | 32 max count |
//! | Each segment | 64 chars |
//! | Agent ID prefix | 63 chars |
//! | Agent ID suffix | 26 chars (fixed) |
//!
//! # Grammar Specification
//!
//! This crate implements the ABNF grammar defined in `grammar.abnf` at the crate root.
//! The grammar follows RFC 5234 (ABNF) and specifies:
//!
//! - **URI structure**: `agent://<trust-root>/<capability-path>/<agent-id>[?query][#fragment]`
//! - **Trust root**: Domain names, IPv4, and IPv6 addresses with optional ports
//! - **Capability path**: Hierarchical path with 1-32 segments
//! - **Agent ID**: `TypeID` format with semantic prefix and `UUIDv7` suffix
//!
//! See `grammar.abnf` for the complete formal specification.
//!
//! # Breaking changes in 0.6.0
//!
//! Two changes, neither of which affects a caller who only propagates errors.
//!
//! ## Error enums are `#[non_exhaustive]`
//!
//! An exhaustive `match` on [`ParseErrorKind`], [`TrustRootError`],
//! [`CapabilityPathError`], [`PathSegmentError`], [`AgentIdError`],
//! [`AgentPrefixError`], [`QueryError`], [`FragmentError`], [`BuilderError`],
//! or [`TypeClassError`] now needs a `_` arm.
//!
//! A parser learns to be more specific over time: a rejection reported today as
//! a generic bad character turns out to deserve a variant of its own, and
//! naming it is an improvement a caller wants. On an exhaustive enum that
//! improvement is a major version bump forever, so past 1.0 it stops happening
//! and the errors stay vaguer than they need to be.
//!
//! The wildcard arm should say what to do about a variant this version does not
//! recognise, rather than treat its absence as impossible:
//!
//! ```
//! use agent_uri::{AgentUri, ParseErrorKind};
//!
//! let error = AgentUri::parse("").unwrap_err();
//! let advice = match error.kind {
//! ParseErrorKind::Empty => "supply a URI",
//! ParseErrorKind::TooLong { .. } => "shorten it",
//! _ => "check the URI against the specification",
//! };
//! assert_eq!(advice, "supply a URI");
//! ```
//!
//! Matching on a specific variant, which is the common case, is unaffected:
//! `matches!`, `if let`, and `Result::is_err` all read the same as before.
//!
//! ## `TypeClass` parsing reports a real error
//!
//! [`ExtensionClass::new`] and `<TypeClass as FromStr>::Err` return
//! [`TypeClassError`] instead of `&'static str`, so a type-class rejection is a
//! [`std::error::Error`] that composes with `?` and names the offending
//! character and its position. [`AgentPrefixError::InvalidTypeClass`]'s
//! `reason` field carries it too.
pub use AgentId;
pub use AgentPrefix;
pub use ;
pub use CapabilityPath;
pub use ;
pub use ;
pub use Fragment;
pub use PathSegment;
pub use QueryParams;
pub use ;
pub use ;
pub use AgentUri;