Skip to main content

smbcloud_ascapi_play/
track.rs

1use serde::{Deserialize, Serialize};
2
3/// Release notes for one language on a track release.
4#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5pub struct LocalizedText {
6    pub language: String,
7    pub text: String,
8}
9
10/// One release on a track. Fields this crate does not edit are carried
11/// through untouched so a `tracks.update` does not drop them.
12#[derive(Debug, Clone, Serialize, Deserialize)]
13#[serde(rename_all = "camelCase")]
14pub struct TrackRelease {
15    #[serde(default, skip_serializing_if = "Option::is_none")]
16    pub name: Option<String>,
17    #[serde(default)]
18    pub version_codes: Vec<String>,
19    #[serde(default, skip_serializing_if = "Option::is_none")]
20    pub status: Option<String>,
21    #[serde(default, skip_serializing_if = "Option::is_none")]
22    pub user_fraction: Option<f64>,
23    #[serde(default)]
24    pub release_notes: Vec<LocalizedText>,
25    #[serde(default, skip_serializing_if = "Option::is_none")]
26    pub country_targeting: Option<serde_json::Value>,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct Track {
31    pub track: String,
32    #[serde(default)]
33    pub releases: Vec<TrackRelease>,
34}
35
36impl Track {
37    /// Set `locale`'s release notes on the release carrying `version_code`,
38    /// replacing any existing note for that language. Returns an error if no
39    /// release carries the code, or the text is over Play's limit.
40    pub fn set_release_notes(
41        &mut self,
42        version_code: &str,
43        locale: &str,
44        text: &str,
45    ) -> std::result::Result<(), String> {
46        let len = text.chars().count();
47        if len > crate::listing::RELEASE_NOTES_MAX_LEN {
48            return Err(format!(
49                "release notes are {len} characters; Google Play accepts at most {}",
50                crate::listing::RELEASE_NOTES_MAX_LEN
51            ));
52        }
53        let release = self
54            .releases
55            .iter_mut()
56            .find(|r| r.version_codes.iter().any(|c| c == version_code))
57            .ok_or_else(|| {
58                format!(
59                    "no release on track {} carries version code {version_code}",
60                    self.track
61                )
62            })?;
63        release.release_notes.retain(|n| n.language != locale);
64        release.release_notes.push(LocalizedText {
65            language: locale.to_string(),
66            text: text.to_string(),
67        });
68        Ok(())
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    fn track() -> Track {
77        serde_json::from_str(
78            r#"{"track":"internal","releases":[{"name":"1.2.0","versionCodes":["120"],
79            "status":"draft","releaseNotes":[{"language":"en-US","text":"old"},
80            {"language":"id","text":"lama"}]}]}"#,
81        )
82        .unwrap()
83    }
84
85    #[test]
86    fn notes_replace_only_the_given_language() {
87        let mut t = track();
88        t.set_release_notes("120", "en-US", "new").unwrap();
89        let notes = &t.releases[0].release_notes;
90        assert_eq!(notes.len(), 2);
91        assert!(notes.iter().any(|n| n.language == "id" && n.text == "lama"));
92        assert!(notes
93            .iter()
94            .any(|n| n.language == "en-US" && n.text == "new"));
95    }
96
97    #[test]
98    fn an_unknown_version_code_is_an_error() {
99        assert!(track().set_release_notes("999", "en-US", "x").is_err());
100    }
101
102    #[test]
103    fn notes_over_five_hundred_characters_are_rejected() {
104        assert!(track()
105            .set_release_notes("120", "en-US", &"x".repeat(501))
106            .is_err());
107    }
108
109    #[test]
110    fn untouched_release_fields_survive_a_round_trip() {
111        let mut t = track();
112        t.set_release_notes("120", "id", "baru").unwrap();
113        let value = serde_json::to_value(&t).unwrap();
114        assert_eq!(value["releases"][0]["status"], "draft");
115        assert_eq!(value["releases"][0]["name"], "1.2.0");
116    }
117}