Files
el/ui/crates/el-publish/src/google.rs
T

125 lines
4.1 KiB
Rust

//! Google Play Developer API publisher.
//!
//! Models the Google Play Developer API calls. Stubs the actual HTTP.
//!
//! API reference: https://developers.google.com/android-publisher
use crate::{
config::{GoogleConfig, PublishConfig},
metadata::StoreMetadata,
PublishError, PublishOutcome, PublishResult,
};
/// Publishes app bundles to Google Play Store.
pub struct GooglePublisher {
pub google_config: GoogleConfig,
pub publish_config: PublishConfig,
}
impl GooglePublisher {
pub fn new(publish_config: PublishConfig) -> PublishResult<Self> {
let google_config = publish_config.google.clone().ok_or_else(|| {
PublishError::Config("no [publish.google] section in el.toml".into())
})?;
Ok(Self { google_config, publish_config })
}
/// Create a new edit session on Google Play.
///
/// In production: POST https://androidpublisher.googleapis.com/v3/applications/{packageName}/edits
pub fn create_edit(&self) -> PublishResult<String> {
// TODO: OAuth2 authentication via service account JSON
let edit_id = format!("edit-{}", self.publish_config.build_number);
Ok(edit_id)
}
/// Upload an AAB (Android App Bundle) to an edit session.
///
/// In production: POST .../edits/{editId}/bundles (multipart upload)
pub fn upload_bundle(
&self,
edit_id: &str,
aab_path: &str,
) -> PublishResult<u32> {
let _ = (edit_id, aab_path);
// Returns the version code of the uploaded bundle
Ok(self.publish_config.build_number)
}
/// Assign a bundle to a track.
///
/// In production: PUT .../edits/{editId}/tracks/{track}
pub fn assign_to_track(
&self,
edit_id: &str,
version_code: u32,
track: &str,
rollout_fraction: f64,
) -> PublishResult<()> {
let _ = (edit_id, version_code, track, rollout_fraction);
if !matches!(track, "internal" | "alpha" | "beta" | "production") {
return Err(PublishError::Config(format!(
"unknown Google Play track: '{}'. Use internal/alpha/beta/production",
track
)));
}
Ok(())
}
/// Upload store listing (metadata) for a locale.
///
/// In production: PATCH .../edits/{editId}/listings/{language}
pub fn upload_listing(
&self,
edit_id: &str,
locale: &str,
metadata: &StoreMetadata,
) -> PublishResult<()> {
let _ = (edit_id, locale, metadata);
Ok(())
}
/// Commit an edit (makes the changes live).
///
/// In production: POST .../edits/{editId}:commit
pub fn commit_edit(&self, edit_id: &str) -> PublishResult<String> {
// Returns the resulting version code
Ok(format!("{}-committed", edit_id))
}
/// Update rollout percentage for a track (for staged rollouts).
pub fn update_rollout(&self, track: &str, percent: u8) -> PublishResult<()> {
if percent > 100 {
return Err(PublishError::Config(format!(
"rollout percent {} exceeds 100",
percent
)));
}
let _ = (track, percent);
// TODO: PATCH .../tracks/{track} with rollout fraction
Ok(())
}
/// Full publish flow: create edit → upload bundle → assign to track → commit.
pub fn publish(&self, aab_path: &str) -> PublishResult<PublishOutcome> {
let metadata = StoreMetadata::load_from_dir(&self.publish_config.metadata_dir)?;
let edit_id = self.create_edit()?;
let version_code = self.upload_bundle(&edit_id, aab_path)?;
let track = &self.google_config.track;
let rollout_fraction = self
.publish_config
.rollout
.as_ref()
.map(|r| r.initial_percent as f64 / 100.0)
.unwrap_or(1.0);
self.assign_to_track(&edit_id, version_code, track, rollout_fraction)?;
self.upload_listing(&edit_id, "en-US", &metadata)?;
let commit_id = self.commit_edit(&edit_id)?;
Ok(PublishOutcome::new("google", &self.publish_config, track)
.with_submission_id(commit_id))
}
}