boj_client/query/metadata.rs
1use crate::error::BojError;
2
3use super::options::{CsvEncoding, Format, Language};
4use super::validation::validate_db;
5
6/// Query builder for the `getMetadata` endpoint.
7///
8/// Constraints enforced at build time:
9/// - `DB` must be non-empty ASCII and must not contain commas.
10///
11/// # Examples
12///
13/// ```
14/// use boj_client::query::{Format, Language, MetadataQuery};
15///
16/// let _query = MetadataQuery::new("ME")?
17/// .with_format(Format::Json)
18/// .with_lang(Language::En);
19/// # Ok::<(), boj_client::error::BojError>(())
20/// ```
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct MetadataQuery {
23 db: String,
24 format: Option<Format>,
25 lang: Option<Language>,
26}
27
28impl MetadataQuery {
29 /// Creates a `getMetadata` query.
30 ///
31 /// # Examples
32 ///
33 /// ```
34 /// use boj_client::query::MetadataQuery;
35 ///
36 /// let _query = MetadataQuery::new("ME")?;
37 /// # Ok::<(), boj_client::error::BojError>(())
38 /// ```
39 ///
40 /// # Errors
41 ///
42 /// Returns [`BojError`] if `db` violates API constraints.
43 pub fn new(db: impl Into<String>) -> Result<Self, BojError> {
44 let db = db.into();
45 validate_db(&db)?;
46
47 Ok(Self {
48 db: db.to_ascii_uppercase(),
49 format: None,
50 lang: None,
51 })
52 }
53
54 /// Sets the response format (`json` or `csv`).
55 ///
56 /// # Examples
57 ///
58 /// ```
59 /// use boj_client::query::{Format, MetadataQuery};
60 ///
61 /// let _query = MetadataQuery::new("ME")?.with_format(Format::Csv);
62 /// # Ok::<(), boj_client::error::BojError>(())
63 /// ```
64 pub fn with_format(mut self, format: Format) -> Self {
65 self.format = Some(format);
66 self
67 }
68
69 /// Sets response language (`jp` or `en`).
70 ///
71 /// # Examples
72 ///
73 /// ```
74 /// use boj_client::query::{Language, MetadataQuery};
75 ///
76 /// let _query = MetadataQuery::new("ME")?.with_lang(Language::Jp);
77 /// # Ok::<(), boj_client::error::BojError>(())
78 /// ```
79 pub fn with_lang(mut self, lang: Language) -> Self {
80 self.lang = Some(lang);
81 self
82 }
83
84 pub(crate) fn endpoint(&self) -> &'static str {
85 "/api/v1/getMetadata"
86 }
87
88 pub(crate) fn query_pairs(&self) -> Vec<(String, String)> {
89 let mut pairs = Vec::new();
90 if let Some(format) = self.format {
91 pairs.push(("format".to_string(), format.as_query_value().to_string()));
92 }
93 if let Some(lang) = self.lang {
94 pairs.push(("lang".to_string(), lang.as_query_value().to_string()));
95 }
96 pairs.push(("db".to_string(), self.db.clone()));
97 pairs
98 }
99
100 pub(crate) fn csv_encoding_hint(&self) -> CsvEncoding {
101 match self.lang.unwrap_or_default() {
102 Language::Jp => CsvEncoding::ShiftJis,
103 Language::En => CsvEncoding::Utf8,
104 }
105 }
106}