use serde::Deserialize;
use super::classify::PublisherModel;
const PAGE_SIZE: &str = "300";
const MAX_PAGES: usize = 20;
#[derive(Debug, thiserror::Error)]
pub enum ListingError {
#[error("listing request failed: {0}")]
Request(#[source] reqwest::Error),
#[error("listing returned an unreadable body: {0}")]
Body(#[source] reqwest::Error),
#[error("listing returned {status}: {body}")]
Status {
status: reqwest::StatusCode,
body: String,
},
#[error("listing returned an unreadable body: {0}")]
Decode(#[from] serde_json::Error),
#[error(
"listing did not terminate after {} pages; {read} models read are discarded",
MAX_PAGES
)]
Unterminated { read: usize },
}
#[derive(Debug, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
struct ListPage {
#[serde(default)]
publisher_models: Vec<PublisherModel>,
#[serde(default)]
next_page_token: Option<String>,
}
pub async fn list_publisher_models(
http: &reqwest::Client,
host: &str,
token: &str,
publisher: &str,
) -> Result<Vec<PublisherModel>, ListingError> {
let url = format!(
"{}/v1beta1/publishers/{publisher}/models",
host.trim_end_matches('/')
);
let mut models = Vec::new();
let mut page_token: Option<String> = None;
for _ in 0..MAX_PAGES {
let mut request = http
.get(&url)
.bearer_auth(token)
.query(&[("pageSize", PAGE_SIZE)]);
if let Some(token) = page_token.as_deref() {
request = request.query(&[("pageToken", token)]);
}
let response = request.send().await.map_err(ListingError::Request)?;
let status = response.status();
let body = response.text().await.map_err(ListingError::Body)?;
if !status.is_success() {
return Err(ListingError::Status {
status,
body: body.trim().to_owned(),
});
}
let page: ListPage = serde_json::from_str(&body)?;
models.extend(page.publisher_models);
match page.next_page_token {
Some(next) if !next.is_empty() => page_token = Some(next),
_ => return Ok(models),
}
}
let read = models.len();
Err(ListingError::Unterminated { read })
}
pub async fn list_all(
http: &reqwest::Client,
host: &str,
token: &str,
provider: &str,
publishers: &[String],
) -> (Vec<PublisherModel>, Vec<String>) {
let mut models = Vec::new();
let mut failures = Vec::new();
for publisher in publishers {
match list_publisher_models(http, host, token, publisher).await {
Ok(page) => models.extend(page),
Err(reason) => failures.push(format!("{provider}/{publisher}: {reason}")),
}
}
(models, failures)
}