cbr_client/
client_common.rs1pub const DEFAULT_BASE_URL: &str = "https://www.cbr.ru/dataservice";
3
4pub(crate) fn normalize_base_url(base_url: impl AsRef<str>) -> String {
5 let value = base_url.as_ref().trim();
6 if value.is_empty() {
7 DEFAULT_BASE_URL.to_owned()
8 } else {
9 value.trim_end_matches('/').to_owned()
10 }
11}
12
13pub(crate) fn endpoint(base_url: &str, path: &str) -> String {
14 format!("{}/{}", base_url, path.trim_start_matches('/'))
15}
16
17macro_rules! configure_reqwest_builder {
18 (
19 $builder:expr,
20 timeout = $timeout:expr,
21 use_system_proxy = $use_system_proxy:expr,
22 proxy_url = $proxy_url:expr,
23 user_agent = $user_agent:expr
24 ) => {{
25 let mut builder = $builder.timeout($timeout).http1_only();
26
27 if !$use_system_proxy && $proxy_url.is_none() {
28 builder = builder.no_proxy();
29 }
30
31 if let Some(proxy_url) = $proxy_url {
32 let proxy = reqwest::Proxy::all(proxy_url).map_err(crate::error::CbrError::build)?;
33 builder = builder.proxy(proxy);
34 }
35
36 if let Some(user_agent) = $user_agent {
37 builder = builder.user_agent(user_agent);
38 }
39
40 builder
41 }};
42}
43
44macro_rules! cbr_endpoint_methods {
45 ($impl_macro:ident) => {
46 $impl_macro!(
47 "Возвращает список публикаций (`/publications`).",
48 publications,
49 (),
50 Vec<Publication>,
51 "/publications",
52 no_query
53 );
54 $impl_macro!(
55 "Возвращает список показателей публикации (`/datasets`).",
56 datasets,
57 (publication_id: PublicationId),
58 Vec<Dataset>,
59 "/datasets",
60 query(publication_id_query(publication_id))
61 );
62 $impl_macro!(
63 "Возвращает разрезы показателя (`/measures`).",
64 measures,
65 (dataset_id: DatasetId),
66 MeasuresResponse,
67 "/measures",
68 query(dataset_id_query(dataset_id))
69 );
70 $impl_macro!(
71 "Возвращает доступный диапазон годов (`/years`).",
72 years,
73 (dataset_id: DatasetId, measure_id: Option<MeasureId>),
74 Vec<YearRange>,
75 "/years",
76 query(years_query(dataset_id, measure_id))
77 );
78 $impl_macro!(
79 "Возвращает диапазон годов в расширенном формате (`/yearsEx`).",
80 years_ex,
81 (publication_id: PublicationId, ids: &[DatasetId]),
82 Vec<YearRange>,
83 "/yearsEx",
84 query(years_ex_query(publication_id, ids))
85 );
86 $impl_macro!(
87 "Возвращает показатели и разрезы публикации (`/datasetsEx`).",
88 datasets_ex,
89 (publication_id: PublicationId),
90 DatasetsExResponse,
91 "/datasetsEx",
92 query(publication_id_query(publication_id))
93 );
94 $impl_macro!(
95 "Возвращает данные для таблицы (`/data`).",
96 data,
97 (query: DataQuery),
98 DataResponse,
99 "/data",
100 query(query)
101 );
102 $impl_macro!(
103 "Возвращает данные в расширенном формате (`/dataEx`).",
104 data_ex,
105 (query: DataExQuery),
106 DataExResponse,
107 "/dataEx",
108 query(query)
109 );
110 $impl_macro!(
111 "Возвращает описание (методологию) показателя (`/DatasetDescription`).",
112 dataset_description,
113 (dataset_id: DatasetId),
114 Vec<DatasetDescription>,
115 "/DatasetDescription",
116 query(dataset_id_query(dataset_id))
117 );
118 $impl_macro!(
119 "Возвращает список категорий и показателей (`/categoryNew`).",
120 category_new,
121 (),
122 CategoryNewResponse,
123 "/categoryNew",
124 no_query
125 );
126 $impl_macro!(
127 "Возвращает данные показателей (`/dataNew`).",
128 data_new,
129 (query: DataNewQuery),
130 DataNewResponse,
131 "/dataNew",
132 query(query)
133 );
134 };
135}
136
137pub(crate) use cbr_endpoint_methods;
138pub(crate) use configure_reqwest_builder;
139
140#[cfg(test)]
141mod tests {
142 use super::{DEFAULT_BASE_URL, endpoint, normalize_base_url};
143
144 #[test]
145 fn normalize_base_url_uses_default_for_empty_value() {
146 assert_eq!(normalize_base_url(" "), DEFAULT_BASE_URL);
147 }
148
149 #[test]
150 fn normalize_base_url_trims_trailing_slashes() {
151 assert_eq!(
152 normalize_base_url("https://example.com/api///"),
153 "https://example.com/api"
154 );
155 }
156
157 #[test]
158 fn endpoint_trims_leading_slashes_in_path() {
159 assert_eq!(
160 endpoint("https://example.com/api", "//datasets"),
161 "https://example.com/api/datasets"
162 );
163 }
164}