use serde::{Deserialize, Serialize};
use crate::{error::Error, spec::BRASIL_API_URL};
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct Brand {
pub nome: String,
pub valor: String,
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct Vehicle {
pub valor: String,
pub marca: String,
pub modelo: String,
#[serde(rename = "anoModelo")]
pub ano_modelo: i64,
pub combustivel: String,
#[serde(rename = "codigoFipe")]
pub codigo_fipe: String,
#[serde(rename = "mesReferencia")]
pub mes_referencia: String,
#[serde(rename = "tipoVeiculo")]
pub tipo_veiculo: i64,
#[serde(rename = "siglaCombustivel")]
pub sigla_combustivel: String,
#[serde(rename = "dataConsulta")]
pub data_consulta: String,
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct ReferenceTable {
codigo: i32,
mes: String,
}
pub enum VehicleType {
Car,
Motorcycle,
Truck,
}
impl VehicleType {
pub fn to_string(&self) -> &str {
match self {
VehicleType::Car => "carros",
VehicleType::Motorcycle => "motos",
VehicleType::Truck => "caminhoes",
}
}
}
pub struct FipeService {
base_url: String,
}
impl FipeService {
pub fn new(base_url: &str) -> Self {
Self {
base_url: base_url.to_string(),
}
}
async fn get_brands_request(
&self,
vehicle_type: VehicleType,
reference_table: Option<i64>,
) -> Result<reqwest::Response, Error> {
let vehicle_type = vehicle_type.to_string();
let reference_table = match reference_table {
Some(reference_table) => format!("tabela_referencia={reference_table}"),
None => "".to_string(),
};
let url = format!(
"{}/api/fipe/marcas/v1/{}?{}",
self.base_url, vehicle_type, reference_table
);
match reqwest::get(&url).await {
Ok(response) => Error::from_response(response).await,
Err(e) => Err(Error::from_error(e)),
}
}
async fn get_vehicle_request(
&self,
fipe_code: &str,
reference_table: Option<i64>,
) -> Result<reqwest::Response, Error> {
let reference_table = match reference_table {
Some(reference_table) => format!("tabela_referencia={reference_table}"),
None => "".to_string(),
};
let url = format!(
"{}/api/fipe/preco/v1/{}?{}",
self.base_url, fipe_code, reference_table
);
match reqwest::get(&url).await {
Ok(response) => Error::from_response(response).await,
Err(e) => Err(Error::from_error(e)),
}
}
async fn get_reference_tables_request(&self) -> Result<reqwest::Response, Error> {
let url = format!("{}/api/fipe/tabelas/v1/", self.base_url);
match reqwest::get(&url).await {
Ok(response) => Error::from_response(response).await,
Err(e) => Err(Error::from_error(e)),
}
}
}
pub async fn get_brands(
vehicle_type: VehicleType,
reference_table: Option<i64>,
) -> Result<Vec<Brand>, Error> {
let fipe_service = FipeService::new(BRASIL_API_URL);
let response = fipe_service
.get_brands_request(vehicle_type, reference_table)
.await?;
let body = response.text().await.unwrap();
let brands: Vec<Brand> = serde_json::from_str(&body).unwrap();
Ok(brands)
}
pub async fn get_vehicles(
fipe_code: &str,
reference_table: Option<i64>,
) -> Result<Vec<Vehicle>, Error> {
let fipe_service = FipeService::new(BRASIL_API_URL);
let response = fipe_service
.get_vehicle_request(fipe_code, reference_table)
.await?;
let body = response.text().await.unwrap();
let vehicle: Vec<Vehicle> = serde_json::from_str(&body).unwrap();
Ok(vehicle)
}
pub async fn get_reference_tables() -> Result<Vec<ReferenceTable>, Error> {
let fipe_service = FipeService::new(BRASIL_API_URL);
let response = fipe_service.get_reference_tables_request().await?;
let body = response.text().await.unwrap();
let reference_tables: Vec<ReferenceTable> = serde_json::from_str(&body).unwrap();
Ok(reference_tables)
}
#[cfg(test)]
mod fipe_tests {
use super::*;
#[tokio::test]
async fn test_get_brands() {
let brands = get_brands(VehicleType::Car, None).await.unwrap();
assert!(!brands.is_empty());
}
#[tokio::test]
async fn test_get_vehicles() {
let vehicles = get_vehicles("008274-0", None).await.unwrap();
assert!(!vehicles.is_empty());
let vehicle = vehicles.first().unwrap();
assert_eq!(vehicle.marca, "Audi");
assert_eq!(vehicle.modelo, "RS E-TRON GT Quattro Aut. (Elétrico)");
}
}