🦠 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.

fix(ast): parse preformatted gemtext as preformatted content

Fuwn 6d92ea62 ab17fe5d

+78 -182
+2 -1
Cargo.toml
··· 2 2 3 3 [package] 4 4 name = "germ" 5 - version = "0.4.1" 5 + version = "0.4.2" 6 6 authors = ["Fuwn <contact@fuwn.me>"] 7 7 edition = "2021" 8 8 description = "The Ultimate Gemini Toolkit." ··· 23 23 meta = [] 24 24 request = ["rustls", "url", "anyhow", "tokio", "tokio-rustls"] 25 25 quick = [] 26 + example-gemtext = [] 26 27 27 28 [dependencies] 28 29 anyhow = { version = "1.0.70", optional = true } # `Result`
+1 -30
examples/ast.rs
··· 19 19 //! This example demonstrates Germ's capabilities for parsing Gemtext into an 20 20 //! abstract syntax tree. 21 21 22 - const EXAMPLE_GEMTEXT: &str = r#"```This is alt-text 23 - Here goes the pre-formatted text. 24 - 25 - This continues the pre-formatted text on a new line after a blank line. 26 - ``` 27 - 28 - # This is a heading 29 - 30 - This is some text. 31 - 32 - This is more text after a blank line. 33 - 34 - * This is a single list item. 35 - * This is the next list item. 36 - 37 - * This is a new list. 38 - * This is the next item on the new list. 39 - 40 - ## This is a sub-heading 41 - 42 - > This is a blockquote. 43 - 44 - ### This is a sub-sub-heading. 45 - 46 - => gemini://gem.rest/ This is a link to GemRest 47 - => /somewhere 48 - 49 - That was a link without text."#; 50 - 51 22 fn main() { 52 23 // Parse `EXAMPLE_GEMTEXT` into an abstract syntax tree 53 - let ast = germ::ast::Ast::from_string(EXAMPLE_GEMTEXT); 24 + let ast = germ::ast::Ast::from_string(germ::EXAMPLE_GEMTEXT); 54 25 // Get the nodes of the abstract syntax tree 55 26 let ast_nodes = ast.inner(); 56 27
+1 -30
examples/ast_to_gemtext.rs
··· 19 19 //! This example converts Gemtext into an abstract syntax tree and then back 20 20 //! into Gemtext, demonstrating both Germ's parsing and generation capabilities. 21 21 22 - const EXAMPLE_GEMTEXT: &str = r#"```This is alt-text 23 - Here goes the pre-formatted text. 24 - 25 - This continues the pre-formatted text on a new line after a blank line. 26 - ``` 27 - 28 - # This is a heading 29 - 30 - This is some text. 31 - 32 - This is more text after a blank line. 33 - 34 - * This is a single list item. 35 - * This is the next list item. 36 - 37 - * This is a new list. 38 - * This is the next item on the new list. 39 - 40 - ## This is a sub-heading 41 - 42 - > This is a blockquote. 43 - 44 - ### This is a sub-sub-heading. 45 - 46 - => gemini://gem.rest/ This is a link to GemRest 47 - => /somewhere 48 - 49 - That was a link without text."#; 50 - 51 22 fn main() { 52 23 // Parse `EXAMPLE_GEMTEXT` into an abstract syntax tree 53 - let ast = germ::ast::Ast::from_string(EXAMPLE_GEMTEXT); 24 + let ast = germ::ast::Ast::from_string(germ::EXAMPLE_GEMTEXT); 54 25 // Convert the abstract syntax tree back to Gemtext 55 26 let gemtext = ast.to_gemtext(); 56 27
+4 -31
examples/html.rs
··· 19 19 //! This example demonstrates Germ's capabilities for converting Gemtext to 20 20 //! HTML. 21 21 22 - const EXAMPLE_GEMTEXT: &str = r#"```This is alt-text 23 - Here goes the pre-formatted text. 24 - 25 - This continues the pre-formatted text on a new line after a blank line. 26 - ``` 27 - 28 - # This is a heading 29 - 30 - This is some text. 31 - 32 - This is more text after a blank line. 33 - 34 - * This is a single list item. 35 - * This is the next list item. 36 - 37 - * This is a new list. 38 - * This is the next item on the new list. 39 - 40 - ## This is a sub-heading 41 - 42 - > This is a blockquote. 43 - 44 - ### This is a sub-sub-heading. 45 - 46 - => gemini://gem.rest/ This is a link to GemRest 47 - => /somewhere 48 - 49 - That was a link without text."#; 50 - 51 22 fn main() { 52 23 // Convert the Gemtext to HTML 53 - let html = 54 - germ::convert::from_string(EXAMPLE_GEMTEXT, &germ::convert::Target::HTML); 24 + let html = germ::convert::from_string( 25 + germ::EXAMPLE_GEMTEXT, 26 + &germ::convert::Target::HTML, 27 + ); 55 28 56 29 // Write the HTML to a file 57 30 std::fs::write("examples/convert.html", html)
+1 -30
examples/markdown.rs
··· 19 19 //! This example demonstrates Germ's capabilities for converting Gemtext to 20 20 //! Markdown. 21 21 22 - const EXAMPLE_GEMTEXT: &str = r#"```This is alt-text 23 - Here goes the pre-formatted text. 24 - 25 - This continues the pre-formatted text on a new line after a blank line. 26 - ``` 27 - 28 - # This is a heading 29 - 30 - This is some text. 31 - 32 - This is more text after a blank line. 33 - 34 - * This is a single list item. 35 - * This is the next list item. 36 - 37 - * This is a new list. 38 - * This is the next item on the new list. 39 - 40 - ## This is a sub-heading 41 - 42 - > This is a blockquote. 43 - 44 - ### This is a sub-sub-heading. 45 - 46 - => gemini://gem.rest/ This is a link to GemRest 47 - => /somewhere 48 - 49 - That was a link without text."#; 50 - 51 22 fn main() { 52 23 // Convert the Gemtext to Markdown 53 24 let html = germ::convert::from_string( 54 - EXAMPLE_GEMTEXT, 25 + germ::EXAMPLE_GEMTEXT, 55 26 &germ::convert::Target::Markdown, 56 27 ); 57 28
+6 -6
src/ast/container.rs
··· 190 190 // Match the first character of the Gemtext line to understand the line 191 191 // type 192 192 match line.get(0..1).unwrap_or("") { 193 - "=" => { 193 + "=" if !*in_preformatted => { 194 194 // If the Gemtext line starts with an "=" ("=>"), it is a link line, 195 195 // so splitting it up should be easy enough. 196 196 let line = line.get(2..).unwrap(); ··· 211 211 212 212 break; 213 213 } 214 - "#" => { 214 + "#" if !*in_preformatted => { 215 215 // If the Gemtext line starts with an "#", it is a heading, so let's 216 216 // find out how deep it goes. 217 217 let level = ··· 234 234 235 235 break; 236 236 } 237 - "*" => { 237 + "*" if !*in_preformatted => { 238 238 // If the Gemtext line starts with an asterisk, it is a list item, so 239 239 // let's enter a list context. 240 240 if !*in_list { ··· 249 249 break; 250 250 } 251 251 } 252 - ">" => { 252 + ">" if !*in_preformatted => { 253 253 // If the Gemtext line starts with an ">", it is a blockquote, so 254 254 // let's just clip off the line identifier. 255 255 nodes.push(Node::Blockquote( ··· 259 259 break; 260 260 } 261 261 "`" => { 262 - // If the Gemtext line starts with a backtick, it is a list item, so 263 - // let's enter a preformatted text context. 262 + // If the Gemtext line starts with a backtick, it's a preformatted 263 + // toggle, so let's enter a preformatted text context. 264 264 *in_preformatted = !*in_preformatted; 265 265 266 266 if *in_preformatted {
+44
src/lib.rs
··· 38 38 #[cfg(feature = "meta")] pub mod meta; 39 39 40 40 #[cfg(feature = "quick")] pub mod quick; 41 + 42 + #[cfg(feature = "example-gemtext")] 43 + pub const EXAMPLE_GEMTEXT: &str = r"```This is alt-text 44 + Here goes the pre-formatted text. 45 + 46 + This continues the pre-formatted text on a new line after a blank line. 47 + ``` 48 + 49 + # This is a heading 50 + 51 + This is some text. 52 + 53 + This is more text after a blank line. 54 + 55 + * This is a single list item. 56 + * This is the next list item. 57 + 58 + * This is a new list. 59 + * This is the next item on the new list. 60 + 61 + ## This is a sub-heading 62 + 63 + > This is a blockquote. 64 + 65 + ### This is a sub-sub-heading. 66 + 67 + => gemini://gem.rest/ This is a link to GemRest 68 + => /somewhere 69 + 70 + ```This is a preformatted block containing inner Gemtext. 71 + => gemini://fuwn.me/ This is a link. 72 + 73 + * This is a list item. 74 + 75 + > This is a blockquote. 76 + 77 + # This is a heading. 78 + 79 + ## This is a sub-heading. 80 + 81 + ### This is a sub-sub-heading. 82 + ``` 83 + 84 + That was a link without text.";
+17 -52
tests/ast.rs
··· 18 18 19 19 #[cfg(test)] 20 20 mod test { 21 - use germ::ast::{Ast, Node}; 22 - 23 - const EXAMPLE_GEMTEXT: &str = r#"```This is alt-text 24 - Here goes the pre-formatted text. 25 - 26 - This continues the pre-formatted text on a new line after a blank line. 27 - ``` 28 - 29 - # This is a heading 30 - 31 - This is some text. 32 - 33 - This is more text after a blank line. 34 - 35 - * This is a single list item. 36 - * This is the next list item. 37 - 38 - * This is a new list. 39 - * This is the next item on the new list. 40 - 41 - ## This is a sub-heading 42 - 43 - > This is a blockquote. 44 - 45 - ### This is a sub-sub-heading. 46 - 47 - => gemini://gem.rest/ This is a link to GemRest 48 - => /somewhere 49 - 50 - That was a link without text."#; 21 + use germ::{ 22 + ast::{Ast, Node}, 23 + EXAMPLE_GEMTEXT, 24 + }; 51 25 52 26 #[test] 53 27 fn build_multi_line_list_with_text() { 54 - assert_eq!( 55 - *Ast::from_string("* item1\n* 2\nhi text").inner(), 56 - vec![ 57 - Node::List(vec!["item1".to_string(), "2".to_string()]), 58 - Node::Text("hi text".to_string()), 59 - ], 60 - ); 28 + assert_eq!(*Ast::from_string("* item1\n* 2\nhi text").inner(), vec![ 29 + Node::List(vec!["item1".to_string(), "2".to_string()]), 30 + Node::Text("hi text".to_string()), 31 + ],); 61 32 } 62 33 63 34 #[test] 64 35 fn build_multi_line_vec() { 65 - assert_eq!( 66 - *Ast::from_string("=> /test hi\nhi there\n> hi").inner(), 67 - vec![ 68 - Node::Link { to: "/test".to_string(), text: Some("hi".to_string()) }, 69 - Node::Text("hi there".to_string()), 70 - Node::Blockquote("hi".to_string()), 71 - ], 72 - ); 36 + assert_eq!(*Ast::from_string("=> /test hi\nhi there\n> hi").inner(), vec![ 37 + Node::Link { to: "/test".to_string(), text: Some("hi".to_string()) }, 38 + Node::Text("hi there".to_string()), 39 + Node::Blockquote("hi".to_string()), 40 + ],); 73 41 } 74 42 75 43 #[test] 76 44 fn build_single_0th_from_vec() { 77 - assert_eq!( 78 - Ast::from_string("=> /test hi").inner(), 79 - &vec![Node::Link { 80 - to: "/test".to_string(), 81 - text: Some("hi".to_string()), 82 - }], 83 - ); 45 + assert_eq!(Ast::from_string("=> /test hi").inner(), &vec![Node::Link { 46 + to: "/test".to_string(), 47 + text: Some("hi".to_string()), 48 + }],); 84 49 } 85 50 86 51 #[test]
+2 -2
tests/node.rs
··· 4 4 fn node_to_gemtext() { 5 5 assert_eq!( 6 6 germ::ast::Node::Link { 7 - to: "/faq".to_string(), 8 - text: Some("FAQ".to_string()) 7 + to: "/faq".to_string(), 8 + text: Some("FAQ".to_string()), 9 9 } 10 10 .to_gemtext(), 11 11 "=> /faq FAQ",