use crate::config::{FundingConfig, StyleConfig};
use crate::errors::{OrandaError, Result};
use crate::site::markdown::to_html;
use axoasset::LocalAsset;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
pub struct Funding {
pub content: HashMap<FundingType, FundingContent>,
pub docs_content: Option<String>,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialOrd, PartialEq, Eq, Hash, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum FundingType {
Github,
Patreon,
OpenCollective,
KoFi,
Tidelift,
CommunityBridge,
Issuehunt,
Liberapay,
Custom,
}
impl std::fmt::Display for FundingType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let string = match self {
FundingType::Github => "github",
FundingType::Patreon => "patreon",
FundingType::OpenCollective => "open_collective",
FundingType::KoFi => "ko_fi",
FundingType::Tidelift => "tide_lift",
FundingType::CommunityBridge => "community_bridge",
FundingType::Issuehunt => "issue_hunt",
FundingType::Liberapay => "liberapay",
FundingType::Custom => "custom",
};
string.fmt(f)
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(untagged)]
pub enum FundingContent {
One(String),
Multiple(Vec<String>),
}
impl Funding {
pub fn new(funding_cfg: &FundingConfig, style_cfg: &StyleConfig) -> Result<Self> {
let mut funding = if let Some(yml_path) = &funding_cfg.yml_path {
match LocalAsset::load_string(yml_path) {
Ok(res) => {
let parsed_response = parse_response(res)?;
Self {
content: parsed_response,
docs_content: None,
}
}
Err(e) => {
let warning = OrandaError::FundingLoadFailed {
path: yml_path.into(),
details: e,
};
eprintln!("{:?}", miette::Report::new(warning));
Self::default()
}
}
} else {
Self::default()
};
if let Some(md_path) = &funding_cfg.md_path {
let res = LocalAsset::load_string(md_path)?;
let html = to_html(&res, &style_cfg.syntax_theme)?;
funding.docs_content = Some(html);
}
if let Some(preferred) = funding_cfg.preferred_funding.as_ref() {
if !funding.content.contains_key(preferred) {
let mut found = String::new();
let mut first = false;
for key in funding.content.keys() {
if !first {
found.push_str(", ");
}
found.push_str(&key.to_string());
first = false;
}
let help = if found.is_empty() {
"We didn't find any funding sources, maybe remove preferred_funding from your config?".to_owned()
} else {
format!("We found the following funding sources: {found}")
};
return Err(OrandaError::PreferredFundingNotFound {
preferred: preferred.to_string(),
help,
});
}
}
Ok(funding)
}
}
fn parse_response(contents: String) -> Result<HashMap<FundingType, FundingContent>> {
let deserialized_map = serde_yaml::from_str(&contents);
match deserialized_map {
Ok(yaml) => Ok(yaml),
Err(e) => Err(OrandaError::GithubFundingParseError {
details: e.to_string(),
}),
}
}