use thiserror::Error;
use url::Url;
use crate::api;
use crate::api::request::ensure_success;
use crate::client::Client;
#[cfg(feature = "send3")]
use crate::config;
const VERSION_ENDPOINT: &str = "__version__";
#[cfg(feature = "send2")]
const V2_PROBE_ENDPOINT: &str = "jsconfig.js";
#[cfg(feature = "send3")]
const V3_PROBE_ENDPOINT: &str = "app.webmanifest";
pub struct Version {
host: Url,
}
impl Version {
pub fn new(host: Url) -> Self {
Self { host }
}
pub fn invoke(self, client: &Client) -> Result<api::Version, Error> {
let mut result = self.fetch_version(client);
if let Err(Error::Unknown) = result {
result = self.probe(client);
}
result
}
fn fetch_version(&self, client: &Client) -> Result<api::Version, Error> {
let version_url = self.host.join(VERSION_ENDPOINT).expect("invalid host");
let response = client.get(version_url).send().map_err(|_| Error::Request)?;
#[cfg(feature = "send3")]
{
if response.status() == config::HTTP_STATUS_EXPIRED {
return Ok(api::Version::V3);
}
}
match ensure_success(&response) {
Ok(_) => {}
Err(_) => return Err(Error::Unknown),
}
let response = response
.json::<VersionResponse>()
.map_err(|_| Error::Unknown)?;
response.determine_version()
}
fn probe(&self, client: &Client) -> Result<api::Version, Error> {
#[cfg(feature = "send3")]
{
if self.exists(client, V3_PROBE_ENDPOINT) {
return Ok(api::Version::V3);
}
}
#[cfg(feature = "send2")]
{
if self.exists(client, V2_PROBE_ENDPOINT) {
return Ok(api::Version::V2);
}
}
Err(Error::Unknown)
}
fn exists(&self, client: &Client, endpoint: &str) -> bool {
let url = self.host.join(endpoint).expect("invalid host");
client
.get(url)
.send()
.map(|r| r.status())
.map(|s| s.is_success() || s.is_redirection())
.unwrap_or(false)
}
}
#[derive(Debug, Deserialize)]
pub struct VersionResponse {
version: String,
}
impl VersionResponse {
pub fn determine_version<'a>(&'a self) -> Result<api::Version, Error> {
api::Version::parse(&self.version).map_err(|v| Error::Unsupported(v.into()))
}
}
#[derive(Error, Debug)]
pub enum Error {
#[error("failed to send request to fetch server version")]
Request,
#[error("failed to determine server version")]
Unknown,
#[error("failed to determine server version, unsupported version")]
Unsupported(String),
}