use crate::common::{ErrorCode, Reply};
use crate::message::{Message, Params};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum Kind {
#[default]
ToolProvider,
Mode,
ModelProvider,
Command,
Other(String),
}
impl Kind {
pub fn as_str(&self) -> &str {
match self {
Kind::ToolProvider => "tool-provider",
Kind::Mode => "mode",
Kind::ModelProvider => "model-provider",
Kind::Command => "command",
Kind::Other(s) => s,
}
}
pub fn from_name(s: &str) -> Self {
match s {
"tool-provider" => Kind::ToolProvider,
"mode" => Kind::Mode,
"model-provider" => Kind::ModelProvider,
"command" => Kind::Command,
o => Kind::Other(o.into()),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct Hello {
pub id: u32,
pub name: String,
pub kind: Kind,
pub caps: Vec<String>,
pub version: Option<u32>,
}
impl Hello {
pub fn new(id: u32, name: &str, kind: Kind, caps: &[&str]) -> Self {
Hello { id, name: name.into(), kind, caps: caps.iter().map(|c| c.to_string()).collect(), version: None }
}
pub fn encode(&self) -> Vec<u8> {
let mut p =
Params::new().with("id", self.id).with("name", &self.name).with("kind", self.kind.as_str()).with("caps", self.caps.join("+"));
if let Some(v) = self.version {
p.push("v", v);
}
Message::new(b'p', b'h', p).encode()
}
pub fn from_message(m: &Message) -> Option<Self> {
if !m.is(b'p', b'h') {
return None;
}
let p = &m.params;
Some(Hello {
id: p.num("id").filter(|&i| i > 0)?,
name: p.get("name").unwrap_or("").into(),
kind: p.get("kind").map(Kind::from_name).unwrap_or_default(),
caps: p.get("caps").unwrap_or("").split('+').filter(|c| !c.is_empty()).map(String::from).collect(),
version: p.num("v"),
})
}
}
pub fn query(id: u32, name: &str) -> Vec<u8> {
Message::new(b'p', b'q', Params::new().with("id", id).with("name", name)).encode()
}
pub fn bye(id: u32, name: &str) -> Vec<u8> {
Message::new(b'p', b'b', Params::new().with("id", id).with("name", name)).encode()
}
pub fn granted(id: u32, caps: &[String]) -> Vec<u8> {
Reply::ok(b'p', id, Params::new().with("granted", caps.join("+"))).encode()
}
pub fn denied(id: u32) -> Vec<u8> {
Reply::error(b'p', id, ErrorCode::Permission, "denied by the user").encode()
}
pub fn granted_caps(r: &Reply) -> Option<Vec<String>> {
if r.subsystem != b'p' || !r.is_ok() {
return None;
}
Some(r.params.get("granted").unwrap_or("").split('+').filter(|c| !c.is_empty()).map(String::from).collect())
}