feat: gemini support

This commit is contained in:
Artemy Egorov 2024-08-04 16:55:03 +03:00
parent 15c43e3482
commit d96b863ea8
26 changed files with 448 additions and 93 deletions

34
src-tauri/Cargo.lock generated
View file

@ -258,9 +258,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
version = "1.6.1"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
dependencies = [
"serde",
]
@ -603,9 +603,9 @@ dependencies = [
[[package]]
name = "dalet"
version = "1.0.0-pre6"
version = "1.0.0-pre9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bc4c347533f8341633bd820799dea680f600e50891310b74bc914740681e8c2"
checksum = "2095f83b5256dc9a981639c3250aba53c02736a3601c1e6b2c54c27ec786274a"
dependencies = [
"clap",
"enum-procs",
@ -3093,6 +3093,15 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
dependencies = [
"libc",
]
[[package]]
name = "simd-adler32"
version = "0.3.7"
@ -3685,8 +3694,11 @@ dependencies = [
"bytes",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.52.0",
]
@ -3704,6 +3716,17 @@ dependencies = [
"webpki-roots",
]
[[package]]
name = "tokio-macros"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
]
[[package]]
name = "tokio-native-tls"
version = "0.3.1"
@ -4006,6 +4029,7 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
name = "vigi"
version = "0.0.0"
dependencies = [
"bytes",
"dalet",
"mime",
"reqwest 0.12.5",
@ -4013,7 +4037,9 @@ dependencies = [
"serde_json",
"tauri",
"tauri-build",
"tokio",
"tokio-gemini",
"tokio-rustls",
"url",
]

View file

@ -26,7 +26,14 @@ tauri = { version = "1", features = [
] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
dalet = "1.0.0-pre6"
dalet = "1.0.0-pre9"
tokio = { version = "1.39.2", features = ["full"] }
tokio-rustls = { version = "0.26.0", default-features = false, features = [
"ring",
] }
bytes = "1.7.1"
reqwest = "0.12.5"
tokio-gemini = "0.1.0"

View file

@ -7,7 +7,7 @@ mod process_input;
mod types;
mod utils;
use tauri::async_runtime::Mutex;
use tokio::sync::Mutex;
use types::{VigiError, VigiJsState, VigiState};
use utils::{read_or_create_jsonl, read_or_create_number};

View file

@ -0,0 +1,56 @@
use tokio_rustls::rustls::{
client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
ClientConfig, SignatureScheme,
};
/// TODO: update to secure version when supported
pub fn insecure_gemini_client() -> tokio_gemini::Client {
tokio_gemini::Client::from(
ClientConfig::builder()
.dangerous()
.with_custom_certificate_verifier(std::sync::Arc::new(NoCertVerification {}))
.with_no_client_auth(),
)
}
#[derive(Debug)]
struct NoCertVerification;
impl ServerCertVerifier for NoCertVerification {
fn verify_server_cert(
&self,
_end_entity: &tokio_rustls::rustls::pki_types::CertificateDer<'_>,
_intermediates: &[tokio_rustls::rustls::pki_types::CertificateDer<'_>],
_server_name: &tokio_rustls::rustls::pki_types::ServerName<'_>,
_ocsp_response: &[u8],
_now: tokio_rustls::rustls::pki_types::UnixTime,
) -> Result<ServerCertVerified, tokio_rustls::rustls::Error> {
Ok(ServerCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
_message: &[u8],
_cert: &tokio_rustls::rustls::pki_types::CertificateDer<'_>,
_dss: &tokio_rustls::rustls::DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, tokio_rustls::rustls::Error> {
Ok(HandshakeSignatureValid::assertion())
}
fn verify_tls13_signature(
&self,
_message: &[u8],
_cert: &tokio_rustls::rustls::pki_types::CertificateDer<'_>,
_dss: &tokio_rustls::rustls::DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, tokio_rustls::rustls::Error> {
Ok(HandshakeSignatureValid::assertion())
}
fn supported_verify_schemes(&self) -> Vec<tokio_rustls::rustls::SignatureScheme> {
vec![
SignatureScheme::ECDSA_NISTP256_SHA256,
SignatureScheme::ECDSA_NISTP384_SHA384,
SignatureScheme::ECDSA_NISTP521_SHA512,
]
}
}

View file

@ -1,15 +1,16 @@
use crate::types::{VigiError, VigiOutput};
use bytes::Bytes;
use mime::Mime;
use url::Url;
mod insecure_gemini_client;
mod process_data;
mod process_url;
use process_data::process_data;
use process_url::process_url;
type Data = Vec<u8>;
type ReqResult = (Mime, Data);
type ReqResult = (Mime, Bytes);
pub async fn process_input(input: &String) -> Result<VigiOutput, VigiError> {
let parsed = Url::parse(input);

View file

@ -1,25 +1,41 @@
use dalet::{daletl::ToDaletlPage, typed::Tag::*};
use bytes::Bytes;
use dalet::{daletl::ToDaletlPage, parsers::gemtext::parse_gemtext, typed::Tag::*};
use mime::Mime;
use std::str;
use crate::types::{VigiError, VigiOutput};
pub async fn process_data(mime: Mime, data: Vec<u8>) -> Result<VigiOutput, VigiError> {
pub async fn process_data(mime: Mime, data: Bytes) -> Result<VigiOutput, VigiError> {
let result = match (mime.type_().as_str(), mime.subtype().as_str()) {
("text", "plain") => {
process_text(String::from_utf8(data).map_err(|_| VigiError::TextIsNotUtf8)?).await
process_text(str::from_utf8(&data).map_err(|_| VigiError::InvalidCharset)?).await
}
("text", "gemini") => {
process_gemini(str::from_utf8(&data).map_err(|_| VigiError::InvalidCharset)?).await?
}
// ("text", "gemini") => {
// process_text(String::from_utf8(data).map_err(|_| VigiError::TextIsNotUtf8)?).await
// }
_ => Err(VigiError::UnsupportedMimeType)?,
};
Ok(result)
}
async fn process_text(data: String) -> VigiOutput {
let mut truncated = data.clone();
async fn process_text(data: &str) -> VigiOutput {
let mut truncated = data.to_owned();
truncated.truncate(50);
VigiOutput::new(truncated, vec![El(data.into())].to_dl_page())
}
async fn process_gemini(data: &str) -> Result<VigiOutput, VigiError> {
let mut truncated = data.to_owned();
truncated.truncate(50);
let res = VigiOutput::new(
truncated,
parse_gemtext(data)
.map_err(|_| VigiError::Parse)?
.to_dl_page(),
);
Ok(res)
}

View file

@ -1,15 +1,17 @@
use bytes::Bytes;
use mime::Mime;
use reqwest::header::CONTENT_TYPE;
use tokio::io::AsyncReadExt;
use url::Url;
use crate::types::VigiError;
use super::ReqResult;
use super::{insecure_gemini_client, ReqResult};
pub async fn process_url(url: Url) -> Result<ReqResult, VigiError> {
let result = match url.scheme() {
"http" | "https" => process_http(url.to_string()).await?,
// "gemini" => process_gemini(url.to_string()).await?,
"gemini" => process_gemini(url.to_string()).await?,
_ => Err(VigiError::UnsupportedProtocol)?,
};
@ -37,16 +39,21 @@ async fn process_http(url: String) -> Result<ReqResult, VigiError> {
))
}
// async fn process_gemini(url: String) -> Result<ReqResult, VigiError> {
// let res = tokio_gemini::Client::default()
// .request(&url)
// .await
// .map_err(|e| {
// println!("{:#?}", e);
// VigiError::Network
// })?;
async fn process_gemini(url: String) -> Result<ReqResult, VigiError> {
let mut res = insecure_gemini_client::insecure_gemini_client()
.request(&url)
.await
.map_err(|_| VigiError::Network)?;
// let mime_type = res.mime().map_err(|_| VigiError::InvalidMimeType)?;
let mime_type = res.mime().map_err(|_| VigiError::InvalidMimeType)?;
// Ok((mime_type, res.message().as_bytes().into()))
// }
let mut buffer = Vec::new();
let tls_stream = res.body();
tls_stream
.read_to_end(&mut buffer)
.await
.map_err(|_| VigiError::Network)?;
Ok((mime_type, Bytes::from(buffer).into()))
}

View file

@ -21,7 +21,7 @@ pub enum VigiError {
UnsupportedMimeType,
InvalidMimeType,
TextIsNotUtf8,
InvalidCharset,
}
#[derive(Debug, Clone)]