馃 The Definitive Gemini Protocol Toolkit
gemini gemini-protocol gemtext parser zero-dependency toolkit ast converter html markdown cli networking
at main 138 lines 3.5 kB view raw
1use std::{borrow::Cow, collections::HashMap, fmt::Display}; 2 3/// Structure-ize a Gemini response's meta section into it's mime type and it's 4/// parameters. 5#[derive(Debug, Default, Clone, PartialEq, Eq)] 6pub struct Meta { 7 /// The mime type of a Gemini response 8 mime: String, 9 /// The parameters of a Gemini response 10 parameters: HashMap<String, String>, 11} 12 13impl Display for Meta { 14 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 15 write!(f, "{}", self.mime)?; 16 17 if !self.parameters.is_empty() { 18 write!(f, "; ")?; 19 20 let mut parameters: Vec<_> = self.parameters.iter().collect(); 21 22 parameters.sort_by(|a, b| a.0.cmp(b.0)); 23 24 for (i, (key, value)) in parameters.iter().enumerate() { 25 if i > 0 { 26 write!(f, "; ")?; 27 } 28 29 write!(f, "{key}={value}")?; 30 } 31 } 32 33 Ok(()) 34 } 35} 36 37impl Meta { 38 /// Create a new `Meta` 39 /// 40 /// # Example 41 /// 42 /// ```rust 43 /// let mut meta = germ::meta::Meta::new(); 44 /// ``` 45 #[must_use] 46 pub fn new() -> Self { Self::default() } 47 48 /// Create a `Meta` from a string 49 /// 50 /// # Example 51 /// 52 /// ```rust 53 /// assert_eq!( 54 /// germ::meta::Meta::from_string("text/gemini; hi=2; hi2=string=2").mime(), 55 /// "text/gemini", 56 /// ); 57 /// ``` 58 #[must_use] 59 pub fn from_string<'a>(meta: impl Into<std::borrow::Cow<'a, str>>) -> Self { 60 let meta = meta.into().to_string(); 61 let mut metas = meta.split(';'); 62 let mime = metas.next().unwrap_or("").to_string(); 63 let mut parameters = HashMap::new(); 64 65 for parameter in metas { 66 let trimmed = parameter.trim_start(); 67 68 // Only parse parameters containing '=' as those without are malformed 69 // according to RFC 2045 70 if let Some(equal_pos) = trimmed.find('=') { 71 let (key, value) = trimmed.split_at(equal_pos); 72 73 parameters.insert(key.to_string(), value[1..].to_string()); 74 } 75 } 76 77 Self { mime, parameters } 78 } 79 80 /// Obtain non-mutable access to the mime of the `Meta` 81 /// 82 /// # Example 83 /// 84 /// ```rust 85 /// assert_eq!( 86 /// germ::meta::Meta::from_string("text/gemini; hi=2; hi2=string=2").mime(), 87 /// "text/gemini", 88 /// ); 89 /// ``` 90 #[allow(clippy::missing_const_for_fn)] 91 #[must_use] 92 pub fn mime(&self) -> Cow<'_, str> { Cow::Borrowed(&self.mime) } 93 94 /// Obtain mutable access to the mime of the `Meta` 95 /// 96 /// # Example 97 /// 98 /// ```rust 99 /// let mut meta = germ::meta::Meta::new(); 100 /// 101 /// *meta.mime_mut() = "text/gemini".to_string(); 102 /// ``` 103 pub const fn mime_mut(&mut self) -> &mut String { &mut self.mime } 104 105 /// Obtain non-mutable access to the parameters of the `Meta` 106 /// 107 /// # Example 108 /// 109 /// ```rust 110 /// assert_eq!( 111 /// germ::meta::Meta::from_string("text/gemini; hi=2; hi2=string=2") 112 /// .parameters() 113 /// .get("hi2"), 114 /// Some(&"string=2".to_string()), 115 /// ); 116 /// ``` 117 #[must_use] 118 pub const fn parameters(&self) -> &HashMap<String, String> { 119 &self.parameters 120 } 121 122 /// Obtain mutable access to the parameters of the `Meta` 123 /// 124 /// # Example 125 /// 126 /// ```rust 127 /// let mut meta = germ::meta::Meta::new(); 128 /// let mut parameters = std::collections::HashMap::new(); 129 /// 130 /// parameters.insert("hi".to_string(), "2".to_string()); 131 /// parameters.insert("hi2".to_string(), "string=2".to_string()); 132 /// 133 /// *meta.parameters_mut() = parameters; 134 /// ``` 135 pub const fn parameters_mut(&mut self) -> &mut HashMap<String, String> { 136 &mut self.parameters 137 } 138}