Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# SaaS RS :: CLI Changelog

## [Unreleased]
### Added
- [#84](https://github.com/saas-rs/cli/issues/84) Support enabling new Stripe payment provider

## [0.3.6] - 2025-09-27
### Changed
- [#82](https://github.com/saas-rs/cli/issues/82) Upgrade Rust from 1.88.0 → 1.89.0
Expand Down
7 changes: 7 additions & 0 deletions src/cmd/enable/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub(super) mod identity_provider;
pub(super) mod payment_provider;
pub(super) mod storage_provider;

use clap::Parser;
Expand All @@ -15,6 +16,9 @@ pub enum Subcommand {
#[command(name = "identity-provider", alias = "identityProvider")]
IdentityProvider(identity_provider::Opts),

#[command(name = "payment-provider", alias = "paymentProvider")]
PaymentProvider(payment_provider::Opts),

#[command(name = "storage-provider", alias = "storageProvider")]
StorageProvider(storage_provider::Opts),
}
Expand All @@ -24,6 +28,9 @@ pub async fn run(subcommand: Subcommand) -> Result<(), Box<dyn std::error::Error
Subcommand::IdentityProvider(identity_provider::Opts { provider }) => {
identity_provider::run(provider).await?;
}
Subcommand::PaymentProvider(payment_provider::Opts { provider }) => {
payment_provider::run(provider).await?;
}
Subcommand::StorageProvider(storage_provider::Opts { provider }) => {
storage_provider::run(provider).await?;
}
Expand Down
41 changes: 41 additions & 0 deletions src/cmd/enable/payment_provider.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use crate::cmd::generate::{do_generate, do_generate_preflight};
use crate::protocol::saas_rs::user::v1::generate_request::UsePaymentProvider;
use crate::protocol::saas_rs::user::v1::{
generate_request::{self, use_payment_provider::Provider},
GenerateRequest,
};
use clap::{
builder::PossibleValue,
{Parser, ValueEnum},
};

#[derive(Debug, Parser)]
pub struct Opts {
/// The identity provider
#[arg(value_name = "provider", value_enum)]
pub provider: Provider,
}

pub async fn run(provider: Provider) -> Result<(), Box<dyn std::error::Error>> {
let (project_id, snapshot) = do_generate_preflight(false).await?;
let req = {
GenerateRequest {
project_id,
snapshot: Some(snapshot),
what: Some(generate_request::What::UsePaymentProvider(UsePaymentProvider {
provider: provider as i32,
})),
}
};
do_generate(req).await
}

impl ValueEnum for Provider {
fn value_variants<'a>() -> &'a [Self] {
&[Self::Stripe]
}

fn to_possible_value(&self) -> Option<PossibleValue> {
Some(PossibleValue::new(self.as_str_name()))
}
}
45 changes: 44 additions & 1 deletion src/protocol/saas_rs.user.v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,7 @@ pub struct GenerateRequest {
pub project_id: ::prost::alloc::string::String,
#[prost(oneof = "generate_request::Snapshot", tags = "2, 3")]
pub snapshot: ::core::option::Option<generate_request::Snapshot>,
#[prost(oneof = "generate_request::What", tags = "4, 5, 6, 7, 8, 9, 10, 11")]
#[prost(oneof = "generate_request::What", tags = "4, 5, 6, 7, 8, 9, 10, 11, 12")]
pub what: ::core::option::Option<generate_request::What>,
}
/// Nested message and enum types in `GenerateRequest`.
Expand Down Expand Up @@ -774,6 +774,47 @@ pub mod generate_request {
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
pub struct UsePaymentProvider {
#[prost(enumeration = "use_payment_provider::Provider", tag = "1")]
pub provider: i32,
}
/// Nested message and enum types in `UsePaymentProvider`.
pub mod use_payment_provider {
#[derive(
Clone,
Copy,
Debug,
PartialEq,
Eq,
Hash,
PartialOrd,
Ord,
::prost::Enumeration
)]
#[repr(i32)]
pub enum Provider {
Stripe = 0,
}
impl Provider {
/// String value of the enum field names used in the ProtoBuf definition.
///
/// The values are not transformed in any way and thus are considered stable
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
pub fn as_str_name(&self) -> &'static str {
match self {
Self::Stripe => "Stripe",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
match value {
"Stripe" => Some(Self::Stripe),
_ => None,
}
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
pub struct UseStorageProvider {
#[prost(enumeration = "use_storage_provider::Provider", tag = "1")]
pub provider: i32,
Expand Down Expand Up @@ -854,6 +895,8 @@ pub mod generate_request {
UseStorageProvider(UseStorageProvider),
#[prost(message, tag = "11")]
UseIdentityProvider(UseIdentityProvider),
#[prost(message, tag = "12")]
UsePaymentProvider(UsePaymentProvider),
}
}
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
Expand Down
179 changes: 179 additions & 0 deletions src/protocol/saas_rs.user.v1.serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13537,6 +13537,9 @@ impl serde::Serialize for GenerateRequest {
generate_request::What::UseIdentityProvider(v) => {
struct_ser.serialize_field("useIdentityProvider", v)?;
}
generate_request::What::UsePaymentProvider(v) => {
struct_ser.serialize_field("usePaymentProvider", v)?;
}
}
}
struct_ser.end()
Expand Down Expand Up @@ -13564,6 +13567,8 @@ impl<'de> serde::Deserialize<'de> for GenerateRequest {
"useStorageProvider",
"use_identity_provider",
"useIdentityProvider",
"use_payment_provider",
"usePaymentProvider",
];

#[allow(clippy::enum_variant_names)]
Expand All @@ -13579,6 +13584,7 @@ impl<'de> serde::Deserialize<'de> for GenerateRequest {
Feature,
UseStorageProvider,
UseIdentityProvider,
UsePaymentProvider,
__SkipField__,
}
impl<'de> serde::Deserialize<'de> for GeneratedField {
Expand Down Expand Up @@ -13612,6 +13618,7 @@ impl<'de> serde::Deserialize<'de> for GenerateRequest {
"feature" => Ok(GeneratedField::Feature),
"useStorageProvider" | "use_storage_provider" => Ok(GeneratedField::UseStorageProvider),
"useIdentityProvider" | "use_identity_provider" => Ok(GeneratedField::UseIdentityProvider),
"usePaymentProvider" | "use_payment_provider" => Ok(GeneratedField::UsePaymentProvider),
_ => Ok(GeneratedField::__SkipField__),
}
}
Expand Down Expand Up @@ -13710,6 +13717,13 @@ impl<'de> serde::Deserialize<'de> for GenerateRequest {
return Err(serde::de::Error::duplicate_field("useIdentityProvider"));
}
what__ = map_.next_value::<::std::option::Option<_>>()?.map(generate_request::What::UseIdentityProvider)
;
}
GeneratedField::UsePaymentProvider => {
if what__.is_some() {
return Err(serde::de::Error::duplicate_field("usePaymentProvider"));
}
what__ = map_.next_value::<::std::option::Option<_>>()?.map(generate_request::What::UsePaymentProvider)
;
}
GeneratedField::__SkipField__ => {
Expand Down Expand Up @@ -15065,6 +15079,171 @@ impl<'de> serde::Deserialize<'de> for generate_request::use_identity_provider::P
deserializer.deserialize_any(GeneratedVisitor)
}
}
impl serde::Serialize for generate_request::UsePaymentProvider {
#[allow(deprecated)]
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeStruct;
let mut len = 0;
if true {
len += 1;
}
let mut struct_ser = serializer.serialize_struct("saas_rs.user.v1.GenerateRequest.UsePaymentProvider", len)?;
if true {
let v = generate_request::use_payment_provider::Provider::try_from(self.provider)
.map_err(|_| serde::ser::Error::custom(format!("Invalid variant {}", self.provider)))?;
struct_ser.serialize_field("provider", &v)?;
}
struct_ser.end()
}
}
impl<'de> serde::Deserialize<'de> for generate_request::UsePaymentProvider {
#[allow(deprecated)]
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
const FIELDS: &[&str] = &[
"provider",
];

#[allow(clippy::enum_variant_names)]
enum GeneratedField {
Provider,
__SkipField__,
}
impl<'de> serde::Deserialize<'de> for GeneratedField {
fn deserialize<D>(deserializer: D) -> std::result::Result<GeneratedField, D::Error>
where
D: serde::Deserializer<'de>,
{
struct GeneratedVisitor;

impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
type Value = GeneratedField;

fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(formatter, "expected one of: {:?}", &FIELDS)
}

#[allow(unused_variables)]
fn visit_str<E>(self, value: &str) -> std::result::Result<GeneratedField, E>
where
E: serde::de::Error,
{
match value {
"provider" => Ok(GeneratedField::Provider),
_ => Ok(GeneratedField::__SkipField__),
}
}
}
deserializer.deserialize_identifier(GeneratedVisitor)
}
}
struct GeneratedVisitor;
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
type Value = generate_request::UsePaymentProvider;

fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("struct saas_rs.user.v1.GenerateRequest.UsePaymentProvider")
}

fn visit_map<V>(self, mut map_: V) -> std::result::Result<generate_request::UsePaymentProvider, V::Error>
where
V: serde::de::MapAccess<'de>,
{
let mut provider__ = None;
while let Some(k) = map_.next_key()? {
match k {
GeneratedField::Provider => {
if provider__.is_some() {
return Err(serde::de::Error::duplicate_field("provider"));
}
provider__ = Some(map_.next_value::<generate_request::use_payment_provider::Provider>()? as i32);
}
GeneratedField::__SkipField__ => {
let _ = map_.next_value::<serde::de::IgnoredAny>()?;
}
}
}
Ok(generate_request::UsePaymentProvider {
provider: provider__.unwrap_or_default(),
})
}
}
deserializer.deserialize_struct("saas_rs.user.v1.GenerateRequest.UsePaymentProvider", FIELDS, GeneratedVisitor)
}
}
impl serde::Serialize for generate_request::use_payment_provider::Provider {
#[allow(deprecated)]
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let variant = match self {
Self::Stripe => "Stripe",
};
serializer.serialize_str(variant)
}
}
impl<'de> serde::Deserialize<'de> for generate_request::use_payment_provider::Provider {
#[allow(deprecated)]
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
const FIELDS: &[&str] = &[
"Stripe",
];

struct GeneratedVisitor;

impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
type Value = generate_request::use_payment_provider::Provider;

fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(formatter, "expected one of: {:?}", &FIELDS)
}

fn visit_i64<E>(self, v: i64) -> std::result::Result<Self::Value, E>
where
E: serde::de::Error,
{
i32::try_from(v)
.ok()
.and_then(|x| x.try_into().ok())
.ok_or_else(|| {
serde::de::Error::invalid_value(serde::de::Unexpected::Signed(v), &self)
})
}

fn visit_u64<E>(self, v: u64) -> std::result::Result<Self::Value, E>
where
E: serde::de::Error,
{
i32::try_from(v)
.ok()
.and_then(|x| x.try_into().ok())
.ok_or_else(|| {
serde::de::Error::invalid_value(serde::de::Unexpected::Unsigned(v), &self)
})
}

fn visit_str<E>(self, value: &str) -> std::result::Result<Self::Value, E>
where
E: serde::de::Error,
{
match value {
"Stripe" => Ok(generate_request::use_payment_provider::Provider::Stripe),
_ => Err(serde::de::Error::unknown_variant(value, FIELDS)),
}
}
}
deserializer.deserialize_any(GeneratedVisitor)
}
}
impl serde::Serialize for generate_request::UseStorageProvider {
#[allow(deprecated)]
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
Expand Down