//! CreditService client library //! //! Provides a convenient client for interacting with CreditService. use creditservice_proto::credit_service_client::CreditServiceClient; use tonic::transport::Channel; use tracing::debug; pub use creditservice_proto::*; /// CreditService client pub struct Client { inner: CreditServiceClient, } impl Client { /// Connect to a CreditService server pub async fn connect(addr: impl AsRef) -> Result { let addr = addr.as_ref().to_string(); debug!("Connecting to CreditService at {}", addr); let inner = CreditServiceClient::connect(addr).await?; Ok(Self { inner }) } /// Get wallet for a project pub async fn get_wallet( &mut self, project_id: impl Into, ) -> Result { let request = GetWalletRequest { project_id: project_id.into(), }; let response = self.inner.get_wallet(request).await?; response .into_inner() .wallet .ok_or_else(|| tonic::Status::not_found("Wallet not found")) } /// Create a new wallet pub async fn create_wallet( &mut self, project_id: impl Into, org_id: impl Into, initial_balance: i64, ) -> Result { let request = CreateWalletRequest { project_id: project_id.into(), org_id: org_id.into(), initial_balance, }; let response = self.inner.create_wallet(request).await?; response .into_inner() .wallet .ok_or_else(|| tonic::Status::internal("Failed to create wallet")) } /// Check quota before resource creation pub async fn check_quota( &mut self, project_id: impl Into, resource_type: ResourceType, quantity: i32, estimated_cost: i64, ) -> Result { let request = CheckQuotaRequest { project_id: project_id.into(), resource_type: resource_type as i32, quantity, estimated_cost, }; self.inner.check_quota(request).await.map(|r| r.into_inner()) } /// Reserve credits for a resource creation pub async fn reserve_credits( &mut self, project_id: impl Into, amount: i64, description: impl Into, resource_type: impl Into, ttl_seconds: i32, ) -> Result { let request = ReserveCreditsRequest { project_id: project_id.into(), amount, description: description.into(), resource_type: resource_type.into(), ttl_seconds, }; let response = self.inner.reserve_credits(request).await?; response .into_inner() .reservation .ok_or_else(|| tonic::Status::internal("Failed to create reservation")) } /// Commit a reservation after successful resource creation pub async fn commit_reservation( &mut self, reservation_id: impl Into, actual_amount: i64, resource_id: impl Into, ) -> Result { let request = CommitReservationRequest { reservation_id: reservation_id.into(), actual_amount, resource_id: resource_id.into(), }; self.inner .commit_reservation(request) .await .map(|r| r.into_inner()) } /// Release a reservation (e.g., if resource creation failed) pub async fn release_reservation( &mut self, reservation_id: impl Into, reason: impl Into, ) -> Result { let request = ReleaseReservationRequest { reservation_id: reservation_id.into(), reason: reason.into(), }; let response = self.inner.release_reservation(request).await?; Ok(response.into_inner().success) } }