1use reqwest::Client as HttpClient;
4use serde::{Deserialize, Serialize};
5use std::time::Duration;
6use thiserror::Error;
7
8#[derive(Error, Debug)]
9pub enum MantrError {
10 #[error("Authentication failed: invalid API key")]
11 Authentication,
12
13 #[error("Insufficient credits")]
14 InsufficientCredits,
15
16 #[error("Rate limit exceeded")]
17 RateLimit,
18
19 #[error("API error: {0}")]
20 Api(String),
21
22 #[error("Network error: {0}")]
23 Network(#[from] reqwest::Error),
24}
25
26#[derive(Debug, Serialize)]
27pub struct WalkRequest {
28 pub phonemes: Vec<String>,
29 #[serde(skip_serializing_if = "Option::is_none")]
30 pub pod: Option<String>,
31 #[serde(skip_serializing_if = "Option::is_none")]
32 pub depth: Option<u32>,
33 #[serde(skip_serializing_if = "Option::is_none")]
34 pub limit: Option<u32>,
35}
36
37#[derive(Debug, Deserialize)]
38pub struct PathResult {
39 pub nodes: Vec<String>,
40 pub score: f64,
41 pub depth: u32,
42}
43
44#[derive(Debug, Deserialize)]
45pub struct WalkResponse {
46 pub paths: Vec<PathResult>,
47 pub latency_us: u64,
48 pub credits_used: u32,
49}
50
51pub struct MantrClient {
53 api_key: String,
54 base_url: String,
55 client: HttpClient,
56}
57
58impl MantrClient {
59 pub fn new(api_key: impl Into<String>) -> Result<Self, MantrError> {
71 let api_key = api_key.into();
72
73 if !api_key.starts_with("vak_") {
74 return Err(MantrError::Api(
75 "Invalid API key format. Must start with 'vak_'".to_string(),
76 ));
77 }
78
79 Ok(Self {
80 api_key,
81 base_url: "https://api.mantr.net".to_string(),
82 client: HttpClient::builder()
83 .timeout(Duration::from_secs(30))
84 .build()?,
85 })
86 }
87
88 pub async fn walk(&self, request: WalkRequest) -> Result<WalkResponse, MantrError> {
110 let resp = self
111 .client
112 .post(format!("{}/v1/walk", self.base_url))
113 .header("Authorization", format!("Bearer {}", self.api_key))
114 .header("User-Agent", "mantr-rust/1.0.0")
115 .json(&request)
116 .send()
117 .await?;
118
119 match resp.status().as_u16() {
120 401 => Err(MantrError::Authentication),
121 402 => Err(MantrError::InsufficientCredits),
122 429 => Err(MantrError::RateLimit),
123 200 => Ok(resp.json().await?),
124 status => Err(MantrError::Api(format!("HTTP {}", status))),
125 }
126 }
127}