use http::Method;
use libsecp256k1::PublicKey;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::fmt::Debug;
use std::str::FromStr;
use uuid::Uuid;
use crate::account::Account;
use crate::account::Username;
use crate::crypto::*;
use crate::file_metadata::{DocumentHmac, FileDiff, FileMetadata, Owner};
use crate::server_file::ServerFile;
use crate::signed_file::SignedFile;
use crate::ValidationFailure;
pub const FREE_TIER_USAGE_SIZE: u64 = 1000000;
pub const PREMIUM_TIER_USAGE_SIZE: u64 = 30000000000;
pub const METADATA_FEE: u64 = 1000;
pub trait Request: Serialize + 'static {
type Response: Debug + DeserializeOwned + Clone;
type Error: Debug + DeserializeOwned + Clone;
const METHOD: Method;
const ROUTE: &'static str;
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RequestWrapper<T: Request> {
pub signed_request: ECSigned<T>,
pub client_version: String,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum ErrorWrapper<E> {
Endpoint(E),
ClientUpdateRequired,
InvalidAuth,
ExpiredAuth,
InternalError,
BadRequest,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct UpsertRequest {
pub updates: Vec<FileDiff<SignedFile>>,
}
impl Request for UpsertRequest {
type Response = ();
type Error = UpsertError;
const METHOD: Method = Method::POST;
const ROUTE: &'static str = "/upsert-file-metadata";
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum UpsertError {
OldVersionIncorrect,
OldFileNotFound,
OldVersionRequired,
NotPermissioned,
DiffMalformed,
HmacModificationInvalid,
RootModificationInvalid,
DeletedFileUpdated,
UsageIsOverDataCap,
Validation(ValidationFailure),
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct ChangeDocRequest {
pub diff: FileDiff<SignedFile>,
pub new_content: EncryptedDocument,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum ChangeDocError {
HmacMissing,
DocumentNotFound,
DocumentDeleted,
NotPermissioned,
OldVersionIncorrect,
DiffMalformed,
UsageIsOverDataCap,
}
impl Request for ChangeDocRequest {
type Response = ();
type Error = ChangeDocError;
const METHOD: Method = Method::PUT;
const ROUTE: &'static str = "/change-document-content";
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct GetDocRequest {
pub id: Uuid,
pub hmac: DocumentHmac,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct GetDocumentResponse {
pub content: EncryptedDocument,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum GetDocumentError {
DocumentNotFound,
NotPermissioned,
}
impl Request for GetDocRequest {
type Response = GetDocumentResponse;
type Error = GetDocumentError;
const METHOD: Method = Method::GET;
const ROUTE: &'static str = "/get-document";
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct GetPublicKeyRequest {
pub username: String,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct GetPublicKeyResponse {
pub key: PublicKey,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum GetPublicKeyError {
InvalidUsername,
UserNotFound,
}
impl Request for GetPublicKeyRequest {
type Response = GetPublicKeyResponse;
type Error = GetPublicKeyError;
const METHOD: Method = Method::GET;
const ROUTE: &'static str = "/get-public-key";
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct GetUsernameRequest {
pub key: PublicKey,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct GetUsernameResponse {
pub username: String,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum GetUsernameError {
UserNotFound,
}
impl Request for GetUsernameRequest {
type Response = GetUsernameResponse;
type Error = GetUsernameError;
const METHOD: Method = Method::GET;
const ROUTE: &'static str = "/get-username";
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct GetUsageRequest {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct GetUsageResponse {
pub usages: Vec<FileUsage>,
pub cap: u64,
}
impl GetUsageResponse {
pub fn sum_server_usage(&self) -> u64 {
self.usages.iter().map(|usage| usage.size_bytes).sum()
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct FileUsage {
pub file_id: Uuid,
pub size_bytes: u64,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum GetUsageError {
UserNotFound,
}
impl Request for GetUsageRequest {
type Response = GetUsageResponse;
type Error = GetUsageError;
const METHOD: Method = Method::GET;
const ROUTE: &'static str = "/get-usage";
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct GetFileIdsRequest {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct GetFileIdsResponse {
pub ids: HashSet<Uuid>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum GetFileIdsError {
UserNotFound,
}
impl Request for GetFileIdsRequest {
type Response = GetFileIdsResponse;
type Error = GetFileIdsError;
const METHOD: Method = Method::GET;
const ROUTE: &'static str = "/get-file-ids";
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct GetUpdatesRequest {
pub since_metadata_version: u64,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct GetUpdatesResponse {
pub as_of_metadata_version: u64,
pub file_metadata: Vec<SignedFile>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum GetUpdatesError {
UserNotFound,
}
impl Request for GetUpdatesRequest {
type Response = GetUpdatesResponse;
type Error = GetUpdatesError;
const METHOD: Method = Method::GET;
const ROUTE: &'static str = "/get-updates";
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct NewAccountRequest {
pub username: Username,
pub public_key: PublicKey,
pub root_folder: SignedFile,
}
impl NewAccountRequest {
pub fn new(account: &Account, root_folder: &ECSigned<FileMetadata>) -> Self {
let root_folder = root_folder.clone();
NewAccountRequest {
username: account.username.clone(),
public_key: account.public_key(),
root_folder,
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct NewAccountResponse {
pub last_synced: u64,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum NewAccountError {
UsernameTaken,
PublicKeyTaken,
InvalidUsername,
FileIdTaken,
Disabled,
}
impl Request for NewAccountRequest {
type Response = NewAccountResponse;
type Error = NewAccountError;
const METHOD: Method = Method::POST;
const ROUTE: &'static str = "/new-account";
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct GetBuildInfoRequest {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum GetBuildInfoError {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct GetBuildInfoResponse {
pub build_version: String,
pub git_commit_hash: String,
}
impl Request for GetBuildInfoRequest {
type Response = GetBuildInfoResponse;
type Error = GetBuildInfoError;
const METHOD: Method = Method::GET;
const ROUTE: &'static str = "/get-build-info";
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct DeleteAccountRequest {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum DeleteAccountError {
UserNotFound,
}
impl Request for DeleteAccountRequest {
type Response = ();
type Error = DeleteAccountError;
const METHOD: Method = Method::DELETE;
const ROUTE: &'static str = "/delete-account";
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum PaymentMethod {
NewCard { number: String, exp_year: i32, exp_month: i32, cvc: String },
OldCard,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum StripeAccountTier {
Premium(PaymentMethod),
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct UpgradeAccountStripeRequest {
pub account_tier: StripeAccountTier,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct UpgradeAccountStripeResponse {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum UpgradeAccountStripeError {
OldCardDoesNotExist,
AlreadyPremium,
CardDecline,
InsufficientFunds,
TryAgain,
CardNotSupported,
ExpiredCard,
InvalidCardNumber,
InvalidCardExpYear,
InvalidCardExpMonth,
InvalidCardCvc,
ExistingRequestPending,
UserNotFound,
}
impl Request for UpgradeAccountStripeRequest {
type Response = UpgradeAccountStripeResponse;
type Error = UpgradeAccountStripeError;
const METHOD: Method = Method::POST;
const ROUTE: &'static str = "/upgrade-account-stripe";
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct UpgradeAccountGooglePlayRequest {
pub purchase_token: String,
pub account_id: String,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct UpgradeAccountGooglePlayResponse {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum UpgradeAccountGooglePlayError {
AlreadyPremium,
InvalidPurchaseToken,
ExistingRequestPending,
UserNotFound,
}
impl Request for UpgradeAccountGooglePlayRequest {
type Response = UpgradeAccountGooglePlayResponse;
type Error = UpgradeAccountGooglePlayError;
const METHOD: Method = Method::POST;
const ROUTE: &'static str = "/upgrade-account-google-play";
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct UpgradeAccountAppStoreRequest {
pub original_transaction_id: String,
pub app_account_token: String,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct UpgradeAccountAppStoreResponse {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum UpgradeAccountAppStoreError {
AppStoreAccountAlreadyLinked,
AlreadyPremium,
InvalidAuthDetails,
ExistingRequestPending,
UserNotFound,
}
impl Request for UpgradeAccountAppStoreRequest {
type Response = UpgradeAccountAppStoreResponse;
type Error = UpgradeAccountAppStoreError;
const METHOD: Method = Method::POST;
const ROUTE: &'static str = "/upgrade-account-app-store";
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct CancelSubscriptionRequest {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct CancelSubscriptionResponse {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum CancelSubscriptionError {
NotPremium,
AlreadyCanceled,
UsageIsOverFreeTierDataCap,
UserNotFound,
ExistingRequestPending,
CannotCancelForAppStore,
}
impl Request for CancelSubscriptionRequest {
type Response = CancelSubscriptionResponse;
type Error = CancelSubscriptionError;
const METHOD: Method = Method::DELETE;
const ROUTE: &'static str = "/cancel-subscription";
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct GetSubscriptionInfoRequest {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct SubscriptionInfo {
pub payment_platform: PaymentPlatform,
pub period_end: UnixTimeMillis,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
#[serde(tag = "tag")]
pub enum PaymentPlatform {
Stripe { card_last_4_digits: String },
GooglePlay { account_state: GooglePlayAccountState },
AppStore { account_state: AppStoreAccountState },
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct GetSubscriptionInfoResponse {
pub subscription_info: Option<SubscriptionInfo>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum GetSubscriptionInfoError {
UserNotFound,
}
impl Request for GetSubscriptionInfoRequest {
type Response = GetSubscriptionInfoResponse;
type Error = GetSubscriptionInfoError;
const METHOD: Method = Method::GET;
const ROUTE: &'static str = "/get-subscription-info";
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct AdminDisappearAccountRequest {
pub username: String,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum AdminDisappearAccountError {
NotPermissioned,
UserNotFound,
}
impl Request for AdminDisappearAccountRequest {
type Response = ();
type Error = AdminDisappearAccountError;
const METHOD: Method = Method::DELETE;
const ROUTE: &'static str = "/admin-disappear-account";
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct AdminDisappearFileRequest {
pub id: Uuid,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum AdminDisappearFileError {
NotPermissioned,
FileNonexistent,
RootModificationInvalid,
}
impl Request for AdminDisappearFileRequest {
type Response = ();
type Error = AdminDisappearFileError;
const METHOD: Method = Method::DELETE;
const ROUTE: &'static str = "/admin-disappear-file";
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct AdminValidateAccountRequest {
pub username: String,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)]
pub struct AdminValidateAccount {
pub tree_validation_failures: Vec<ValidationFailure>,
pub documents_missing_size: Vec<Uuid>,
pub documents_missing_content: Vec<Uuid>,
}
impl AdminValidateAccount {
pub fn is_empty(&self) -> bool {
self.tree_validation_failures.is_empty()
&& self.documents_missing_content.is_empty()
&& self.documents_missing_size.is_empty()
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum AdminValidateAccountError {
NotPermissioned,
UserNotFound,
}
impl Request for AdminValidateAccountRequest {
type Response = AdminValidateAccount;
type Error = AdminValidateAccountError;
const METHOD: Method = Method::GET;
const ROUTE: &'static str = "/admin-validate-account";
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct AdminValidateServerRequest {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)]
pub struct AdminValidateServer {
pub users_with_validation_failures: HashMap<Username, AdminValidateAccount>,
pub usernames_mapped_to_wrong_accounts: HashMap<String, String>,
pub usernames_mapped_to_nonexistent_accounts: HashMap<String, Owner>,
pub usernames_unmapped_to_accounts: HashSet<String>,
pub owners_mapped_to_unowned_files: HashMap<Owner, HashSet<Uuid>>,
pub owners_mapped_to_nonexistent_files: HashMap<Owner, HashSet<Uuid>>,
pub owners_unmapped_to_owned_files: HashMap<Owner, HashSet<Uuid>>,
pub owners_unmapped: HashSet<Owner>,
pub sharees_mapped_to_unshared_files: HashMap<Owner, HashSet<Uuid>>,
pub sharees_mapped_to_nonexistent_files: HashMap<Owner, HashSet<Uuid>>,
pub sharees_mapped_for_owned_files: HashMap<Owner, HashSet<Uuid>>,
pub sharees_mapped_for_deleted_files: HashMap<Owner, HashSet<Uuid>>,
pub sharees_unmapped_to_shared_files: HashMap<Owner, HashSet<Uuid>>,
pub sharees_unmapped: HashSet<Owner>,
pub files_mapped_as_parent_to_non_children: HashMap<Uuid, HashSet<Uuid>>,
pub files_mapped_as_parent_to_nonexistent_children: HashMap<Uuid, HashSet<Uuid>>,
pub files_mapped_as_parent_to_self: HashSet<Uuid>,
pub files_unmapped_as_parent_to_children: HashMap<Uuid, HashSet<Uuid>>,
pub files_unmapped_as_parent: HashSet<Uuid>,
pub sizes_mapped_for_files_without_hmac: HashSet<Uuid>,
pub sizes_mapped_for_nonexistent_files: HashSet<Uuid>,
pub sizes_unmapped_for_files_with_hmac: HashSet<Uuid>,
pub files_with_hmacs_and_no_contents: HashSet<Uuid>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum AdminValidateServerError {
NotPermissioned,
}
impl Request for AdminValidateServerRequest {
type Response = AdminValidateServer;
type Error = AdminValidateServerError;
const METHOD: Method = Method::GET;
const ROUTE: &'static str = "/admin-validate-server";
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct AdminListUsersRequest {
pub filter: Option<AccountFilter>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum AccountFilter {
Premium,
AppStorePremium,
StripePremium,
GooglePlayPremium,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct AdminListUsersResponse {
pub users: Vec<Username>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum AdminListUsersError {
NotPermissioned,
}
impl Request for AdminListUsersRequest {
type Response = AdminListUsersResponse;
type Error = AdminListUsersError;
const METHOD: Method = Method::GET;
const ROUTE: &'static str = "/admin-list-users";
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct AdminGetAccountInfoRequest {
pub identifier: AccountIdentifier,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum AccountIdentifier {
PublicKey(PublicKey),
Username(Username),
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct AdminGetAccountInfoResponse {
pub account: AccountInfo,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct AccountInfo {
pub username: String,
pub root: Uuid,
pub payment_platform: Option<PaymentPlatform>,
pub usage: String,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum AdminGetAccountInfoError {
UserNotFound,
NotPermissioned,
}
impl Request for AdminGetAccountInfoRequest {
type Response = AdminGetAccountInfoResponse;
type Error = AdminGetAccountInfoError;
const METHOD: Method = Method::GET;
const ROUTE: &'static str = "/admin-get-account-info";
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct AdminFileInfoRequest {
pub id: Uuid,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AdminFileInfoResponse {
pub file: ServerFile,
pub ancestors: Vec<ServerFile>,
pub descendants: Vec<ServerFile>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum AdminFileInfoError {
NotPermissioned,
FileNonexistent,
}
impl Request for AdminFileInfoRequest {
type Response = AdminFileInfoResponse;
type Error = AdminFileInfoError;
const METHOD: Method = Method::GET;
const ROUTE: &'static str = "/admin-file-info";
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum AdminSetUserTierInfo {
Stripe {
customer_id: String,
customer_name: Uuid,
payment_method_id: String,
last_4: String,
subscription_id: String,
expiration_time: UnixTimeMillis,
account_state: StripeAccountState,
},
GooglePlay {
purchase_token: String,
expiration_time: UnixTimeMillis,
account_state: GooglePlayAccountState,
},
AppStore {
account_token: String,
original_transaction_id: String,
expiration_time: UnixTimeMillis,
account_state: AppStoreAccountState,
},
Free,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct AdminSetUserTierRequest {
pub username: String,
pub info: AdminSetUserTierInfo,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AdminSetUserTierResponse {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum AdminSetUserTierError {
UserNotFound,
NotPermissioned,
ExistingRequestPending,
}
impl Request for AdminSetUserTierRequest {
type Response = AdminSetUserTierResponse;
type Error = AdminSetUserTierError;
const METHOD: Method = Method::POST;
const ROUTE: &'static str = "/admin-set-user-tier";
}
pub type UnixTimeMillis = u64;
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
pub enum ServerIndex {
OwnedFiles,
SharedFiles,
FileChildren,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AdminRebuildIndexRequest {
pub index: ServerIndex,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum AdminRebuildIndexError {
NotPermissioned,
}
impl Request for AdminRebuildIndexRequest {
type Response = ();
type Error = AdminRebuildIndexError;
const METHOD: Method = Method::POST;
const ROUTE: &'static str = "/admin-rebuild-index";
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum StripeAccountState {
Ok,
InvoiceFailed,
Canceled,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum GooglePlayAccountState {
Ok,
Canceled,
GracePeriod,
OnHold,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum AppStoreAccountState {
Ok,
GracePeriod,
FailedToRenew,
Expired,
}
impl FromStr for StripeAccountState {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Ok" => Ok(StripeAccountState::Ok),
"Canceled" => Ok(StripeAccountState::Canceled),
"InvoiceFailed" => Ok(StripeAccountState::InvoiceFailed),
_ => Err(()),
}
}
}
impl FromStr for GooglePlayAccountState {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Ok" => Ok(GooglePlayAccountState::Ok),
"Canceled" => Ok(GooglePlayAccountState::Canceled),
"GracePeriod" => Ok(GooglePlayAccountState::GracePeriod),
"OnHold" => Ok(GooglePlayAccountState::OnHold),
_ => Err(()),
}
}
}
impl FromStr for AppStoreAccountState {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Ok" => Ok(AppStoreAccountState::Ok),
"Expired" => Ok(AppStoreAccountState::Expired),
"GracePeriod" => Ok(AppStoreAccountState::GracePeriod),
"FailedToRenew" => Ok(AppStoreAccountState::FailedToRenew),
_ => Err(()),
}
}
}