#[ cfg( feature = "curl-diagnostics" ) ]
mod private
{
use std::collections::HashMap;
use serde_json::Value;
pub trait AsCurl
{
fn as_curl( &self, url : &str ) -> String;
}
pub trait AsCurlClient
{
fn as_curl_for_request< T : AsCurl + serde::Serialize >( &self, request : &T, url : &str ) -> String;
}
#[ derive( Debug ) ]
pub struct CurlBuilder
{
method : String,
url : String,
headers : HashMap< String, String >,
body : Option< String >,
options : Vec< String >,
}
impl CurlBuilder
{
#[ inline ]
#[ must_use ]
pub fn new( url : impl Into< String > ) -> Self
{
Self
{
method : "GET".to_string(),
url : url.into(),
headers : HashMap::new(),
body : None,
options : Vec::new(),
}
}
#[ inline ]
#[ must_use ]
pub fn method( mut self, method : impl Into< String > ) -> Self
{
self.method = method.into();
self
}
#[ inline ]
#[ must_use ]
pub fn header( mut self, key : impl Into< String >, value : impl Into< String > ) -> Self
{
self.headers.insert( key.into(), value.into() );
self
}
#[ inline ]
#[ must_use ]
pub fn body( mut self, body : impl Into< String > ) -> Self
{
self.body = Some( body.into() );
self
}
#[ inline ]
#[ must_use ]
pub fn option( mut self, option : impl Into< String > ) -> Self
{
self.options.push( option.into() );
self
}
#[ must_use ]
pub fn build( &self ) -> String
{
let mut parts = vec![ "curl".to_string() ];
if self.method != "GET"
{
parts.push( format!( "--request {}", self.method ) );
}
parts.push( format!( "\"{}\"", self.url ) );
for ( key, value ) in &self.headers
{
parts.push( format!( "--header \"{}: {}\"", escape_header_value( key ), escape_header_value( value ) ) );
}
if let Some( ref body ) = self.body
{
parts.push( format!( "--data '{}'", escape_json_body( body ) ) );
}
for option in &self.options
{
parts.push( option.clone() );
}
if parts.len() > 4 || parts.iter().map( std::string::String::len ).sum::< usize >() > 120
{
parts.join( " \\\n " )
}
else
{
parts.join( " " )
}
}
}
fn escape_header_value( value : &str ) -> String
{
value
.replace( '\\', "\\\\" )
.replace( '"', "\\\"" )
}
fn escape_json_body( body : &str ) -> String
{
body.replace( '\'', "'\"'\"'" )
}
fn format_json_for_curl( value : &Value ) -> String
{
serde_json::to_string( value ).unwrap_or_else( |_| "{}".to_string() )
}
impl AsCurl for crate::CreateMessageRequest
{
fn as_curl( &self, url : &str ) -> String
{
let mut builder = CurlBuilder::new( url )
.method( "POST" )
.header( "Content-Type", "application/json" );
#[ cfg( feature = "streaming" ) ]
if self.stream.unwrap_or( false )
{
builder = builder.option( "--no-buffer".to_string() );
}
if let Ok( json ) = serde_json::to_value( self )
{
let json_string = format_json_for_curl( &json );
builder = builder.body( json_string );
}
builder.build()
}
}
impl AsCurlClient for crate::Client
{
fn as_curl_for_request< T : AsCurl + serde::Serialize >( &self, request : &T, url : &str ) -> String
{
let base_curl = request.as_curl( url );
let mut builder = CurlBuilder::new( url )
.method( "POST" )
.header( "Content-Type", "application/json" );
if let Some( api_key ) = self.api_key()
{
builder = builder.header( "x-api-key", &api_key.ANTHROPIC_API_KEY );
}
builder = builder.header( "anthropic-version", "2023-06-01" );
builder = builder.header( "User-Agent", format!( "api_claude/{}", env!( "CARGO_PKG_VERSION" ) ) );
if let Ok( json ) = serde_json::to_value( request )
{
let json_string = format_json_for_curl( &json );
builder = builder.body( json_string );
}
if base_curl.contains( "--no-buffer" )
{
builder = builder.option( "--no-buffer".to_string() );
}
builder.build()
}
}
}
#[ cfg( feature = "curl-diagnostics" ) ]
crate::mod_interface!
{
exposed use
{
AsCurl,
AsCurlClient,
CurlBuilder,
};
}