Gemini version

This commit is contained in:
deadvey 2025-04-03 21:42:37 +01:00
parent 845866ef9d
commit a7468d3f40

View File

@ -3,6 +3,7 @@ use std::io::{stdin,stdout,Write};
use colored::Colorize; use colored::Colorize;
use regex::Regex; use regex::Regex;
use url::{Url, ParseError}; use url::{Url, ParseError};
use std::fs;
fn clear_screen() { fn clear_screen() {
println!("clearing"); println!("clearing");
@ -11,63 +12,37 @@ fn clear_screen() {
.expect("Failed to clear screen"); .expect("Failed to clear screen");
} }
fn parse_markdown(page_content: String) -> (String, Vec<String>) { fn parse_gemtext(page_content: String) -> (String, Vec<String>) {
let mut parsed_page_content: String = "".to_string(); let mut parsed_page_content: String = "".to_string();
let mut hyperlink_number_counter: u64 = 0; let mut hyperlink_number_counter: u64 = 0;
let mut links: Vec<String> = Vec::new(); let mut links: Vec<String> = Vec::new();
let (screen_width, _screen_height) = termion::terminal_size().unwrap(); // So the horizontal line (<hr/>) spans the whole console let mut preformatted_code_toggle = false;
for line in page_content.lines() { for line in page_content.lines() {
let mut parsed_line: String = line.to_string(); let mut parsed_line: String = line.to_string();
// Bold let mut remove_line = false;
let bold_regex = Regex::new(r"((\*\*)|(__))(.*?)((\*\*)|(__))").unwrap();
parsed_line = bold_regex.replace_all(&parsed_line, |caps: &regex::Captures| {
caps[4].bold().to_string()
}).to_string();
// Strikethrough // preformatted text
let strikethrough_regex = Regex::new(r"~~(.*?)~~").unwrap(); let preformatted_text_regex = Regex::new(r"^```(.*)").unwrap();
parsed_line = strikethrough_regex.replace_all(&parsed_line, |caps: &regex::Captures| { parsed_line = preformatted_text_regex.replace_all(&parsed_line, |caps: &regex::Captures| {
caps[1].strikethrough().to_string() // Flip the toggle
}).to_string(); preformatted_code_toggle = ! preformatted_code_toggle;
// Horizontal lines if caps[1] == *""
let hr_regex = Regex::new(r"^(\*\*\*)|(---)|(___)$").unwrap(); {
parsed_line = hr_regex.replace_all(&parsed_line, |_caps: &regex::Captures| { remove_line = true;
let mut result: String = "\n".to_string();
for _x in 0..screen_width/2 {
result += "- ";
} }
result += "\n";
result // Remove the ```
}).to_string(); format!("{}", &caps[1].magenta())
// html br tag support
let br_regex = Regex::new(r"(.*?)<br/>(.*?)").unwrap();
parsed_line = br_regex.replace_all(&parsed_line, |caps: &regex::Captures| {
format!("{}{}{}", &caps[1], "\n", &caps[2])
}).to_string();
// Italics
let italic_regex = Regex::new(r"\*(.*?)\*").unwrap();
parsed_line = italic_regex.replace_all(&parsed_line, |caps: &regex::Captures| {
caps[1].italic().to_string()
}).to_string();
let italic_regex = Regex::new(r"_(.*?)_").unwrap();
parsed_line = italic_regex.replace_all(&parsed_line, |caps: &regex::Captures| {
caps[1].italic().to_string()
}).to_string(); }).to_string();
if preformatted_code_toggle == false
{
// Block quotes // Block quotes
let block_quotes_regex = Regex::new(r"^>(.*)").unwrap(); let block_quotes_regex = Regex::new(r"^>(.*)").unwrap();
parsed_line = block_quotes_regex.replace_all(&parsed_line, |caps: &regex::Captures| { parsed_line = block_quotes_regex.replace_all(&parsed_line, |caps: &regex::Captures| {
format!(" | {}", &caps[1]) format!(" | {}", &caps[1].red())
}).to_string();
// Ordered list
let ordered_list_regex = Regex::new(r"^([ \t]+|^)([0-9]+)\. (.*)").unwrap();
parsed_line = ordered_list_regex.replace_all(&parsed_line, |caps: &regex::Captures| {
format!("{} {}. {}", &caps[1], &caps[2], &caps[3])
}).to_string(); }).to_string();
// Unordered list ([ ]+|^)- (.*) // Unordered list ([ ]+|^)- (.*)
@ -76,113 +51,87 @@ fn parse_markdown(page_content: String) -> (String, Vec<String>) {
format!("{}{}", &caps[1], &caps[3]) format!("{}{}", &caps[1], &caps[3])
}).to_string(); }).to_string();
// Inline code
let inline_code_regex = Regex::new(r"`([^`]+?)`").unwrap();
parsed_line = inline_code_regex.replace_all(&parsed_line, |caps: &regex::Captures| {
format!("{}", &caps[1].magenta())
}).to_string();
// HyperLink // HyperLink
let hyperlink_regex = Regex::new(r"(.*?)\[(.*?)\]\((.*?)\)").unwrap(); let hyperlink_regex = Regex::new(r"=>\s(\S*)\s(.*)").unwrap();
parsed_line = hyperlink_regex.replace_all(&parsed_line, |caps: &regex::Captures| { parsed_line = hyperlink_regex.replace_all(&parsed_line, |caps: &regex::Captures| {
// Check if the character before the link is not '!' // Check if the character before the link is not '!'
if !caps[1].ends_with('!') { // caps[1] is everything before the link let result = format!("[{}] {}", hyperlink_number_counter, &caps[2].blue().underline());
let result = format!("{}{}[{}]", &caps[1], &caps[2].blue().underline(), hyperlink_number_counter); let url = caps[1].to_string();
let url = caps[3].to_string();
links.push(url); links.push(url);
hyperlink_number_counter += 1; hyperlink_number_counter += 1;
result result
} else {
// If it's an image (starts with !), return the link as is
let url = caps[3].to_string();
links.push(url);
hyperlink_number_counter += 1;
format!("({})[{}]", &caps[2].green(), hyperlink_number_counter)
}
}).to_string(); }).to_string();
let quick_hyperlink_regex = Regex::new(r"<(.*:\/\/.*)>").unwrap(); let quick_hyperlink_regex = Regex::new(r"=>\s(.*)").unwrap();
parsed_line = quick_hyperlink_regex.replace_all(&parsed_line, |caps: &regex::Captures| { parsed_line = quick_hyperlink_regex.replace_all(&parsed_line, |caps: &regex::Captures| {
hyperlink_number_counter += 1; hyperlink_number_counter += 1;
let url = caps[1].to_string(); let url = caps[1].to_string();
links.push(url); links.push(url);
format!("{}[{}]", &caps[1].blue().underline(), hyperlink_number_counter) format!("[{}] {}", hyperlink_number_counter, &caps[1].blue().underline())
}).to_string(); }).to_string();
}
else if preformatted_code_toggle == true
parsed_page_content+=&(parsed_line + "\n"); {
parsed_line = parsed_line.magenta().to_string();
} }
// multiline code if remove_line == false
let multiline_code_regex = Regex::new(r"(?ms)```((.*?\n)+?)```").unwrap(); {
parsed_page_content = multiline_code_regex.replace_all(&parsed_page_content, |caps: &regex::Captures| { parsed_page_content+=&(parsed_line + "\n");
// Capture the code inside the %% blocks }
let code_block = &caps[1]; }
// Add a tab to each line in the block
let indented_code = code_block
.lines()
.map(|line| format!("\t{}", line)) // Insert tab at the start of each line
.collect::<Vec<String>>()
.join("\n");
// Return the formatted block with magenta color
format!("{}", indented_code.magenta())
}).to_string();
return (parsed_page_content, links); return (parsed_page_content, links);
} }
fn fetch_page(url: &Url) -> String { fn fetch_page(url: &Url) {
let full_url_formatted = format!("{}", url); let full_url_formatted = format!("{}", url);
// Call curl using Com, mand let output = Command::new("gemget")
let output = Command::new("curl") .args([full_url_formatted, "-o".to_string(), "/tmp/page".to_string()])
.arg(full_url_formatted)
.output() .output()
.expect("Failed to execute curl command"); .expect("Failed to execute gemget command");
// Check if the command was successful // Check if the command was successful
if output.status.success() { if ! output.status.success() {
let page: String = String::from_utf8_lossy(&output.stdout).to_string(); println!("{}\n{:?}\n", "Failed to fetch page:".red(), output);
return page
} else {
eprintln!("Error:\n{}", String::from_utf8_lossy(&output.stderr));
let result: String = "error".to_string();
return result
} }
} }
fn render_page(url: Url, source: bool) -> Vec<String> { fn render_page(url: Url, source: bool) -> Vec<String> {
clear_screen(); clear_screen();
let mut content = fetch_page(&url); fetch_page(&url);
let mut links = Vec::new(); let mut links = Vec::new();
if let Ok(mut content) = fs::read_to_string::<String>("/tmp/page".to_string()) {
Command::new("rm")
.arg("/tmp/page")
.output()
.expect("Failed to delete tmp page");
let (screen_width, _screen_height) = termion::terminal_size().unwrap(); let (screen_width, _screen_height) = termion::terminal_size().unwrap();
if source == true { if source == true {
content += &format!("{}", &"Viewing source code".yellow()); content += &format!("{}", &"Viewing source code".yellow());
} }
else if &content[..13] == "<!DOCTYPE md>" {
(content, links) = parse_markdown((&content[13..]).to_string());
}
else { else {
content += &format!("{}", &"Warning: This page is invalid markdown, it should contain <!DOCTYPE md> at the very start of the file, showing raw text".yellow()); (content, links) = parse_gemtext(content);
} }
for _i in 0..screen_width { for _i in 0..screen_width {
print!(""); print!("-");
} }
print!("{}\n", url); print!("{}\n", url);
for _i in 0..screen_width { for _i in 0..screen_width {
print!(""); print!("-");
} }
println!("\n\n{}", content); println!("\n\n{}", content);
for _i in 0..screen_width { for _i in 0..screen_width {
print!(""); print!("-");
} }
println!(); println!();
// Return links (you can add link parsing logic) // Return links (you can add link parsing logic)
}
return links; return links;
} }
@ -207,17 +156,17 @@ fn parse_url(user_input: String, previous_url: &Url) -> Result<Url, ParseError>
user_input user_input
} }
else if user_input[..1] == *"/" { else if user_input[..1] == *"/" {
format!("http://{}/{}",Url::host_str(previous_url).expect("ivalid").to_string(), user_input) format!("gemini://{}/{}",Url::host_str(previous_url).expect("ivalid").to_string(), user_input)
} }
else { else {
println!("prepending scheme to user input"); println!("prepending scheme to user input");
format!("http://{}", user_input) // Prepend 'mttp://' if no scheme is found format!("gemini://{}", user_input) // Prepend 'mttp://' if no scheme is found
}; };
println!("Parsing: {}", to_parse); println!("Parsing: {}", to_parse);
if let Ok(mut url) = Url::parse(&to_parse) { if let Ok(mut url) = Url::parse(&to_parse) {
if url.port() == None { if url.port() == None {
let _ = url.set_port(Some(3477)); let _ = url.set_port(Some(1965));
} }
println!("{:?}",url); println!("{:?}",url);
println!("{}",url.as_str()); println!("{}",url.as_str());
@ -235,7 +184,7 @@ fn main() {
println!("Enter a url: "); println!("Enter a url: ");
let user_input = input(); let user_input = input();
if user_input == "q" { if user_input == "q" || user_input == "quit" || user_input == "exit" {
std::process::exit(0); std::process::exit(0);
} }
let mut load_page: bool = true; let mut load_page: bool = true;