/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::collections::HashMap; use client::error::ComponentError; use error_support::handle_error; use mars::error::CallbackRequestError; use parking_lot::Mutex; use url::Url as AdsClientUrl; use client::AdsClient; use http_cache::CachePolicy; use mars::ad_request::AdPlacementRequest; mod client; mod ffi; pub mod http_cache; mod mars; pub mod telemetry; pub use ffi::*; use crate::ffi::telemetry::MozAdsTelemetryWrapper; #[cfg(test)] mod test_utils; uniffi::setup_scaffolding!("ads_client"); uniffi::custom_type!(AdsClientUrl, String, { remote, try_lift: |val| Ok(AdsClientUrl::parse(&val)?), lower: |obj| obj.as_str().to_string(), }); #[derive(uniffi::Object)] pub struct MozAdsClient { inner: Mutex>, } #[uniffi::export] impl MozAdsClient { pub fn clear_cache(&self) -> AdsClientApiResult<()> { let inner = self.inner.lock(); inner .clear_cache() .map_err(|e| MozAdsClientApiError::Other { reason: format!("Failed to clear cache: {}", e), }) } #[handle_error(ComponentError)] #[uniffi::method(default(options = None))] pub fn record_click( &self, click_url: String, options: Option, ) -> AdsClientApiResult<()> { let url = AdsClientUrl::parse(&click_url) .map_err(|e| ComponentError::RecordClick(CallbackRequestError::InvalidUrl(e).into()))?; let ohttp = options.map(|o| o.ohttp).unwrap_or(false); let inner = self.inner.lock(); inner .record_click(url, ohttp) .map_err(ComponentError::RecordClick) } #[handle_error(ComponentError)] #[uniffi::method(default(options = None))] pub fn record_impression( &self, impression_url: String, options: Option, ) -> AdsClientApiResult<()> { let url = AdsClientUrl::parse(&impression_url).map_err(|e| { ComponentError::RecordImpression(CallbackRequestError::InvalidUrl(e).into()) })?; let ohttp = options.map(|o| o.ohttp).unwrap_or(false); let inner = self.inner.lock(); inner .record_impression(url, ohttp) .map_err(ComponentError::RecordImpression) } #[handle_error(ComponentError)] #[uniffi::method(default(options = None))] pub fn report_ad( &self, report_url: String, reason: MozAdsReportReason, options: Option, ) -> AdsClientApiResult<()> { let url = AdsClientUrl::parse(&report_url) .map_err(|e| ComponentError::ReportAd(CallbackRequestError::InvalidUrl(e).into()))?; let ohttp = options.map(|o| o.ohttp).unwrap_or(false); let inner = self.inner.lock(); inner .report_ad(url, reason.into(), ohttp) .map_err(ComponentError::ReportAd) } #[handle_error(ComponentError)] #[uniffi::method(default(options = None))] pub fn request_image_ads( &self, moz_ad_requests: Vec, options: Option, ) -> AdsClientApiResult> { let inner = self.inner.lock(); let requests: Vec = moz_ad_requests.iter().map(|r| r.into()).collect(); let ohttp = options.as_ref().map(|o| o.ohttp).unwrap_or(false); let cache_policy: CachePolicy = options.into(); let response = inner .request_image_ads(requests, Some(cache_policy), ohttp) .map_err(ComponentError::RequestAds)?; Ok(response.into_iter().map(|(k, v)| (k, v.into())).collect()) } #[handle_error(ComponentError)] #[uniffi::method(default(options = None))] pub fn request_spoc_ads( &self, moz_ad_requests: Vec, options: Option, ) -> AdsClientApiResult>> { let inner = self.inner.lock(); let requests: Vec = moz_ad_requests.iter().map(|r| r.into()).collect(); let ohttp = options.as_ref().map(|o| o.ohttp).unwrap_or(false); let cache_policy: CachePolicy = options.into(); let response = inner .request_spoc_ads(requests, Some(cache_policy), ohttp) .map_err(ComponentError::RequestAds)?; Ok(response .into_iter() .map(|(k, v)| (k, v.into_iter().map(|spoc| spoc.into()).collect())) .collect()) } #[handle_error(ComponentError)] #[uniffi::method(default(options = None))] pub fn request_tile_ads( &self, moz_ad_requests: Vec, options: Option, ) -> AdsClientApiResult> { let inner = self.inner.lock(); let requests: Vec = moz_ad_requests.iter().map(|r| r.into()).collect(); let ohttp = options.as_ref().map(|o| o.ohttp).unwrap_or(false); let cache_policy: CachePolicy = options.into(); let response = inner .request_tile_ads(requests, Some(cache_policy), ohttp) .map_err(ComponentError::RequestAds)?; Ok(response.into_iter().map(|(k, v)| (k, v.into())).collect()) } }