aspero_engine/
standard.rs1use std::sync::Arc;
4
5use aspero_core::{Error, HttpClient, ResiliencePolicy};
6
7use crate::transport::{Paced, ReqwestClient};
8use crate::{Cache, Engine, EngineBuilder, Pacing};
9
10#[cfg(feature = "crossref")]
11pub use aspero_crossref::CrossrefConfig;
12#[cfg(feature = "datacite")]
13pub use aspero_datacite::DataciteConfig;
14#[cfg(feature = "doi")]
15pub use aspero_doi::DoiConfig;
16#[cfg(feature = "europepmc")]
17pub use aspero_europepmc::EuropePmcConfig;
18#[cfg(feature = "openalex")]
19pub use aspero_openalex::OpenAlexConfig;
20#[cfg(feature = "openlibrary")]
21pub use aspero_openlibrary::OpenLibraryConfig;
22
23#[derive(Clone, Default)]
25#[non_exhaustive]
26pub struct StandardConfig {
27 pub mailto: Option<String>,
29 pub cache: Option<Arc<dyn Cache>>,
31 pub pacing: Option<Arc<dyn Pacing>>,
34 #[cfg(feature = "openalex")]
35 pub openalex: Option<OpenAlexConfig>,
36 #[cfg(feature = "crossref")]
37 pub crossref: Option<CrossrefConfig>,
38 #[cfg(feature = "datacite")]
39 pub datacite: Option<DataciteConfig>,
40 #[cfg(feature = "doi")]
41 pub doi: Option<DoiConfig>,
42 #[cfg(feature = "europepmc")]
43 pub europepmc: Option<EuropePmcConfig>,
44 #[cfg(feature = "openlibrary")]
45 pub openlibrary: Option<OpenLibraryConfig>,
46}
47
48pub fn standard(config: StandardConfig) -> Result<Engine, Error> {
58 let user_agent = match &config.mailto {
59 Some(mailto) => format!("aspero/{} (mailto:{mailto})", env!("CARGO_PKG_VERSION")),
60 None => format!("aspero/{}", env!("CARGO_PKG_VERSION")),
61 };
62 #[allow(unused_variables)]
64 let client = |label: &str, policy: &ResiliencePolicy| -> Result<Arc<dyn HttpClient>, Error> {
65 let inner = Arc::new(ReqwestClient::new(&user_agent, policy.attempt_timeout)?);
66 let paced =
67 Paced::new(label, inner, policy.min_interval).max_in_flight(policy.max_in_flight);
68 Ok(Arc::new(match &config.pacing {
69 Some(pacing) => paced.shared(pacing.clone()),
70 None => paced,
71 }))
72 };
73
74 let mut builder: EngineBuilder = Engine::builder();
75 if let Some(cache) = config.cache {
76 builder = builder.cache(cache);
77 }
78
79 #[cfg(feature = "openalex")]
80 if let Some(oa) = config.openalex {
81 use aspero_openalex::OpenAlex;
82 let policy = OpenAlex::default_policy();
83 let provider = OpenAlex::new(oa, client("openalex", &policy)?);
84 builder = builder.provider(Arc::new(provider), policy, 10);
85 }
86
87 #[cfg(feature = "crossref")]
88 if let Some(mut cr) = config.crossref {
89 use aspero_crossref::Crossref;
90 if cr.mailto.is_none() {
91 cr.mailto = config.mailto.clone();
92 }
93 let mut policy = Crossref::default_policy();
94 policy.max_in_flight = if cr.mailto.is_some() { 3 } else { 1 };
98 let provider = Crossref::new(cr, client("crossref", &policy)?);
99 builder = builder.provider(Arc::new(provider), policy, 20);
100 }
101
102 #[cfg(feature = "datacite")]
103 if let Some(dc) = config.datacite {
104 use aspero_datacite::Datacite;
105 let policy = Datacite::default_policy();
106 let provider = Datacite::new(dc, client("datacite", &policy)?);
107 builder = builder.provider(Arc::new(provider), policy, 25);
108 }
109
110 #[cfg(feature = "doi")]
111 if let Some(doi) = config.doi {
112 use aspero_doi::DoiOrg;
113 let policy = DoiOrg::default_policy();
114 let provider = DoiOrg::new(doi, client("doi", &policy)?);
115 builder = builder.provider(Arc::new(provider), policy, 30);
116 }
117
118 #[cfg(feature = "europepmc")]
119 if let Some(epmc) = config.europepmc {
120 use aspero_europepmc::EuropePmc;
121 let policy = EuropePmc::default_policy();
122 let provider = EuropePmc::new(epmc, client("europepmc", &policy)?);
123 builder = builder.provider(Arc::new(provider), policy, 35);
124 }
125
126 #[cfg(feature = "openlibrary")]
127 if let Some(ol) = config.openlibrary {
128 use aspero_openlibrary::OpenLibrary;
129 let policy = OpenLibrary::default_policy();
130 let provider = OpenLibrary::new(ol, client("openlibrary", &policy)?);
131 builder = builder.provider(Arc::new(provider), policy, 40);
132 }
133
134 Ok(builder.build())
135}