🦠 The Definitive Gemini Protocol Toolkit
gemini gemini-protocol gemtext parser zero-dependency toolkit ast converter html markdown cli networking
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor: Use latest best practices and formatting

Fuwn 884aa535 aed39348

+105 -70
+1 -1
.github/workflows/check.yaml
··· 19 19 uses: actions-rs/toolchain@v1 20 20 with: 21 21 profile: minimal 22 - toolchain: 1.79.0 22 + toolchain: stable 23 23 components: rustfmt, clippy 24 24 override: true 25 25 - name: Check ✅
+1 -1
examples/meta.rs
··· 28 28 29 29 // Convert the structured meta representation back to a string, identical to 30 30 // the original meta section 31 - println!("{}", meta.to_string()); 31 + println!("{}", meta); 32 32 33 33 // The MIME type of the meta section 34 34 println!("{}", meta.mime());
+1 -1
rust-toolchain.toml
··· 1 1 [toolchain] 2 - channel = "1.79.0" 2 + channel = "stable"
+1 -1
rustfmt.toml
··· 20 20 use_field_init_shorthand = true 21 21 use_small_heuristics = "Max" 22 22 use_try_shorthand = true 23 - version = "Two" 23 + style_edition = "2024" 24 24 wrap_comments = true
+39 -26
src/ast/container.rs
··· 16 16 // Copyright (C) 2022-2022 Fuwn <contact@fuwn.me> 17 17 // SPDX-License-Identifier: GPL-3.0-only 18 18 19 - use super::Node; 19 + use {super::Node, std::fmt::Write}; 20 20 21 21 /// An AST structure which contains an AST tree 22 22 /// ··· 52 52 /// ``` 53 53 #[must_use] 54 54 #[allow(clippy::needless_pass_by_value)] 55 - pub fn from_string(value: (impl Into<String> + ?Sized)) -> Self { 55 + pub fn from_string(value: impl Into<String>) -> Self { 56 56 Self::from_value(&value.into()) 57 57 } 58 58 ··· 82 82 )); 83 83 } 84 84 85 - if source.chars().last().map_or(false, |c| c == '\n') { 85 + if source.ends_with('\n') { 86 86 if let Some(last) = ast.last() { 87 87 if !matches!(last, Node::Whitespace) { 88 88 ast.push(Node::Whitespace); ··· 110 110 /// ); 111 111 /// ``` 112 112 #[must_use] 113 - pub fn from_nodes(nodes: Vec<Node>) -> Self { Self { inner: nodes } } 113 + pub const fn from_nodes(nodes: Vec<Node>) -> Self { Self { inner: nodes } } 114 114 115 115 #[must_use] 116 116 pub fn to_gemtext(&self) -> String { ··· 118 118 119 119 for node in &self.inner { 120 120 match node { 121 - Node::Text(text) => gemtext.push_str(&format!("{text}\n")), 122 - Node::Link { to, text } => gemtext.push_str(&format!( 123 - "=> {}{}\n", 124 - to, 125 - text.clone().map_or_else(String::new, |text| format!(" {text}")), 126 - )), 127 - Node::Heading { level, text } => 128 - gemtext.push_str(&format!("{} {}\n", "#".repeat(*level), text)), 129 - Node::List(items) => gemtext.push_str(&format!( 130 - "{}\n", 131 - items 132 - .iter() 133 - .map(|i| format!("* {i}")) 134 - .collect::<Vec<String>>() 135 - .join("\n"), 136 - )), 137 - Node::Blockquote(text) => gemtext.push_str(&format!("> {text}\n")), 138 - Node::PreformattedText { alt_text, text } => 139 - gemtext.push_str(&format!( 140 - "```{}\n{}```\n", 121 + Node::Text(text) => { 122 + let _ = writeln!(&mut gemtext, "{text}"); 123 + } 124 + Node::Link { to, text } => { 125 + let _ = writeln!( 126 + &mut gemtext, 127 + "=> {}{}", 128 + to, 129 + text.clone().map_or_else(String::new, |text| format!(" {text}")), 130 + ); 131 + } 132 + Node::Heading { level, text } => { 133 + let _ = writeln!(&mut gemtext, "{} {}", "#".repeat(*level), text); 134 + } 135 + Node::List(items) => { 136 + let _ = writeln!( 137 + &mut gemtext, 138 + "{}", 139 + items 140 + .iter() 141 + .map(|i| format!("* {i}")) 142 + .collect::<Vec<String>>() 143 + .join("\n"), 144 + ); 145 + } 146 + Node::Blockquote(text) => { 147 + let _ = writeln!(&mut gemtext, "> {text}"); 148 + } 149 + Node::PreformattedText { alt_text, text } => { 150 + let _ = writeln!( 151 + &mut gemtext, 152 + "```{}\n{}```", 141 153 alt_text.clone().unwrap_or_default(), 142 154 text 143 - )), 155 + ); 156 + } 144 157 Node::Whitespace => gemtext.push('\n'), 145 158 } 146 159 } ··· 281 294 if *in_preformatted { 282 295 // If we are in a preformatted line context, add the line to the 283 296 // preformatted blocks content and increment the line. 284 - preformatted.push_str(&format!("{line}\n")); 297 + let _ = writeln!(&mut preformatted, "{line}"); 285 298 286 299 if let Some(next_line) = lines.next() { 287 300 line = next_line;
+25 -17
src/convert/html.rs
··· 16 16 // Copyright (C) 2022-2022 Fuwn <contact@fuwn.me> 17 17 // SPDX-License-Identifier: GPL-3.0-only 18 18 19 - use crate::ast::Node; 19 + use {crate::ast::Node, std::fmt::Write}; 20 20 21 21 pub fn convert(source: &[Node]) -> String { 22 22 let mut html = String::new(); ··· 25 25 // this AST tree to an alternative markup format. 26 26 for node in source { 27 27 match node { 28 - Node::Text(text) => html.push_str(&format!("<p>{text}</p>")), 28 + Node::Text(text) => { 29 + let _ = write!(&mut html, "<p>{text}</p>"); 30 + } 29 31 Node::Link { to, text } => { 30 - html.push_str(&format!( 32 + let _ = write!( 33 + &mut html, 31 34 "<a href=\"{}\">{}</a><br>", 32 35 to, 33 36 text.clone().unwrap_or_else(|| to.clone()) 34 - )); 37 + ); 35 38 } 36 39 Node::Heading { level, text } => { 37 - html.push_str(&format!( 40 + let _ = write!( 41 + &mut html, 38 42 "<{}>{}</{0}>", 39 43 match level { 40 44 1 => "h1", ··· 43 47 _ => "p", 44 48 }, 45 49 text 46 - )); 50 + ); 47 51 } 48 - Node::List(items) => html.push_str(&format!( 49 - "<ul>{}</ul>", 50 - items 51 - .iter() 52 - .map(|i| format!("<li>{i}</li>")) 53 - .collect::<Vec<String>>() 54 - .join("\n") 55 - )), 56 - Node::Blockquote(text) => 57 - html.push_str(&format!("<blockquote>{text}</blockquote>")), 52 + Node::List(items) => { 53 + let _ = write!( 54 + &mut html, 55 + "<ul>{}</ul>", 56 + items 57 + .iter() 58 + .map(|i| format!("<li>{i}</li>")) 59 + .collect::<Vec<String>>() 60 + .join("\n") 61 + ); 62 + } 63 + Node::Blockquote(text) => { 64 + let _ = write!(&mut html, "<blockquote>{text}</blockquote>"); 65 + } 58 66 Node::PreformattedText { text, .. } => { 59 - html.push_str(&format!("<pre>{text}</pre>")); 67 + let _ = write!(&mut html, "<pre>{text}</pre>"); 60 68 } 61 69 Node::Whitespace => {} 62 70 }
+26 -17
src/convert/markdown.rs
··· 16 16 // Copyright (C) 2022-2022 Fuwn <contact@fuwn.me> 17 17 // SPDX-License-Identifier: GPL-3.0-only 18 18 19 - use crate::ast::Node; 19 + use {crate::ast::Node, std::fmt::Write}; 20 20 21 21 pub fn convert(source: &[Node]) -> String { 22 22 let mut markdown = String::new(); ··· 25 25 // this AST tree to an alternative markup format. 26 26 for node in source { 27 27 match node { 28 - Node::Text(text) => markdown.push_str(&format!("{text}\n")), 28 + Node::Text(text) => { 29 + let _ = writeln!(&mut markdown, "{text}"); 30 + } 29 31 Node::Link { to, text } => markdown.push_str(&text.clone().map_or_else( 30 32 || format!("<{to}>\n"), 31 33 |text| format!("[{text}]({to})\n"), 32 34 )), 33 35 Node::Heading { level, text } => { 34 - markdown.push_str(&format!( 35 - "{} {}\n", 36 + let _ = writeln!( 37 + &mut markdown, 38 + "{} {}", 36 39 match level { 37 40 1 => "#", 38 41 2 => "##", ··· 40 43 _ => "", 41 44 }, 42 45 text 43 - )); 46 + ); 47 + } 48 + Node::List(items) => { 49 + let _ = writeln!( 50 + &mut markdown, 51 + "{}", 52 + items 53 + .iter() 54 + .map(|i| format!("- {i}")) 55 + .collect::<Vec<String>>() 56 + .join("\n"), 57 + ); 44 58 } 45 - Node::List(items) => markdown.push_str(&format!( 46 - "{}\n", 47 - items 48 - .iter() 49 - .map(|i| format!("- {i}")) 50 - .collect::<Vec<String>>() 51 - .join("\n"), 52 - )), 53 - Node::Blockquote(text) => markdown.push_str(&format!("> {text}\n")), 59 + Node::Blockquote(text) => { 60 + let _ = writeln!(&mut markdown, "> {text}"); 61 + } 54 62 Node::PreformattedText { alt_text, text } => { 55 - markdown.push_str(&format!( 56 - "```{}\n{}```\n", 63 + let _ = writeln!( 64 + &mut markdown, 65 + "```{}\n{}```", 57 66 alt_text.clone().unwrap_or_default(), 58 67 text 59 - )); 68 + ); 60 69 } 61 70 Node::Whitespace => markdown.push('\n'), 62 71 }
+3 -2
src/meta.rs
··· 100 100 /// "text/gemini", 101 101 /// ); 102 102 /// ``` 103 + #[allow(clippy::missing_const_for_fn)] 103 104 #[must_use] 104 105 pub fn mime(&self) -> Cow<'_, str> { Cow::Borrowed(&self.mime) } 105 106 ··· 112 113 /// 113 114 /// *meta.mime_mut() = "text/gemini".to_string(); 114 115 /// ``` 115 - pub fn mime_mut(&mut self) -> &mut String { &mut self.mime } 116 + pub const fn mime_mut(&mut self) -> &mut String { &mut self.mime } 116 117 117 118 /// Obtain non-mutable access to the parameters of the `Meta` 118 119 /// ··· 144 145 /// 145 146 /// *meta.parameters_mut() = parameters; 146 147 /// ``` 147 - pub fn parameters_mut(&mut self) -> &mut HashMap<String, String> { 148 + pub const fn parameters_mut(&mut self) -> &mut HashMap<String, String> { 148 149 &mut self.parameters 149 150 } 150 151 }
+1
src/request/response.rs
··· 64 64 #[must_use] 65 65 pub const fn status(&self) -> &Status { &self.status } 66 66 67 + #[allow(clippy::missing_const_for_fn)] 67 68 #[must_use] 68 69 pub fn meta(&self) -> Cow<'_, str> { Cow::Borrowed(&self.meta) } 69 70
+4 -1
src/request/verifier.rs
··· 17 17 // SPDX-License-Identifier: GPL-3.0-only 18 18 19 19 use { 20 - rustls::{client, client::ServerCertVerified, Certificate}, 20 + rustls::{ 21 + Certificate, 22 + client::{self, ServerCertVerified}, 23 + }, 21 24 std::time::SystemTime, 22 25 }; 23 26
+2 -2
tests/ast.rs
··· 19 19 #[cfg(test)] 20 20 mod test { 21 21 use germ::{ 22 - ast::{Ast, Node}, 23 22 EXAMPLE_GEMTEXT, 23 + ast::{Ast, Node}, 24 24 }; 25 25 26 26 #[test] ··· 51 51 #[test] 52 52 fn build_single_element() { 53 53 assert_eq!( 54 - Ast::from_string("=> /test hi").inner().get(0).unwrap(), 54 + Ast::from_string("=> /test hi").inner().first().unwrap(), 55 55 &Node::Link { to: "/test".to_string(), text: Some("hi".to_string()) }, 56 56 ); 57 57 }
+1 -1
tests/convert.rs
··· 19 19 #[cfg(test)] 20 20 mod test { 21 21 use germ::{ 22 - convert::{from_string, Target}, 22 + convert::{Target, from_string}, 23 23 gemini_to_html, gemini_to_md, 24 24 }; 25 25