machi_protocol/
tool_id.rs1use std::fmt;
4
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
9pub struct ToolId {
10 name: String,
11}
12
13impl ToolId {
14 #[must_use]
20 pub fn new(name: impl Into<String>) -> Option<Self> {
21 let name = name.into();
22 if name.trim().is_empty() {
23 return None;
24 }
25 Some(Self { name })
26 }
27
28 #[must_use]
34 pub fn const_new(name: &'static str) -> Self {
35 assert!(!name.is_empty(), "tool id must be non-empty");
36 Self {
37 name: name.to_owned(),
38 }
39 }
40
41 #[must_use]
43 pub fn as_str(&self) -> &str {
44 &self.name
45 }
46}
47
48impl fmt::Display for ToolId {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 f.write_str(&self.name)
51 }
52}
53
54impl AsRef<str> for ToolId {
55 fn as_ref(&self) -> &str {
56 &self.name
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63
64 #[test]
65 fn rejects_empty() {
66 assert!(ToolId::new("").is_none(), "empty rejected");
67 assert!(ToolId::new(" ").is_none(), "whitespace rejected");
68 }
69
70 #[test]
71 fn accepts_name() {
72 let id = ToolId::new("read_file").expect("id");
73 assert_eq!(id.as_str(), "read_file");
74 }
75}