1#![deny(missing_docs)]
2#![cfg_attr(test, deny(warnings))]
3
4use crate::api::types::account::{AccountData, AccountInfo, ExternalAuth, UserData};
89use crate::api::types::epic_asset::EpicAsset;
90use crate::api::types::fab_asset_manifest::DownloadInfo;
91use crate::api::types::friends::Friend;
92use crate::api::{EpicAPI};
93
94use api::types::asset_info::{AssetInfo, GameToken};
95use api::types::asset_manifest::AssetManifest;
96use api::types::artifact_service::ArtifactServiceTicket;
97use api::types::billing_account::BillingAccount;
98use api::types::catalog_item::CatalogItemPage;
99use api::types::catalog_offer::CatalogOfferPage;
100use api::types::cloud_save::CloudSaveResponse;
101use api::types::currency::CurrencyPage;
102use api::types::download_manifest::DownloadManifest;
103use api::types::entitlement::Entitlement;
104use api::types::library::Library;
105use api::types::presence::PresenceUpdate;
106use api::types::price::PriceResponse;
107use api::types::quick_purchase::QuickPurchaseResponse;
108use api::types::service_status::ServiceStatus;
109use api::types::uplay::{
110 UplayClaimResult, UplayCodesResult, UplayGraphQLResponse, UplayRedeemResult,
111};
112use api::types::cosmos;
113use api::types::engine_blob;
114use api::types::fab_search;
115use api::types::fab_taxonomy;
116use api::types::fab_entitlement;
117use api::types::account::TokenVerification;
118use log::{error, info, warn};
119use crate::api::error::EpicAPIError;
120
121pub mod api;
123
124#[derive(Debug, Clone)]
137pub struct EpicGames {
138 egs: EpicAPI,
139}
140
141impl Default for EpicGames {
142 fn default() -> Self {
143 Self::new()
144 }
145}
146
147impl EpicGames {
148 pub fn new() -> Self {
150 EpicGames {
151 egs: EpicAPI::new(),
152 }
153 }
154
155 pub fn is_logged_in(&self) -> bool {
160 if let Some(exp) = self.egs.user_data.expires_at {
161 let now = chrono::offset::Utc::now();
162 let td = exp - now;
163 if td.num_seconds() > 600 {
164 return true;
165 }
166 }
167 false
168 }
169
170 pub fn user_details(&self) -> UserData {
176 self.egs.user_data.clone()
177 }
178
179 pub fn set_user_details(&mut self, user_details: UserData) {
185 self.egs.user_data.update(user_details);
186 }
187
188 pub async fn try_auth_code(
190 &mut self,
191 exchange_token: Option<String>,
192 authorization_code: Option<String>,
193 ) -> Result<bool, EpicAPIError> {
194 self.egs
195 .start_session(exchange_token, authorization_code)
196 .await
197 }
198
199 pub async fn auth_code(
203 &mut self,
204 exchange_token: Option<String>,
205 authorization_code: Option<String>,
206 ) -> bool {
207 self.try_auth_code(exchange_token, authorization_code)
208 .await
209 .unwrap_or(false)
210 }
211
212 pub async fn logout(&mut self) -> bool {
214 self.egs.invalidate_sesion().await
215 }
216
217 pub async fn try_login(&mut self) -> Result<bool, EpicAPIError> {
219 if let Some(exp) = self.egs.user_data.expires_at {
220 let now = chrono::offset::Utc::now();
221 let td = exp - now;
222 if td.num_seconds() > 600 {
223 info!("Trying to re-use existing login session... ");
224 let resumed = self.egs.resume_session().await.map_err(|e| {
225 warn!("{}", e);
226 e
227 })?;
228 if resumed {
229 info!("Logged in");
230 return Ok(true);
231 }
232 return Ok(false);
233 }
234 }
235 info!("Logging in...");
236 if let Some(exp) = self.egs.user_data.refresh_expires_at {
237 let now = chrono::offset::Utc::now();
238 let td = exp - now;
239 if td.num_seconds() > 600 {
240 let started = self.egs.start_session(None, None).await.map_err(|e| {
241 error!("{}", e);
242 e
243 })?;
244 if started {
245 info!("Logged in");
246 return Ok(true);
247 }
248 return Ok(false);
249 }
250 }
251 Ok(false)
252 }
253
254 pub async fn login(&mut self) -> bool {
260 if let Some(exp) = self.egs.user_data.expires_at {
261 let now = chrono::offset::Utc::now();
262 let td = exp - now;
263 if td.num_seconds() > 600 {
264 info!("Trying to re-use existing login session... ");
265 match self.egs.resume_session().await {
266 Ok(b) => {
267 if b {
268 info!("Logged in");
269 return true;
270 }
271 return false;
272 }
273 Err(e) => {
274 warn!("{}", e)
275 }
276 };
277 }
278 }
279 info!("Logging in...");
280 if let Some(exp) = self.egs.user_data.refresh_expires_at {
281 let now = chrono::offset::Utc::now();
282 let td = exp - now;
283 if td.num_seconds() > 600 {
284 match self.egs.start_session(None, None).await {
285 Ok(b) => {
286 if b {
287 info!("Logged in");
288 return true;
289 }
290 return false;
291 }
292 Err(e) => {
293 error!("{}", e)
294 }
295 }
296 }
297 }
298 false
299 }
300
301 pub async fn try_list_assets(
303 &mut self,
304 platform: Option<String>,
305 label: Option<String>,
306 ) -> Result<Vec<EpicAsset>, EpicAPIError> {
307 self.egs.assets(platform, label).await
308 }
309
310 pub async fn list_assets(
315 &mut self,
316 platform: Option<String>,
317 label: Option<String>,
318 ) -> Vec<EpicAsset> {
319 self.try_list_assets(platform, label)
320 .await
321 .unwrap_or_else(|_| Vec::new())
322 }
323
324 pub async fn try_asset_manifest(
326 &mut self,
327 platform: Option<String>,
328 label: Option<String>,
329 namespace: Option<String>,
330 item_id: Option<String>,
331 app: Option<String>,
332 ) -> Result<AssetManifest, EpicAPIError> {
333 self.egs
334 .asset_manifest(platform, label, namespace, item_id, app)
335 .await
336 }
337
338 pub async fn asset_manifest(
343 &mut self,
344 platform: Option<String>,
345 label: Option<String>,
346 namespace: Option<String>,
347 item_id: Option<String>,
348 app: Option<String>,
349 ) -> Option<AssetManifest> {
350 self.try_asset_manifest(platform, label, namespace, item_id, app)
351 .await
352 .ok()
353 }
354
355 pub async fn fab_asset_manifest(
359 &self,
360 artifact_id: &str,
361 namespace: &str,
362 asset_id: &str,
363 platform: Option<&str>,
364 ) -> Result<Vec<DownloadInfo>, EpicAPIError> {
365 match self
366 .egs
367 .fab_asset_manifest(artifact_id, namespace, asset_id, platform)
368 .await
369 {
370 Ok(a) => Ok(a),
371 Err(e) => Err(e),
372 }
373 }
374
375 pub async fn try_asset_info(
377 &mut self,
378 asset: &EpicAsset,
379 ) -> Result<Option<AssetInfo>, EpicAPIError> {
380 let mut info = self.egs.asset_info(asset).await?;
381 Ok(info.remove(asset.catalog_item_id.as_str()))
382 }
383
384 pub async fn asset_info(&mut self, asset: &EpicAsset) -> Option<AssetInfo> {
388 self.try_asset_info(asset).await.ok().flatten()
389 }
390
391 pub async fn try_account_details(&mut self) -> Result<AccountData, EpicAPIError> {
393 self.egs.account_details().await
394 }
395
396 pub async fn account_details(&mut self) -> Option<AccountData> {
400 self.try_account_details().await.ok()
401 }
402
403 pub async fn try_account_ids_details(
405 &mut self,
406 ids: Vec<String>,
407 ) -> Result<Vec<AccountInfo>, EpicAPIError> {
408 self.egs.account_ids_details(ids).await
409 }
410
411 pub async fn account_ids_details(&mut self, ids: Vec<String>) -> Option<Vec<AccountInfo>> {
415 self.try_account_ids_details(ids).await.ok()
416 }
417
418 pub async fn try_account_friends(
420 &mut self,
421 include_pending: bool,
422 ) -> Result<Vec<Friend>, EpicAPIError> {
423 self.egs.account_friends(include_pending).await
424 }
425
426 pub async fn account_friends(&mut self, include_pending: bool) -> Option<Vec<Friend>> {
430 self.try_account_friends(include_pending).await.ok()
431 }
432
433 pub async fn try_game_token(&mut self) -> Result<GameToken, EpicAPIError> {
435 self.egs.game_token().await
436 }
437
438 pub async fn game_token(&mut self) -> Option<GameToken> {
442 self.try_game_token().await.ok()
443 }
444
445 pub async fn try_ownership_token(&mut self, asset: &EpicAsset) -> Result<String, EpicAPIError> {
447 self.egs.ownership_token(asset).await.map(|a| a.token)
448 }
449
450 pub async fn ownership_token(&mut self, asset: &EpicAsset) -> Option<String> {
454 self.try_ownership_token(asset).await.ok()
455 }
456
457 pub async fn try_user_entitlements(&mut self) -> Result<Vec<Entitlement>, EpicAPIError> {
459 self.egs.user_entitlements().await
460 }
461
462 pub async fn user_entitlements(&mut self) -> Vec<Entitlement> {
466 self.try_user_entitlements().await.unwrap_or_else(|_| Vec::new())
467 }
468
469 pub async fn try_library_items(&mut self, include_metadata: bool) -> Result<Library, EpicAPIError> {
471 self.egs.library_items(include_metadata).await
472 }
473
474 pub async fn library_items(&mut self, include_metadata: bool) -> Option<Library> {
478 self.try_library_items(include_metadata).await.ok()
479 }
480
481 pub async fn try_fab_library_items(
483 &mut self,
484 account_id: String,
485 ) -> Result<api::types::fab_library::FabLibrary, EpicAPIError> {
486 self.egs.fab_library_items(account_id).await
487 }
488
489 pub async fn fab_library_items(
493 &mut self,
494 account_id: String,
495 ) -> Option<api::types::fab_library::FabLibrary> {
496 self.try_fab_library_items(account_id).await.ok()
497 }
498
499 pub async fn asset_download_manifests(&self, manifest: AssetManifest) -> Vec<DownloadManifest> {
504 self.egs.asset_download_manifests(manifest).await
505 }
506
507 pub async fn fab_download_manifest(
511 &self,
512 download_info: DownloadInfo,
513 distribution_point_url: &str,
514 ) -> Result<DownloadManifest, EpicAPIError> {
515 self.egs
516 .fab_download_manifest(download_info, distribution_point_url)
517 .await
518 }
519
520 pub async fn try_auth_client_credentials(&mut self) -> Result<bool, EpicAPIError> {
522 self.egs.start_client_credentials_session().await
523 }
524
525 pub async fn auth_client_credentials(&mut self) -> bool {
534 self.try_auth_client_credentials().await.unwrap_or(false)
535 }
536
537 pub async fn try_external_auths(&self, account_id: &str) -> Result<Vec<ExternalAuth>, EpicAPIError> {
539 self.egs.external_auths(account_id).await
540 }
541
542 pub async fn external_auths(&self, account_id: &str) -> Option<Vec<ExternalAuth>> {
549 self.try_external_auths(account_id).await.ok()
550 }
551
552 pub async fn try_sso_domains(&self) -> Result<Vec<String>, EpicAPIError> {
554 self.egs.sso_domains().await
555 }
556
557 pub async fn sso_domains(&self) -> Option<Vec<String>> {
564 self.try_sso_domains().await.ok()
565 }
566
567 pub async fn try_catalog_items(
569 &self,
570 namespace: &str,
571 start: i64,
572 count: i64,
573 ) -> Result<CatalogItemPage, EpicAPIError> {
574 self.egs.catalog_items(namespace, start, count).await
575 }
576
577 pub async fn catalog_items(
586 &self,
587 namespace: &str,
588 start: i64,
589 count: i64,
590 ) -> Option<CatalogItemPage> {
591 self.try_catalog_items(namespace, start, count).await.ok()
592 }
593
594 pub async fn try_catalog_offers(
596 &self,
597 namespace: &str,
598 start: i64,
599 count: i64,
600 ) -> Result<CatalogOfferPage, EpicAPIError> {
601 self.egs.catalog_offers(namespace, start, count).await
602 }
603
604 pub async fn catalog_offers(
612 &self,
613 namespace: &str,
614 start: i64,
615 count: i64,
616 ) -> Option<CatalogOfferPage> {
617 self.try_catalog_offers(namespace, start, count).await.ok()
618 }
619
620 pub async fn try_bulk_catalog_items(
622 &self,
623 items: &[(&str, &str)],
624 ) -> Result<std::collections::HashMap<String, std::collections::HashMap<String, AssetInfo>>, EpicAPIError> {
625 self.egs.bulk_catalog_items(items).await
626 }
627
628 pub async fn bulk_catalog_items(
636 &self,
637 items: &[(&str, &str)],
638 ) -> Option<std::collections::HashMap<String, std::collections::HashMap<String, AssetInfo>>> {
639 self.try_bulk_catalog_items(items).await.ok()
640 }
641
642 pub async fn try_currencies(&self, start: i64, count: i64) -> Result<CurrencyPage, EpicAPIError> {
644 self.egs.currencies(start, count).await
645 }
646
647 pub async fn currencies(&self, start: i64, count: i64) -> Option<CurrencyPage> {
654 self.try_currencies(start, count).await.ok()
655 }
656
657 pub async fn try_library_state_token_status(
659 &self,
660 token_id: &str,
661 ) -> Result<bool, EpicAPIError> {
662 self.egs.library_state_token_status(token_id).await
663 }
664
665 pub async fn library_state_token_status(&self, token_id: &str) -> Option<bool> {
673 self.try_library_state_token_status(token_id).await.ok()
674 }
675
676 pub async fn try_service_status(
678 &self,
679 service_id: &str,
680 ) -> Result<Vec<ServiceStatus>, EpicAPIError> {
681 self.egs.service_status(service_id).await
682 }
683
684 pub async fn service_status(&self, service_id: &str) -> Option<Vec<ServiceStatus>> {
692 self.try_service_status(service_id).await.ok()
693 }
694
695 pub async fn try_offer_prices(
697 &self,
698 namespace: &str,
699 offer_ids: &[String],
700 country: &str,
701 ) -> Result<PriceResponse, EpicAPIError> {
702 self.egs.offer_prices(namespace, offer_ids, country).await
703 }
704
705 pub async fn offer_prices(
713 &self,
714 namespace: &str,
715 offer_ids: &[String],
716 country: &str,
717 ) -> Option<PriceResponse> {
718 self.try_offer_prices(namespace, offer_ids, country).await.ok()
719 }
720
721 pub async fn try_quick_purchase(
723 &self,
724 namespace: &str,
725 offer_id: &str,
726 ) -> Result<QuickPurchaseResponse, EpicAPIError> {
727 self.egs.quick_purchase(namespace, offer_id).await
728 }
729
730 pub async fn quick_purchase(
738 &self,
739 namespace: &str,
740 offer_id: &str,
741 ) -> Option<QuickPurchaseResponse> {
742 self.try_quick_purchase(namespace, offer_id).await.ok()
743 }
744
745 pub async fn try_billing_account(&self) -> Result<BillingAccount, EpicAPIError> {
747 self.egs.billing_account().await
748 }
749
750 pub async fn billing_account(&self) -> Option<BillingAccount> {
757 self.try_billing_account().await.ok()
758 }
759
760 pub async fn update_presence(
767 &self,
768 session_id: &str,
769 body: &PresenceUpdate,
770 ) -> Result<(), EpicAPIError> {
771 self.egs.update_presence(session_id, body).await
772 }
773
774 pub async fn try_fab_file_download_info(
776 &self,
777 listing_id: &str,
778 format_id: &str,
779 file_id: &str,
780 ) -> Result<DownloadInfo, EpicAPIError> {
781 self.egs
782 .fab_file_download_info(listing_id, format_id, file_id)
783 .await
784 }
785
786 pub async fn fab_file_download_info(
795 &self,
796 listing_id: &str,
797 format_id: &str,
798 file_id: &str,
799 ) -> Option<DownloadInfo> {
800 self.try_fab_file_download_info(listing_id, format_id, file_id)
801 .await
802 .ok()
803 }
804
805 pub async fn cloud_save_list(
812 &self,
813 app_name: Option<&str>,
814 manifests: bool,
815 ) -> Result<CloudSaveResponse, EpicAPIError> {
816 self.egs.cloud_save_list(app_name, manifests).await
817 }
818
819 pub async fn cloud_save_query(
823 &self,
824 app_name: &str,
825 filenames: &[String],
826 ) -> Result<CloudSaveResponse, EpicAPIError> {
827 self.egs.cloud_save_query(app_name, filenames).await
828 }
829
830 pub async fn cloud_save_delete(&self, path: &str) -> Result<(), EpicAPIError> {
832 self.egs.cloud_save_delete(path).await
833 }
834
835 pub async fn artifact_service_ticket(
843 &self,
844 sandbox_id: &str,
845 artifact_id: &str,
846 label: Option<&str>,
847 platform: Option<&str>,
848 ) -> Result<ArtifactServiceTicket, EpicAPIError> {
849 self.egs
850 .artifact_service_ticket(sandbox_id, artifact_id, label, platform)
851 .await
852 }
853
854 pub async fn game_manifest_by_ticket(
859 &self,
860 artifact_id: &str,
861 signed_ticket: &str,
862 label: Option<&str>,
863 platform: Option<&str>,
864 ) -> Result<AssetManifest, EpicAPIError> {
865 self.egs
866 .game_manifest_by_ticket(artifact_id, signed_ticket, label, platform)
867 .await
868 }
869
870 pub async fn launcher_manifests(
872 &self,
873 platform: Option<&str>,
874 label: Option<&str>,
875 ) -> Result<AssetManifest, EpicAPIError> {
876 self.egs.launcher_manifests(platform, label).await
877 }
878
879 pub async fn delta_manifest(
883 &self,
884 base_url: &str,
885 old_build_id: &str,
886 new_build_id: &str,
887 ) -> Option<Vec<u8>> {
888 self.egs
889 .delta_manifest(base_url, old_build_id, new_build_id)
890 .await
891 }
892
893 pub async fn auth_sid(&mut self, sid: &str) -> Result<bool, EpicAPIError> {
900 self.egs.auth_sid(sid).await
901 }
902
903 pub async fn store_get_uplay_codes(
907 &self,
908 ) -> Result<UplayGraphQLResponse<UplayCodesResult>, EpicAPIError> {
909 self.egs.store_get_uplay_codes().await
910 }
911
912 pub async fn store_claim_uplay_code(
914 &self,
915 uplay_account_id: &str,
916 game_id: &str,
917 ) -> Result<UplayGraphQLResponse<UplayClaimResult>, EpicAPIError> {
918 self.egs
919 .store_claim_uplay_code(uplay_account_id, game_id)
920 .await
921 }
922
923 pub async fn store_redeem_uplay_codes(
925 &self,
926 uplay_account_id: &str,
927 ) -> Result<UplayGraphQLResponse<UplayRedeemResult>, EpicAPIError> {
928 self.egs.store_redeem_uplay_codes(uplay_account_id).await
929 }
930
931 pub async fn cosmos_session_setup(
936 &self,
937 exchange_code: &str,
938 ) -> Result<cosmos::CosmosAuthResponse, EpicAPIError> {
939 self.egs.cosmos_session_setup(exchange_code).await
940 }
941
942 pub async fn cosmos_auth_upgrade(
944 &self,
945 ) -> Result<cosmos::CosmosAuthResponse, EpicAPIError> {
946 self.egs.cosmos_auth_upgrade().await
947 }
948
949 pub async fn cosmos_eula_check(&self, eula_id: &str, locale: &str) -> Option<bool> {
951 self.egs
952 .cosmos_eula_check(eula_id, locale)
953 .await
954 .ok()
955 .map(|r| r.accepted)
956 }
957
958 pub async fn try_cosmos_eula_check(
960 &self,
961 eula_id: &str,
962 locale: &str,
963 ) -> Result<cosmos::CosmosEulaResponse, EpicAPIError> {
964 self.egs.cosmos_eula_check(eula_id, locale).await
965 }
966
967 pub async fn cosmos_eula_accept(
969 &self,
970 eula_id: &str,
971 locale: &str,
972 version: u32,
973 ) -> Option<bool> {
974 self.egs
975 .cosmos_eula_accept(eula_id, locale, version)
976 .await
977 .ok()
978 .map(|r| r.accepted)
979 }
980
981 pub async fn try_cosmos_eula_accept(
983 &self,
984 eula_id: &str,
985 locale: &str,
986 version: u32,
987 ) -> Result<cosmos::CosmosEulaResponse, EpicAPIError> {
988 self.egs.cosmos_eula_accept(eula_id, locale, version).await
989 }
990
991 pub async fn cosmos_account(&self) -> Option<cosmos::CosmosAccount> {
993 self.egs.cosmos_account().await.ok()
994 }
995
996 pub async fn try_cosmos_account(&self) -> Result<cosmos::CosmosAccount, EpicAPIError> {
998 self.egs.cosmos_account().await
999 }
1000
1001 pub async fn engine_versions(
1003 &self,
1004 platform: &str,
1005 ) -> Option<engine_blob::EngineBlobsResponse> {
1006 self.egs.engine_versions(platform).await.ok()
1007 }
1008
1009 pub async fn try_engine_versions(
1011 &self,
1012 platform: &str,
1013 ) -> Result<engine_blob::EngineBlobsResponse, EpicAPIError> {
1014 self.egs.engine_versions(platform).await
1015 }
1016
1017 pub async fn fab_search(
1021 &self,
1022 params: &fab_search::FabSearchParams,
1023 ) -> Option<fab_search::FabSearchResults> {
1024 self.egs.fab_search(params).await.ok()
1025 }
1026
1027 pub async fn try_fab_search(
1029 &self,
1030 params: &fab_search::FabSearchParams,
1031 ) -> Result<fab_search::FabSearchResults, EpicAPIError> {
1032 self.egs.fab_search(params).await
1033 }
1034
1035 pub async fn fab_listing(&self, uid: &str) -> Option<fab_search::FabListingDetail> {
1037 self.egs.fab_listing(uid).await.ok()
1038 }
1039
1040 pub async fn try_fab_listing(
1042 &self,
1043 uid: &str,
1044 ) -> Result<fab_search::FabListingDetail, EpicAPIError> {
1045 self.egs.fab_listing(uid).await
1046 }
1047
1048 pub async fn fab_listing_ue_formats(
1050 &self,
1051 uid: &str,
1052 ) -> Option<Vec<fab_search::FabListingUeFormat>> {
1053 self.egs.fab_listing_ue_formats(uid).await.ok()
1054 }
1055
1056 pub async fn try_fab_listing_ue_formats(
1058 &self,
1059 uid: &str,
1060 ) -> Result<Vec<fab_search::FabListingUeFormat>, EpicAPIError> {
1061 self.egs.fab_listing_ue_formats(uid).await
1062 }
1063
1064 pub async fn fab_listing_state(
1066 &self,
1067 uid: &str,
1068 ) -> Option<fab_search::FabListingState> {
1069 self.egs.fab_listing_state(uid).await.ok()
1070 }
1071
1072 pub async fn try_fab_listing_state(
1074 &self,
1075 uid: &str,
1076 ) -> Result<fab_search::FabListingState, EpicAPIError> {
1077 self.egs.fab_listing_state(uid).await
1078 }
1079
1080 pub async fn fab_listing_states_bulk(
1082 &self,
1083 listing_ids: &[&str],
1084 ) -> Option<Vec<fab_search::FabListingState>> {
1085 self.egs.fab_listing_states_bulk(listing_ids).await.ok()
1086 }
1087
1088 pub async fn try_fab_listing_states_bulk(
1090 &self,
1091 listing_ids: &[&str],
1092 ) -> Result<Vec<fab_search::FabListingState>, EpicAPIError> {
1093 self.egs.fab_listing_states_bulk(listing_ids).await
1094 }
1095
1096 pub async fn fab_bulk_prices(
1098 &self,
1099 offer_ids: &[&str],
1100 ) -> Option<fab_search::FabBulkPricesResponse> {
1101 self.egs.fab_bulk_prices(offer_ids).await.ok()
1102 }
1103
1104 pub async fn try_fab_bulk_prices(
1106 &self,
1107 offer_ids: &[&str],
1108 ) -> Result<fab_search::FabBulkPricesResponse, EpicAPIError> {
1109 self.egs.fab_bulk_prices(offer_ids).await
1110 }
1111
1112 pub async fn fab_listing_ownership(
1114 &self,
1115 uid: &str,
1116 ) -> Option<fab_search::FabOwnership> {
1117 self.egs.fab_listing_ownership(uid).await.ok()
1118 }
1119
1120 pub async fn try_fab_listing_ownership(
1122 &self,
1123 uid: &str,
1124 ) -> Result<fab_search::FabOwnership, EpicAPIError> {
1125 self.egs.fab_listing_ownership(uid).await
1126 }
1127
1128 pub async fn fab_listing_prices(
1130 &self,
1131 uid: &str,
1132 ) -> Option<Vec<fab_search::FabPriceInfo>> {
1133 self.egs.fab_listing_prices(uid).await.ok()
1134 }
1135
1136 pub async fn try_fab_listing_prices(
1138 &self,
1139 uid: &str,
1140 ) -> Result<Vec<fab_search::FabPriceInfo>, EpicAPIError> {
1141 self.egs.fab_listing_prices(uid).await
1142 }
1143
1144 pub async fn fab_listing_reviews(
1146 &self,
1147 uid: &str,
1148 sort_by: Option<&str>,
1149 cursor: Option<&str>,
1150 ) -> Option<fab_search::FabReviewsResponse> {
1151 self.egs.fab_listing_reviews(uid, sort_by, cursor).await.ok()
1152 }
1153
1154 pub async fn try_fab_listing_reviews(
1156 &self,
1157 uid: &str,
1158 sort_by: Option<&str>,
1159 cursor: Option<&str>,
1160 ) -> Result<fab_search::FabReviewsResponse, EpicAPIError> {
1161 self.egs.fab_listing_reviews(uid, sort_by, cursor).await
1162 }
1163
1164 pub async fn fab_licenses(&self) -> Option<Vec<fab_taxonomy::FabLicenseType>> {
1168 self.egs.fab_licenses().await.ok()
1169 }
1170
1171 pub async fn try_fab_licenses(
1173 &self,
1174 ) -> Result<Vec<fab_taxonomy::FabLicenseType>, EpicAPIError> {
1175 self.egs.fab_licenses().await
1176 }
1177
1178 pub async fn fab_format_groups(&self) -> Option<Vec<fab_taxonomy::FabFormatGroup>> {
1180 self.egs.fab_format_groups().await.ok()
1181 }
1182
1183 pub async fn try_fab_format_groups(
1185 &self,
1186 ) -> Result<Vec<fab_taxonomy::FabFormatGroup>, EpicAPIError> {
1187 self.egs.fab_format_groups().await
1188 }
1189
1190 pub async fn fab_tag_groups(&self) -> Option<Vec<fab_taxonomy::FabTagGroup>> {
1192 self.egs.fab_tag_groups().await.ok()
1193 }
1194
1195 pub async fn try_fab_tag_groups(
1197 &self,
1198 ) -> Result<Vec<fab_taxonomy::FabTagGroup>, EpicAPIError> {
1199 self.egs.fab_tag_groups().await
1200 }
1201
1202 pub async fn fab_ue_versions(&self) -> Option<Vec<String>> {
1204 self.egs.fab_ue_versions().await.ok()
1205 }
1206
1207 pub async fn try_fab_ue_versions(&self) -> Result<Vec<String>, EpicAPIError> {
1209 self.egs.fab_ue_versions().await
1210 }
1211
1212 pub async fn fab_channel(&self, slug: &str) -> Option<fab_taxonomy::FabChannel> {
1214 self.egs.fab_channel(slug).await.ok()
1215 }
1216
1217 pub async fn try_fab_channel(
1219 &self,
1220 slug: &str,
1221 ) -> Result<fab_taxonomy::FabChannel, EpicAPIError> {
1222 self.egs.fab_channel(slug).await
1223 }
1224
1225 pub async fn fab_library_entitlements(
1229 &self,
1230 params: &fab_entitlement::FabEntitlementSearchParams,
1231 ) -> Option<fab_entitlement::FabEntitlementResults> {
1232 self.egs.fab_library_entitlements(params).await.ok()
1233 }
1234
1235 pub async fn try_fab_library_entitlements(
1237 &self,
1238 params: &fab_entitlement::FabEntitlementSearchParams,
1239 ) -> Result<fab_entitlement::FabEntitlementResults, EpicAPIError> {
1240 self.egs.fab_library_entitlements(params).await
1241 }
1242
1243 pub async fn fab_csrf(&self) -> Result<(), EpicAPIError> {
1247 self.egs.fab_csrf().await
1248 }
1249
1250 pub async fn fab_user_context(&self) -> Option<fab_search::FabUserContext> {
1252 self.egs.fab_user_context().await.ok()
1253 }
1254
1255 pub async fn try_fab_user_context(
1257 &self,
1258 ) -> Result<fab_search::FabUserContext, EpicAPIError> {
1259 self.egs.fab_user_context().await
1260 }
1261
1262 pub async fn fab_add_to_library(&self, listing_uid: &str) -> Result<(), EpicAPIError> {
1264 self.egs.fab_add_to_library(listing_uid).await
1265 }
1266
1267 pub async fn fab_listing_formats(
1269 &self,
1270 listing_uid: &str,
1271 ) -> Option<Vec<fab_search::FabListingFormat>> {
1272 self.egs.fab_listing_formats(listing_uid).await.ok()
1273 }
1274
1275 pub async fn try_fab_listing_formats(
1277 &self,
1278 listing_uid: &str,
1279 ) -> Result<Vec<fab_search::FabListingFormat>, EpicAPIError> {
1280 self.egs.fab_listing_formats(listing_uid).await
1281 }
1282
1283 pub async fn cosmos_policy_aodc(&self) -> Option<cosmos::CosmosPolicyAodc> {
1287 self.egs.cosmos_policy_aodc().await.ok()
1288 }
1289
1290 pub async fn try_cosmos_policy_aodc(
1292 &self,
1293 ) -> Result<cosmos::CosmosPolicyAodc, EpicAPIError> {
1294 self.egs.cosmos_policy_aodc().await
1295 }
1296
1297 pub async fn cosmos_comm_opt_in(
1299 &self,
1300 setting: &str,
1301 ) -> Option<cosmos::CosmosCommOptIn> {
1302 self.egs.cosmos_comm_opt_in(setting).await.ok()
1303 }
1304
1305 pub async fn try_cosmos_comm_opt_in(
1307 &self,
1308 setting: &str,
1309 ) -> Result<cosmos::CosmosCommOptIn, EpicAPIError> {
1310 self.egs.cosmos_comm_opt_in(setting).await
1311 }
1312
1313 pub async fn cosmos_search(
1317 &self,
1318 query: &str,
1319 slug: Option<&str>,
1320 locale: Option<&str>,
1321 filter: Option<&str>,
1322 ) -> Option<cosmos::CosmosSearchResults> {
1323 self.try_cosmos_search(query, slug, locale, filter).await.ok()
1324 }
1325
1326 pub async fn try_cosmos_search(
1328 &self,
1329 query: &str,
1330 slug: Option<&str>,
1331 locale: Option<&str>,
1332 filter: Option<&str>,
1333 ) -> Result<cosmos::CosmosSearchResults, EpicAPIError> {
1334 self.egs.cosmos_search(query, slug, locale, filter).await
1335 }
1336
1337 pub async fn verify_access_token(&self, include_perms: bool) -> Option<TokenVerification> {
1341 self.try_verify_access_token(include_perms).await.ok()
1342 }
1343
1344 pub async fn try_verify_access_token(
1346 &self,
1347 include_perms: bool,
1348 ) -> Result<TokenVerification, EpicAPIError> {
1349 self.egs.verify_token(include_perms).await
1350 }
1351}
1352
1353#[cfg(test)]
1354mod facade_tests {
1355 use super::*;
1356 use crate::api::types::account::UserData;
1357 use chrono::{Duration, Utc};
1358
1359 #[test]
1360 fn new_creates_instance() {
1361 let egs = EpicGames::new();
1362 assert!(!egs.is_logged_in());
1363 }
1364
1365 #[test]
1366 fn default_same_as_new() {
1367 let egs = EpicGames::default();
1368 assert!(!egs.is_logged_in());
1369 }
1370
1371 #[test]
1372 fn user_details_default_empty() {
1373 let egs = EpicGames::new();
1374 assert!(egs.user_details().access_token.is_none());
1375 }
1376
1377 #[test]
1378 fn set_and_get_user_details() {
1379 let mut egs = EpicGames::new();
1380 let mut ud = UserData::new();
1381 ud.display_name = Some("TestUser".to_string());
1382 egs.set_user_details(ud);
1383 assert_eq!(egs.user_details().display_name, Some("TestUser".to_string()));
1384 }
1385
1386 #[test]
1387 fn is_logged_in_expired() {
1388 let mut egs = EpicGames::new();
1389 let mut ud = UserData::new();
1390 ud.expires_at = Some(Utc::now() - Duration::hours(1));
1391 egs.set_user_details(ud);
1392 assert!(!egs.is_logged_in());
1393 }
1394
1395 #[test]
1396 fn is_logged_in_valid() {
1397 let mut egs = EpicGames::new();
1398 let mut ud = UserData::new();
1399 ud.expires_at = Some(Utc::now() + Duration::hours(2));
1400 egs.set_user_details(ud);
1401 assert!(egs.is_logged_in());
1402 }
1403
1404 #[test]
1405 fn is_logged_in_within_600s_threshold() {
1406 let mut egs = EpicGames::new();
1407 let mut ud = UserData::new();
1408 ud.expires_at = Some(Utc::now() + Duration::seconds(500));
1409 egs.set_user_details(ud);
1410 assert!(!egs.is_logged_in());
1411 }
1412}