ajour_core/repository/backend/
wowi.rs1use super::*;
2use crate::config::Flavor;
3use crate::error::{DownloadError, RepositoryError};
4use crate::network::request_async;
5use crate::repository::{ReleaseChannel, RemotePackage, RepositoryKind, RepositoryPackage};
6
7use async_trait::async_trait;
8use chrono::{TimeZone, Utc};
9use isahc::AsyncReadResponseExt;
10use serde::Deserialize;
11
12use std::collections::HashMap;
13
14const API_ENDPOINT: &str = "https://api.mmoui.com/v4/game/WOW/filedetails";
15const ADDON_URL: &str = "https://www.wowinterface.com/downloads/info";
16
17#[derive(Debug, Clone)]
18pub struct WowI {
19 pub id: String,
20 pub flavor: Flavor,
21}
22
23#[async_trait]
24impl Backend for WowI {
25 async fn get_metadata(&self) -> Result<RepositoryMetadata, RepositoryError> {
26 let packages = fetch_remote_packages(&[self.id.clone()]).await?;
27
28 let package = packages
29 .into_iter()
30 .next()
31 .ok_or(RepositoryError::WowIMissingPackage {
32 id: self.id.clone(),
33 })?;
34
35 let metadata = metadata_from_wowi_package(package);
36
37 Ok(metadata)
38 }
39
40 async fn get_changelog(
41 &self,
42 _file_id: Option<i64>,
43 _tag_name: Option<String>,
44 ) -> Result<Option<String>, RepositoryError> {
45 Ok(None)
46 }
47}
48
49pub(crate) fn metadata_from_wowi_package(package: WowIPackage) -> RepositoryMetadata {
50 let mut remote_packages = HashMap::new();
51
52 {
53 let version = package.version.clone();
54 let download_url = package.download_uri.clone();
55 let date_time = Utc.timestamp(package.last_update / 1000, 0);
56
57 let package = RemotePackage {
58 version,
59 download_url,
60 date_time: Some(date_time),
61 file_id: None,
62 modules: vec![],
63 };
64
65 remote_packages.insert(ReleaseChannel::Stable, package);
67 }
68
69 let mut metadata = RepositoryMetadata::empty();
70 metadata.remote_packages = remote_packages;
71
72 let website_url = addon_url(&package.id.to_string());
73 metadata.website_url = Some(website_url.clone());
74 metadata.changelog_url = Some(format!("{}/#changelog", website_url));
75 metadata.title = Some(package.title);
76
77 metadata
78}
79
80fn api_endpoint(ids: &str) -> String {
82 format!("{}/{}.json", API_ENDPOINT, ids)
83}
84
85pub(crate) fn addon_url(id: &str) -> String {
87 format!("{}{}", ADDON_URL, id)
88}
89
90pub(crate) async fn batch_fetch_repo_packages(
91 flavor: Flavor,
92 wowi_ids: &[String],
93) -> Result<Vec<RepositoryPackage>, DownloadError> {
94 let mut wowi_repo_packages = vec![];
95
96 if wowi_ids.is_empty() {
97 return Ok(wowi_repo_packages);
98 }
99
100 let wowi_packages = wowi::fetch_remote_packages(&wowi_ids).await?;
101
102 wowi_repo_packages.extend(
103 wowi_packages
104 .into_iter()
105 .map(|package| {
106 (
107 package.id.to_string(),
108 wowi::metadata_from_wowi_package(package),
109 )
110 })
111 .filter_map(|(id, metadata)| {
112 RepositoryPackage::from_repo_id(flavor, RepositoryKind::WowI, id)
113 .ok()
114 .map(|r| r.with_metadata(metadata))
115 }),
116 );
117
118 Ok(wowi_repo_packages)
119}
120
121pub(crate) async fn fetch_remote_packages(
124 ids: &[String],
125) -> Result<Vec<WowIPackage>, DownloadError> {
126 let url = api_endpoint(&ids.join(","));
127 let timeout = Some(30);
128 let mut resp = request_async(&url, vec![], timeout).await?;
129
130 if resp.status().is_success() {
131 let packages = resp.json().await;
132 Ok(packages?)
133 } else {
134 Err(DownloadError::InvalidStatusCode {
135 code: resp.status(),
136 url,
137 })
138 }
139}
140
141#[serde(rename_all = "camelCase")]
142#[derive(Clone, Debug, Deserialize)]
143pub struct WowIPackage {
145 pub id: i64,
146 pub title: String,
147 pub version: String,
148 pub download_uri: String,
149 pub last_update: i64,
150 pub author: String,
151 pub description: String,
152}