馃 The Definitive Gemini Protocol Toolkit
gemini
gemini-protocol
gemtext
parser
zero-dependency
toolkit
ast
converter
html
markdown
cli
networking
1//! This example demonstrates a chain of Germ's capabilities by fetching a
2//! Gemini capsule, parsing the response content into an abstract syntax tree,
3//! and converting the abstract syntax tree back to Gemtext, identical to the
4//! Gemini response content.
5
6use std::env;
7
8fn main() {
9 // Try to obtain a URL from the command line arguments or use the default one
10 let url_string =
11 env::args().nth(1).unwrap_or_else(|| "gemini://fuwn.me/".to_string());
12 // Form a valid URL to a Gemini capsule
13 let url = match url::Url::parse(&url_string) {
14 Ok(url) => url,
15 Err(error) => {
16 eprintln!("Error parsing URL '{}': {}", url_string, error);
17 std::process::exit(1);
18 }
19 };
20
21 // Perform a blocking request to the Gemini capsule
22 let request = germ::request::blocking::request(&url);
23
24 match request {
25 // If the request was successful:
26 Ok(response) => {
27 // Obtain the content of the Gemini response
28 let response_content =
29 &*response.content().clone().unwrap_or_else(|| "".to_string());
30 // Parse the Gemini response content into an abstract syntax tree
31 let ast = germ::ast::Ast::from_string(response_content);
32 // Convert the abstract syntax tree back to Gemtext, identical to the
33 // Gemini response content, constructed from the parsed abstract syntax
34 // tree
35 let gemtext = ast.to_gemtext();
36
37 // Print the Gemtext
38 println!("{}", gemtext);
39
40 // Check if the response content and reconstruction are identical
41 if response_content == gemtext {
42 println!(
43 "\nValidation: Response content and reconstruction are identical"
44 );
45 } else {
46 println!("\nValidation: Response content and reconstruction differ");
47 print_diff(response_content, &gemtext);
48 }
49 }
50 // If the request was unsuccessful, print an error message and exit
51 Err(error) => {
52 eprintln!("Error fetching '{}': {}", url_string, error);
53 std::process::exit(1);
54 }
55 }
56}
57
58fn print_diff(original: &str, reconstructed: &str) {
59 use std::io::{self, Write};
60
61 let mut stdout = io::stdout();
62 let original_lines = original.lines().collect::<Vec<&str>>();
63 let reconstructed_lines = reconstructed.lines().collect::<Vec<&str>>();
64 let max_lines = original_lines.len().max(reconstructed_lines.len());
65 let mut has_printed_diff = false;
66
67 for i in 0..max_lines {
68 let original_line = original_lines.get(i).unwrap_or(&"");
69 let reconstructed_line = reconstructed_lines.get(i).unwrap_or(&"");
70
71 if original_line != reconstructed_line {
72 if has_printed_diff {
73 let _ = writeln!(stdout);
74 }
75
76 let _ = writeln!(stdout, "Line {}:", i + 1);
77 let _ = writeln!(stdout, " Original: '{}'", original_line);
78 let _ = writeln!(stdout, " Reconstructed: '{}'", reconstructed_line);
79
80 has_printed_diff = true;
81 }
82 }
83
84 if original_lines.len() != reconstructed_lines.len() {
85 if has_printed_diff {
86 let _ = writeln!(stdout);
87 }
88
89 let _ = writeln!(stdout, "Length difference:");
90 let _ = writeln!(stdout, " Original: {} lines", original_lines.len());
91 let _ =
92 writeln!(stdout, " Reconstructed: {} lines", reconstructed_lines.len());
93 }
94}