Back to Examples
Rust
Integrate GuardCrow into your Rust applications using reqwest or other HTTP clients.
Basic Example
A simple example using reqwest:
main.rsrust
use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE};use serde::{Deserialize, Serialize};use std::env;#[derive(Serialize)]struct AnalyzeRequest {content: String,}#[derive(Deserialize, Debug)]#[serde(rename_all = "camelCase")]struct AnalyzeResponse {passed: bool,score: i32,sentiment: String,description: String,categories: Vec<String>,processing_time_ms: i32,}#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> {let api_key = env::var("GUARDCROW_API_KEY")?;let mut headers = HeaderMap::new();headers.insert("X-API-Key", HeaderValue::from_str(&api_key)?);headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));let client = reqwest::Client::new();let response = client.post("https://api.guardcrow.com/v1/analyze").headers(headers).json(&AnalyzeRequest {content: "Hello, this is a test message".to_string(),}).send().await?.json::<AnalyzeResponse>().await?;println!("Passed: {}, Score: {}", response.passed, response.score);Ok(())}
Complete Client Library
A production-ready client with error handling and retry logic:
src/guardcrow.rsrust
use reqwest::{Client, StatusCode};use serde::{Deserialize, Serialize};use std::collections::HashMap;use std::time::Duration;use thiserror::Error;#[derive(Error, Debug)]pub enum GuardCrowError {#[error("HTTP error: {0}")]Http(#[from] reqwest::Error),#[error("Rate limit exceeded")]RateLimited,#[error("Invalid API key")]InvalidApiKey,#[error("Content too long (max 10,000 characters)")]ContentTooLong,#[error("API error: {code} - {message}")]Api { code: String, message: String },}#[derive(Serialize)]pub struct AnalyzeRequest {pub content: String,#[serde(skip_serializing_if = "Option::is_none")]pub metadata: Option<HashMap<String, String>>,}#[derive(Deserialize, Debug, Clone)]#[serde(rename_all = "camelCase")]pub struct AnalyzeResponse {pub passed: bool,pub score: i32,pub sentiment: String,pub description: String,pub categories: Vec<String>,pub processing_time_ms: i32,}#[derive(Deserialize)]struct ErrorResponse {error: ApiError,}#[derive(Deserialize)]struct ApiError {code: String,message: String,}pub struct GuardCrowClient {client: Client,api_key: String,base_url: String,max_retries: u32,}impl GuardCrowClient {pub fn new(api_key: impl Into<String>) -> Self {Self {client: Client::builder().timeout(Duration::from_secs(30)).build().unwrap(),api_key: api_key.into(),base_url: "https://api.guardcrow.com/v1".to_string(),max_retries: 3,}}pub fn with_base_url(mut self, url: impl Into<String>) -> Self {self.base_url = url.into();self}pub fn with_max_retries(mut self, retries: u32) -> Self {self.max_retries = retries;self}pub async fn analyze(&self, content: impl Into<String>) -> Result<AnalyzeResponse, GuardCrowError> {self.analyze_with_metadata(content, None).await}pub async fn analyze_with_metadata(&self,content: impl Into<String>,metadata: Option<HashMap<String, String>>,) -> Result<AnalyzeResponse, GuardCrowError> {let request = AnalyzeRequest {content: content.into(),metadata,};let mut last_error = None;for attempt in 0..self.max_retries {match self.do_analyze(&request).await {Ok(response) => return Ok(response),Err(e) => {match &e {GuardCrowError::InvalidApiKey |GuardCrowError::ContentTooLong => return Err(e),_ => {last_error = Some(e);if attempt < self.max_retries - 1 {let backoff = Duration::from_secs(2u64.pow(attempt));tokio::time::sleep(backoff).await;}}}}}}Err(last_error.unwrap())}async fn do_analyze(&self, request: &AnalyzeRequest) -> Result<AnalyzeResponse, GuardCrowError> {let response = self.client.post(format!("{}/analyze", self.base_url)).header("X-API-Key", &self.api_key).header("Content-Type", "application/json").json(request).send().await?;match response.status() {StatusCode::OK => {Ok(response.json::<AnalyzeResponse>().await?)}StatusCode::TOO_MANY_REQUESTS => {Err(GuardCrowError::RateLimited)}StatusCode::UNAUTHORIZED => {Err(GuardCrowError::InvalidApiKey)}StatusCode::BAD_REQUEST => {let error_resp: ErrorResponse = response.json().await?;if error_resp.error.code == "CONTENT_TOO_LONG" {Err(GuardCrowError::ContentTooLong)} else {Err(GuardCrowError::Api {code: error_resp.error.code,message: error_resp.error.message,})}}_ => {let error_resp: ErrorResponse = response.json().await?;Err(GuardCrowError::Api {code: error_resp.error.code,message: error_resp.error.message,})}}}pub async fn health_check(&self) -> Result<bool, GuardCrowError> {let response = self.client.get(format!("{}/health", self.base_url)).send().await?;Ok(response.status() == StatusCode::OK)}}
Using the Client
src/main.rsrust
mod guardcrow;use guardcrow::GuardCrowClient;use std::collections::HashMap;use std::env;#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> {let api_key = env::var("GUARDCROW_API_KEY")?;let client = GuardCrowClient::new(api_key).with_max_retries(3);// Simple analysislet result = client.analyze("Hello, this is a test").await?;println!("Simple analysis - Passed: {}, Score: {}", result.passed, result.score);// Analysis with metadatalet mut metadata = HashMap::new();metadata.insert("source".to_string(), "user-comments".to_string());metadata.insert("user_id".to_string(), "user_123".to_string());let result = client.analyze_with_metadata("Check this content too", Some(metadata)).await?;println!("With metadata - Sentiment: {}", result.sentiment);println!("Description: {}", result.description);Ok(())}
Actix Web Integration
Create middleware for automatic content moderation in Actix Web:
src/main.rsrust
use actix_web::{web, App, HttpResponse, HttpServer, middleware};use serde::{Deserialize, Serialize};use std::sync::Arc;mod guardcrow;use guardcrow::GuardCrowClient;#[derive(Deserialize)]struct CommentRequest {content: String,user_id: String,}#[derive(Serialize)]struct CommentResponse {status: String,moderation_score: i32,}#[derive(Serialize)]struct ErrorResponse {error: String,reason: Option<String>,}async fn create_comment(client: web::Data<Arc<GuardCrowClient>>,req: web::Json<CommentRequest>,) -> HttpResponse {// Moderate the contentmatch client.analyze(&req.content).await {Ok(result) => {if result.score >= 7 {return HttpResponse::BadRequest().json(ErrorResponse {error: "Content blocked by moderation".to_string(),reason: Some(result.description),});}// Save comment (your logic here)HttpResponse::Created().json(CommentResponse {status: "created".to_string(),moderation_score: result.score,})}Err(e) => {eprintln!("Moderation error: {:?}", e);// Allow content through on API errorsHttpResponse::Created().json(CommentResponse {status: "created".to_string(),moderation_score: 0,})}}}#[actix_web::main]async fn main() -> std::io::Result<()> {let api_key = std::env::var("GUARDCROW_API_KEY").expect("GUARDCROW_API_KEY must be set");let client = Arc::new(GuardCrowClient::new(api_key));HttpServer::new(move || {App::new().app_data(web::Data::new(client.clone())).wrap(middleware::Logger::default()).route("/comments", web::post().to(create_comment))}).bind("127.0.0.1:8080")?.run().await}
Axum Integration
src/main.rsrust
use axum::{extract::State,http::StatusCode,response::IntoResponse,routing::post,Json, Router,};use serde::{Deserialize, Serialize};use std::sync::Arc;mod guardcrow;use guardcrow::GuardCrowClient;#[derive(Clone)]struct AppState {cs_client: Arc<GuardCrowClient>,}#[derive(Deserialize)]struct CommentRequest {content: String,}#[derive(Serialize)]struct CommentResponse {status: String,moderation_score: i32,}async fn create_comment(State(state): State<AppState>,Json(req): Json<CommentRequest>,) -> impl IntoResponse {match state.cs_client.analyze(&req.content).await {Ok(result) => {if result.score >= 7 {return (StatusCode::BAD_REQUEST,Json(serde_json::json!({"error": "Content blocked","reason": result.description})),);}(StatusCode::CREATED,Json(serde_json::json!({"status": "created","moderation_score": result.score})),)}Err(_) => (StatusCode::CREATED,Json(serde_json::json!({"status": "created","moderation_score": 0})),),}}#[tokio::main]async fn main() {let api_key = std::env::var("GUARDCROW_API_KEY").expect("GUARDCROW_API_KEY must be set");let state = AppState {cs_client: Arc::new(GuardCrowClient::new(api_key)),};let app = Router::new().route("/comments", post(create_comment)).with_state(state);let listener = tokio::net::TcpListener::bind("127.0.0.1:8080").await.unwrap();axum::serve(listener, app).await.unwrap();}
Cargo.toml Dependencies
Cargo.tomltoml
[dependencies]reqwest = { version = "0.11", features = ["json"] }serde = { version = "1.0", features = ["derive"] }serde_json = "1.0"tokio = { version = "1", features = ["full"] }thiserror = "1.0"# For Actix Webactix-web = "4"# For Axumaxum = "0.7"
Set your API key as an environment variable:
Terminalbash
export GUARDCROW_API_KEY=sk_live_your_api_key_here