use std::cmp::Ordering;
use std::fmt::{self, Debug, Display, Formatter};
use std::str::FromStr;
use anyhow::{format_err, Error, Result};
use semver::Version;
pub use bellperson::groth16::aggregate::AggregateVersion;
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum ApiVersion {
V1_0_0,
V1_1_0,
V1_2_0,
}
impl Ord for ApiVersion {
fn cmp(&self, other: &Self) -> Ordering {
self.as_semver().cmp(&other.as_semver())
}
}
impl PartialOrd for ApiVersion {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl ApiVersion {
pub fn as_semver(&self) -> Version {
match self {
ApiVersion::V1_0_0 => Version::new(1, 0, 0),
ApiVersion::V1_1_0 => Version::new(1, 1, 0),
ApiVersion::V1_2_0 => Version::new(1, 2, 0),
}
}
#[inline]
pub fn supports_feature(&self, feat: &ApiFeature) -> bool {
self >= &feat.first_supported_version()
&& feat
.last_supported_version()
.map(|v_last| self <= &v_last)
.unwrap_or(true)
}
#[inline]
pub fn supports_features(&self, feats: &[ApiFeature]) -> bool {
feats.iter().all(|feat| self.supports_feature(feat))
}
}
impl Debug for ApiVersion {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let semver = self.as_semver();
write!(f, "{}.{}.{}", semver.major, semver.minor, semver.patch)
}
}
impl Display for ApiVersion {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let semver = self.as_semver();
write!(f, "{}.{}.{}", semver.major, semver.minor, semver.patch)
}
}
impl FromStr for ApiVersion {
type Err = Error;
fn from_str(api_version_str: &str) -> Result<Self> {
let api_version = Version::parse(api_version_str)?;
match (api_version.major, api_version.minor, api_version.patch) {
(1, 0, 0) => Ok(ApiVersion::V1_0_0),
(1, 1, 0) => Ok(ApiVersion::V1_1_0),
(1, 2, 0) => Ok(ApiVersion::V1_2_0),
(1, 0, _) | (1, 1, _) | (1, 2, _) => Err(format_err!(
"Could not parse API Version from string (patch)"
)),
(1, _, _) => Err(format_err!(
"Could not parse API Version from string (minor)"
)),
_ => Err(format_err!(
"Could not parse API Version from string (major)"
)),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ApiFeature {
SyntheticPoRep,
NonInteractivePoRep,
}
impl ApiFeature {
#[inline]
pub fn first_supported_version(&self) -> ApiVersion {
match self {
ApiFeature::SyntheticPoRep | ApiFeature::NonInteractivePoRep => ApiVersion::V1_2_0,
}
}
#[inline]
pub fn last_supported_version(&self) -> Option<ApiVersion> {
match self {
ApiFeature::SyntheticPoRep | ApiFeature::NonInteractivePoRep => None,
}
}
pub fn conflicting_features(&self) -> &[ApiFeature] {
match self {
ApiFeature::SyntheticPoRep => &[ApiFeature::NonInteractivePoRep],
ApiFeature::NonInteractivePoRep => &[ApiFeature::SyntheticPoRep],
}
}
}
impl Display for ApiFeature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let api_feature_str = match self {
Self::SyntheticPoRep => "synthetic-porep",
Self::NonInteractivePoRep => "non-interactive-porep",
};
write!(f, "{}", api_feature_str)
}
}
impl FromStr for ApiFeature {
type Err = Error;
fn from_str(api_feature_str: &str) -> Result<Self> {
match api_feature_str {
"synthetic-porep" => Ok(ApiFeature::SyntheticPoRep),
"non-interactive-porep" => Ok(ApiFeature::NonInteractivePoRep),
_ => Err(format_err!(
"'{}' cannot be parsed as valid API feature",
api_feature_str
)),
}
}
}
#[test]
fn test_fmt() {
assert_eq!(format!("{}", ApiVersion::V1_0_0), "1.0.0");
assert_eq!(format!("{}", ApiVersion::V1_1_0), "1.1.0");
assert_eq!(format!("{}", ApiVersion::V1_2_0), "1.2.0");
}
#[test]
fn test_as_semver() {
assert_eq!(ApiVersion::V1_0_0.as_semver().major, 1);
assert_eq!(ApiVersion::V1_1_0.as_semver().major, 1);
assert_eq!(ApiVersion::V1_2_0.as_semver().major, 1);
assert_eq!(ApiVersion::V1_0_0.as_semver().minor, 0);
assert_eq!(ApiVersion::V1_1_0.as_semver().minor, 1);
assert_eq!(ApiVersion::V1_2_0.as_semver().minor, 2);
assert_eq!(ApiVersion::V1_0_0.as_semver().patch, 0);
assert_eq!(ApiVersion::V1_1_0.as_semver().patch, 0);
assert_eq!(ApiVersion::V1_2_0.as_semver().patch, 0);
}
#[test]
fn test_api_version_order() {
assert!(ApiVersion::V1_0_0 < ApiVersion::V1_1_0 && ApiVersion::V1_1_0 < ApiVersion::V1_2_0);
assert!(ApiVersion::V1_1_0 > ApiVersion::V1_0_0 && ApiVersion::V1_2_0 > ApiVersion::V1_1_0);
}
#[test]
fn test_api_feature_synthetic_porep() {
let feature = ApiFeature::SyntheticPoRep;
assert_eq!(format!("{}", feature), "synthetic-porep");
assert_eq!(
ApiFeature::from_str("synthetic-porep").expect("can be parsed"),
feature
);
assert!(feature.first_supported_version() == ApiVersion::V1_2_0);
assert!(feature.last_supported_version().is_none());
}
#[test]
fn test_api_feature_non_interactive_porep() {
let feature = ApiFeature::NonInteractivePoRep;
assert!(feature.first_supported_version() == ApiVersion::V1_2_0);
assert!(feature.last_supported_version().is_none());
}