use crate::client::Skailar;
use crate::error::Error;
use crate::types::models::{Model, ModelList, ModelSummary};
#[derive(Debug, Clone, Copy)]
pub struct Models<'a> {
client: &'a Skailar,
}
impl<'a> Models<'a> {
pub(crate) fn new(client: &'a Skailar) -> Self {
Models { client }
}
pub async fn list(&self) -> Result<Vec<ModelSummary>, Error> {
let list: ModelList = self.client.get_json("v1/models").await?;
Ok(list.data)
}
pub async fn retrieve(&self, id: impl AsRef<str>) -> Result<Model, Error> {
let path = format!("v1/models/{}", encode_path(id.as_ref()));
self.client.get_json(&path).await
}
}
fn encode_path(id: &str) -> String {
id.split('/')
.map(encode_segment)
.collect::<Vec<_>>()
.join("/")
}
fn encode_segment(segment: &str) -> String {
let mut out = String::with_capacity(segment.len());
for byte in segment.bytes() {
match byte {
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
out.push(byte as char);
}
_ => out.push_str(&format!("%{byte:02X}")),
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn keeps_slashes_encodes_specials() {
assert_eq!(
encode_path("google/gemini-2.5-pro"),
"google/gemini-2.5-pro"
);
assert_eq!(encode_path("a b"), "a%20b");
assert_eq!(encode_path("ns/a b"), "ns/a%20b");
}
}