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 analysis
let result = client.analyze("Hello, this is a test").await?;
println!("Simple analysis - Passed: {}, Score: {}", result.passed, result.score);
// Analysis with metadata
let 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 content
match 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 errors
HttpResponse::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 Web
actix-web = "4"
# For Axum
axum = "0.7"

Set your API key as an environment variable:

Terminalbash
export GUARDCROW_API_KEY=sk_live_your_api_key_here