news-flash 3.0.1

Base library for a modern feed reader
Documentation
use super::Inoreader;
use super::config::AccountConfig;
use super::oauth::InoreaderOAuth;
use crate::feed_api::{ApiMetadata, FeedApi, FeedApiError, FeedApiResult, Portal};
use crate::models::{
    ApiSecret, LoginGUI, OAuthLoginGUI, PluginID, PluginIcon, PluginInfo, ServiceLicense, ServicePrice, ServiceType, Url, VectorIcon,
};
use crate::util;
use greader_api::{AuthData, GReaderApi, InoreaderAuth};
use rust_embed::RustEmbed;
use std::path::Path;
use std::str;
use std::sync::Arc;
use tokio::sync::RwLock;

#[derive(RustEmbed)]
#[folder = "src/feed_api_implementations/inoreader/icons"]
struct InoreaderResources;

pub struct InoreaderMetadata;

impl InoreaderMetadata {
    pub fn get_id() -> PluginID {
        PluginID::new("inoreader")
    }
}

impl ApiMetadata for InoreaderMetadata {
    fn id(&self) -> PluginID {
        Self::get_id()
    }

    fn info(&self) -> FeedApiResult<PluginInfo> {
        let icon_data = InoreaderResources::get("feed-service-inoreader.svg").ok_or(FeedApiError::Resource)?;
        let icon = VectorIcon {
            data: icon_data.data.to_vec(),
            width: 48,
            height: 48,
        };
        let icon = PluginIcon::Vector(icon);

        let symbolic_icon_data = InoreaderResources::get("feed-service-inoreader-symbolic.svg").ok_or(FeedApiError::Resource)?;
        let symbolic_icon = VectorIcon {
            data: symbolic_icon_data.data.to_vec(),
            width: 48,
            height: 48,
        };
        let symbolic_icon = PluginIcon::Vector(symbolic_icon);

        let login_fn = move |secret: Option<&ApiSecret>| Url::parse(&InoreaderOAuth::new().login_url(secret)).ok();

        let api_info = InoreaderOAuth::new();
        let login_gui = LoginGUI::OAuth(Box::new(OAuthLoginGUI {
            login_website: Box::new(login_fn),
            catch_redirect: Some(api_info.redirect_uri()),
            custom_api_secret: true,
            custom_api_secret_url: Some(api_info.create_secret_url.clone()),
            embeded_api_secret: true,
            create_secret_url: Some(api_info.create_secret_url),
        }));

        Ok(PluginInfo {
            id: self.id(),
            name: String::from("Inoreader"),
            icon: Some(icon),
            icon_symbolic: Some(symbolic_icon),
            website: Some(crate::models::Url::parse("https://inoreader.com").unwrap()),
            service_type: ServiceType::Remote { self_hosted: false },
            license_type: ServiceLicense::GenericProprietary,
            service_price: ServicePrice::PaidPremimum,
            login_gui,
        })
    }

    fn get_instance(&self, path: &Path, portal: Box<dyn Portal>, user_api_secret: Option<ApiSecret>) -> FeedApiResult<Box<dyn FeedApi>> {
        let mut account_config = AccountConfig::load(path)?;
        let mut api: Option<GReaderApi> = None;
        let mut logged_in: bool = false;

        let (client_id, client_secret) = if let Some(user_api_secret) = user_api_secret {
            // custom API secrets entered by the user during login
            account_config.set_custom_api_secret(Some(&user_api_secret));

            (user_api_secret.client_id, user_api_secret.client_secret)
        } else if let Some(user_api_secret) = account_config.get_custom_api_secret() {
            // load previous custom API secrets from config
            (user_api_secret.client_id, user_api_secret.client_secret)
        } else {
            // no custom API secrets: use the default one
            let api_info = InoreaderOAuth::new();
            (api_info.client_id, api_info.client_secret)
        };

        if let Some(access_token) = account_config.get_access_token()
            && let Some(refresh_token) = account_config.get_refresh_token()
            && let Some(expires_at) = account_config.get_token_expires()
        {
            let timestamp = expires_at.parse::<i64>().map_err(|_| FeedApiError::Unknown)?;
            let expires_at = util::timestamp_to_datetime(timestamp);

            api = Some(GReaderApi::new(
                &crate::models::Url::parse("https://inoreader.com").unwrap(),
                AuthData::Inoreader(InoreaderAuth {
                    client_id,
                    client_secret,
                    access_token,
                    refresh_token,
                    expires_at,
                }),
            ));
            logged_in = true;
        }

        let inoreader = Inoreader {
            api,
            portal: Arc::new(portal),
            logged_in,
            config: Arc::new(RwLock::new(account_config)),
        };
        let template = Box::new(inoreader);
        Ok(template)
    }
}