pub const TYPESCRIPT_CLIENT_TEMPLATE: &str = r#"
// Generated by Kegani Client Generator
// DO NOT EDIT MANUALLY
export interface ApiClientOptions {
baseUrl: string;
authToken?: string;
timeout?: number;
}
export class KeganiApiClient {
private baseUrl: string;
private headers: Record<string, string>;
private timeout: number;
constructor(options: ApiClientOptions) {
this.baseUrl = options.baseUrl;
this.timeout = options.timeout || 30000;
this.headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
...(options.authToken && { 'Authorization': `Bearer ${options.authToken}` }),
};
}
private async request<T>(
method: string,
path: string,
body?: any,
params?: Record<string, string>
): Promise<T> {
const url = new URL(path, this.baseUrl);
if (params) {
Object.entries(params).forEach(([key, value]) => {
url.searchParams.append(key, value);
});
}
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), this.timeout);
try {
const response = await fetch(url.toString(), {
method,
headers: this.headers,
body: body ? JSON.stringify(body) : undefined,
signal: controller.signal,
});
if (!response.ok) {
const error = await response.text();
throw new ApiError(response.status, error);
}
return response.json();
} finally {
clearTimeout(timeout);
}
}
// User endpoints
async listUsers(params?: { page?: number; size?: number }): Promise<UserListResponse> {
return this.request('GET', '/api/users', undefined, params);
}
async getUser(id: string): Promise<User> {
return this.request('GET', `/api/users/${id}`);
}
async createUser(data: CreateUserInput): Promise<User> {
return this.request('POST', '/api/users', data);
}
async updateUser(id: string, data: UpdateUserInput): Promise<User> {
return this.request('PUT', `/api/users/${id}`, data);
}
async deleteUser(id: string): Promise<void> {
return this.request('DELETE', `/api/users/${id}`);
}
}
export class ApiError extends Error {
constructor(public status: number, message: string) {
super(message);
this.name = 'ApiError';
}
}
// Types
export interface User {
id: string;
email: string;
name: string;
createdAt: string;
}
export interface CreateUserInput {
email: string;
name: string;
password: string;
}
export interface UpdateUserInput {
name?: string;
}
export interface UserListResponse {
items: User[];
page: number;
size: number;
total: number;
}
export default KeganiApiClient;
"#;
pub const RUST_CLIENT_TEMPLATE: &str = r#"
//! Generated by Kegani Client Generator
//! DO NOT EDIT MANUALLY
use reqwest::{Client, Error, Response};
use serde::{Deserialize, Serialize};
use std::time::Duration;
/// API client configuration
#[derive(Debug, Clone)]
pub struct ApiClientConfig {
pub base_url: String,
pub auth_token: Option<String>,
pub timeout: Duration,
}
impl Default for ApiClientConfig {
fn default() -> Self {
Self {
base_url: "http://localhost:8080".to_string(),
auth_token: None,
timeout: Duration::from_secs(30),
}
}
}
/// Kegani API client
#[derive(Clone)]
pub struct KeganiApiClient {
client: Client,
config: ApiClientConfig,
}
impl KeganiApiClient {
/// Create a new client
pub fn new(config: ApiClientConfig) -> Self {
let client = Client::builder()
.timeout(config.timeout)
.build()
.unwrap_or_else(|_| Client::new());
Self { client, config }
}
/// Create with auth token
pub fn with_auth(token: &str) -> Self {
Self::new(ApiClientConfig {
auth_token: Some(token.to_string()),
..Default::default()
})
}
/// Make a request
async fn request<T: for<'de> Deserialize<'de>>(
&self,
method: &str,
path: &str,
body: Option<&impl Serialize>,
) -> Result<T, ApiError> {
let url = format!("{}{}", self.config.base_url, path);
let mut request = match method {
"GET" => self.client.get(&url),
"POST" => self.client.post(&url),
"PUT" => self.client.put(&url),
"DELETE" => self.client.delete(&url),
"PATCH" => self.client.patch(&url),
_ => return Err(ApiError::InvalidMethod(method.to_string())),
};
// Add auth header
if let Some(token) = &self.config.auth_token {
request = request.header("Authorization", format!("Bearer {}", token));
}
// Add body
if let Some(body) = body {
request = request.json(body);
}
let response = request.send().await?;
Self::handle_response(response).await
}
async fn handle_response<T: for<'de> Deserialize<'de>>(
response: Response,
) -> Result<T, ApiError> {
let status = response.status();
if status.is_success() {
response.json().await.map_err(ApiError::Request)
} else {
let text = response.text().await.unwrap_or_default();
Err(ApiError::Server(status.as_u16(), text))
}
}
// User endpoints
pub async fn list_users(&self, page: Option<u32>, size: Option<u32>) -> Result<UserListResponse, ApiError> {
let mut query = vec![];
if let Some(p) = page { query.push(format!("page={}", p)); }
if let Some(s) = size { query.push(format!("size={}", s)); }
let path = if query.is_empty() {
"/api/users".to_string()
} else {
format!("/api/users?{}", query.join("&"))
};
self.request("GET", &path, None::<&()>).await
}
pub async fn get_user(&self, id: &str) -> Result<User, ApiError> {
self.request("GET", &format!("/api/users/{}", id), None::<&()>).await
}
pub async fn create_user(&self, input: &CreateUserInput) -> Result<User, ApiError> {
self.request("POST", "/api/users", Some(input)).await
}
pub async fn update_user(&self, id: &str, input: &UpdateUserInput) -> Result<User, ApiError> {
self.request("PUT", &format!("/api/users/{}", id), Some(input)).await
}
pub async fn delete_user(&self, id: &str) -> Result<(), ApiError> {
let _: serde_json::Value = self.request("DELETE", &format!("/api/users/{}", id), None::<&()>).await?;
Ok(())
}
}
/// API error
#[derive(Debug)]
pub enum ApiError {
Request(reqwest::Error),
Server(u16, String),
InvalidMethod(String),
}
impl std::fmt::Display for ApiError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ApiError::Request(e) => write!(f, "Request error: {}", e),
ApiError::Server(status, msg) => write!(f, "Server error {}: {}", status, msg),
ApiError::InvalidMethod(m) => write!(f, "Invalid method: {}", m),
}
}
}
impl std::error::Error for ApiError {}
impl From<reqwest::Error> for ApiError {
fn from(e: reqwest::Error) -> Self {
ApiError::Request(e)
}
}
// Types
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
pub id: String,
pub email: String,
pub name: String,
#[serde(rename = "createdAt")]
pub created_at: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateUserInput {
pub email: String,
pub name: String,
pub password: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateUserInput {
pub name: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserListResponse {
pub items: Vec<User>,
pub page: u32,
pub size: u32,
pub total: i64,
}
"#;