use crate::error::Error;
use reqwest;
use serde::Deserialize;
use std::{collections::VecDeque, env};
const ENV_API_KEY: &str = "CONGRESS_GOV_API_KEY";
const ROOT_URL: &str = "https://api.congress.gov/v3";
const MEMBERS_PATH: &str = "/member";
const API_KEY_URL_PARAM: &str = "api_key=";
#[derive(Clone, Debug, Deserialize)]
pub struct Depiction {
#[serde(rename = "attribution")]
pub attribution: Option<String>,
#[serde(rename = "imageUrl")]
pub image_url: Option<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct Pagination {
#[serde(rename = "count")]
pub count: i32,
#[serde(rename = "next")]
pub next: Option<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct TermItem {
#[serde(rename = "chamber")]
pub chamber: String,
#[serde(rename = "endYear")]
pub end_year: Option<i32>,
#[serde(rename = "startYear")]
pub start_year: i32,
}
#[derive(Clone, Debug, Deserialize)]
pub struct Terms {
pub item: Vec<TermItem>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct Member {
#[serde(rename = "bioguideId")]
pub bioguide_id: String,
#[serde(rename = "depiction")]
pub depiction: Option<Depiction>,
#[serde(rename = "district")]
pub district: Option<i32>,
#[serde(rename = "name")]
pub name: String,
#[serde(rename = "partyName")]
pub party_name: String,
#[serde(rename = "state")]
pub state: String,
#[serde(rename = "terms")]
pub terms: Terms,
#[serde(rename = "updateDate")]
pub update_date: String,
#[serde(rename = "url")]
pub url: String,
}
impl Member {
pub fn split_name(&self) -> Result<(String, Option<String>, String), Error> {
let first_name: &str;
let middle_name: Option<String>;
if !self.name.contains(",") {
return Err(Error::NameSplitError(format!(
"The name {0} is not in the expected format",
self.name
)));
}
let mut parts: VecDeque<&str> = self.name.split(',').collect();
if parts.len() < 2 {
return Err(Error::NameSplitError(format!(
"The name {0} did not split into the expected number of segments",
self.name
)));
}
let last_name_option = parts.pop_front();
let second_segment_option = parts.pop_front();
let last_name: &str = match last_name_option {
Some(last) => last,
None => {
return Err(Error::NameSplitError(String::from(
"Failed to obtain the last name.",
)));
}
};
let second_segment = match second_segment_option {
Some(second_segment) => second_segment.trim_start(),
None => {
return Err(Error::NameSplitError(format!(
"The name {0} did not contain a first or middle name",
self.name
)));
}
};
if second_segment.contains(' ') {
let space_index = second_segment.find(' ').unwrap();
let (first, middle) = second_segment.split_at(space_index);
first_name = first;
middle_name = if middle.is_empty() {
None
} else {
Some(String::from(middle))
};
} else {
first_name = second_segment;
middle_name = None;
}
if first_name.is_empty() {
Err(Error::NameSplitError(String::from(
"Failed to parse the first name segment.",
)))
} else if last_name.is_empty() {
Err(Error::NameSplitError(String::from(
"Failed to parse the last name segment.",
)))
} else {
Ok((
String::from(first_name),
middle_name,
String::from(last_name),
))
}
} }
#[derive(Clone, Debug, Deserialize)]
pub struct MembersMessage {
#[serde(rename = "members")]
pub members: Vec<Member>,
#[serde(rename = "pagination")]
pub pagination: Pagination,
}
pub async fn get_members(offset: i32, limit: i32) -> Result<Vec<Member>, Error> {
let api_key = match env::var(ENV_API_KEY) {
Ok(key) => key,
Err(e) => return Err(Error::CongressGovApiKeyError(e.to_string())),
};
let url = format!(
"{ROOT_URL}{MEMBERS_PATH}?{API_KEY_URL_PARAM}{api_key}&offset={offset}&limit={limit}"
);
match reqwest::get(url).await {
Ok(response) => {
match response.text().await {
Ok(body) => {
match serde_json::from_str::<MembersMessage>(&body) {
Ok(member_struct) => Ok(member_struct.members),
Err(e) => {
Err(Error::CongressGovResponseError(
format!(
"{}\nFailed to deserialize the response body into a MembersMessage structure: {}",
body,
e
)
))
},
}
},
Err(e) => Err(Error::CongressGovResponseError(e.to_string())),
}
},
Err(e) => Err(Error::CongressGovHttpGetError(e.to_string())),
}
}