I am struggling with the async code with Tauri. The code itself works just fine outside of the tauri application, however when I tried implementing the code I made inside of Tauri everything broke. I am pretty new to Tauri, so any help would be greatly appreciated!
Here’s the code:
#![cfg_attr(all(not(debug_assertions), target_os = "windows"),windows_subsystem = "windows")]
use tauri::Manager;
use tokio::sync::mpsc;
use tokio::sync::Mutex;
use tracing::info;
use tracing_subscriber;
use scraper::{Html, Selector};
use std::time::Instant;
use url::Url;
struct AsyncProcInputTx {
inner: Mutex<mpsc::Sender<String>>,
}
#[tokio::main]
async fn main() {
tracing_subscriber::fmt::init();
let (async_proc_input_tx, async_proc_input_rx) = mpsc::channel(1);
let (async_proc_output_tx, mut async_proc_output_rx) = mpsc::channel(1);
tauri::Builder::default()
.manage(AsyncProcInputTx {
inner: Mutex::new(async_proc_input_tx),
})
.invoke_handler(tauri::generate_handler![js2rs])
.setup(|app| {
tauri::async_runtime::spawn(async move {
async_process_model(
async_proc_input_rx,
async_proc_output_tx,
).await
});
let app_handle = app.handle();
tauri::async_runtime::spawn(async move {
loop {
if let Some(output) = async_proc_output_rx.recv().await {
rs2js(output, &app_handle);
}
}
});
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
fn rs2js<R: tauri::Runtime>(message: String, manager: &impl Manager<R>) {
info!(?message, "rs2js");
manager
.emit_all("rs2js", format!("rs: {}", message))
.unwrap();
}
#[tauri::command]
async fn js2rs(
state: tauri::State<'_, AsyncProcInputTx>,
) -> Result<(), String> {
let async_proc_input_tx = state.inner.lock().await;
let start = Instant::now();
// Using a more efficient client configuration with reqwest
let client = reqwest::Client::builder()
.tcp_nodelay(true)
.build()?;
let body = client
.get("https://finance.yahoo.com/topic/stock-market-news/")
.send()
.await?
.text()
.await?;
let document = Html::parse_document(&body);
let document = document.read().unwrap();
// Corrected selector for headlines on Yahoo Finance
let selector = Selector::parse(r#"h3[class="Mb(5px)"]"#)?;
let selector_link = Selector::parse("a")?;
let mut tasks = vec![];
let line = "-".repeat(80);
for element in document.select(&selector) {
let headline = element.text().collect::<Vec<_>>().join(""); // Join the text fragments into a single string
if let Some(link) = element.select(&selector_link).next() {
if let Some(href) = link.value().attr("href") {
let href = Url::parse("https://finance.yahoo.com")?.join(href)?.as_str().to_owned();
let client = client.clone();
let line_clone = line.clone(); // Clone the line variable for each task
let task = tokio::spawn(async move {
match get_article(&client, &href).await {
Ok(article_text) => {
info!("Headline: {}nURL: {}n, Text: {}nArticle: {}n", headline, href, line_clone, article_text);
info!("{}", line_clone);
}
Err(e) => info!("Failed to get article: {}", e),
}
});
tasks.push(task);
}
}
}
for task in tasks {
task.await?;
}
let end = Instant::now();
let duration = end.duration_since(start);
info!("Request took: {:?}", duration);
async_proc_input_tx
.send("Pass".to_string())
.await
.map_err(|e| e.to_string());
Ok(())
}
async fn get_article(client: &reqwest::Client, url: &str) -> Result<String, Box<dyn std::error::Error>> {
let body = client
.get(url)
.send()
.await?
.text()
.await?;
let document = Html::parse_document(&body);
let selector = Selector::parse(r#"div[class="caas-body"]"#)?;
let mut text = String::new();
for element in document.select(&selector) {
text.push_str(&element.text().collect::<Vec<_>>().join(""));
}
Ok(text)
}
async fn async_process_model(
mut input_rx: mpsc::Receiver<String>,
output_tx: mpsc::Sender<String>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
while let Some(input) = input_rx.recv().await {
let output = input;
output_tx.send(output).await?;
}
Ok(())
}
Here are the errors:
error[E0277]: `?` couldn't convert the error to `String`
--> src/main.rs:70:17
|
70 | .build()?;
| ^ the trait `From<reqwest::Error>` is not implemented for `String`, which is required by `Result<(), String>: FromResidual<Result<Infallible, reqwest::Error>>`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= help: the following other types implement trait `From<T>`:
<String as From<char>>
<String as From<tendril::tendril::Tendril<tendril::fmt::UTF8, A>>>
<String as From<Box<str>>>
<String as From<AssetKey>>
<String as From<Cow<'a, str>>>
<String as From<tauri::Url>>
<String as From<uuid::Uuid>>
<String as From<&'a tendril::tendril::Tendril<tendril::fmt::UTF8, A>>>
and 3 others
= note: required for `Result<(), String>` to implement `FromResidual<Result<Infallible, reqwest::Error>>`
error[E0277]: `?` couldn't convert the error to `String`
--> src/main.rs:75:15
|
75 | .await?
| ^ the trait `From<reqwest::Error>` is not implemented for `String`, which is required by `Result<(), String>: FromResidual<Result<Infallible, reqwest::Error>>`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= help: the following other types implement trait `From<T>`:
<String as From<char>>
<String as From<tendril::tendril::Tendril<tendril::fmt::UTF8, A>>>
<String as From<Box<str>>>
<String as From<AssetKey>>
<String as From<Cow<'a, str>>>
<String as From<tauri::Url>>
<String as From<uuid::Uuid>>
<String as From<&'a tendril::tendril::Tendril<tendril::fmt::UTF8, A>>>
and 3 others
= note: required for `Result<(), String>` to implement `FromResidual<Result<Infallible, reqwest::Error>>`
error[E0277]: `?` couldn't convert the error to `String`
--> src/main.rs:77:15
|
77 | .await?;
| ^ the trait `From<reqwest::Error>` is not implemented for `String`, which is required by `Result<(), String>: FromResidual<Result<Infallible, reqwest::Error>>`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= help: the following other types implement trait `From<T>`:
<String as From<char>>
<String as From<tendril::tendril::Tendril<tendril::fmt::UTF8, A>>>
<String as From<Box<str>>>
<String as From<AssetKey>>
<String as From<Cow<'a, str>>>
<String as From<tauri::Url>>
<String as From<uuid::Uuid>>
<String as From<&'a tendril::tendril::Tendril<tendril::fmt::UTF8, A>>>
and 3 others
= note: required for `Result<(), String>` to implement `FromResidual<Result<Infallible, reqwest::Error>>`
error[E0599]: no method named `read` found for struct `scraper::Html` in the current scope
--> src/main.rs:80:29
|
80 | let document = document.read().unwrap();
| ^^^^ method not found in `Html`
error[E0277]: `?` couldn't convert the error to `String`
--> src/main.rs:82:61
|
82 | let selector = Selector::parse(r#"h3[class="Mb(5px)"]"#)?;
| ^ the trait `From<SelectorErrorKind<'_>>` is not implemented for `String`, which is required by `Result<(), String>: FromResidual<Result<Infallible, SelectorErrorKind<'_>>>`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= help: the following other types implement trait `From<T>`:
<String as From<char>>
<String as From<tendril::tendril::Tendril<tendril::fmt::UTF8, A>>>
<String as From<Box<str>>>
<String as From<AssetKey>>
<String as From<Cow<'a, str>>>
<String as From<tauri::Url>>
<String as From<uuid::Uuid>>
<String as From<&'a tendril::tendril::Tendril<tendril::fmt::UTF8, A>>>
and 3 others
= note: required for `Result<(), String>` to implement `FromResidual<Result<Infallible, SelectorErrorKind<'_>>>`
error[E0277]: `?` couldn't convert the error to `String`
--> src/main.rs:83:45
|
83 | let selector_link = Selector::parse("a")?;
| ^ the trait `From<SelectorErrorKind<'_>>` is not implemented for `String`, which is required by `Result<(), String>: FromResidual<Result<Infallible, SelectorErrorKind<'_>>>`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= help: the following other types implement trait `From<T>`:
<String as From<char>>
<String as From<tendril::tendril::Tendril<tendril::fmt::UTF8, A>>>
<String as From<Box<str>>>
<String as From<AssetKey>>
<String as From<Cow<'a, str>>>
<String as From<tauri::Url>>
<String as From<uuid::Uuid>>
<String as From<&'a tendril::tendril::Tendril<tendril::fmt::UTF8, A>>>
and 3 others
= note: required for `Result<(), String>` to implement `FromResidual<Result<Infallible, SelectorErrorKind<'_>>>`
error[E0277]: `?` couldn't convert the error to `String`
--> src/main.rs:92:67
|
92 | let href = Url::parse("https://finance.yahoo.com")?.join(href)?.as_str().to_owned();
| ^ the trait `From<url::ParseError>` is not implemented for `String`, which is required by `Result<(), String>: FromResidual<Result<Infallible, url::ParseError>>`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= help: the following other types implement trait `From<T>`:
<String as From<char>>
<String as From<tendril::tendril::Tendril<tendril::fmt::UTF8, A>>>
<String as From<Box<str>>>
<String as From<AssetKey>>
<String as From<Cow<'a, str>>>
<String as From<tauri::Url>>
<String as From<uuid::Uuid>>
<String as From<&'a tendril::tendril::Tendril<tendril::fmt::UTF8, A>>>
and 3 others
= note: required for `Result<(), String>` to implement `FromResidual<Result<Infallible, url::ParseError>>`
error[E0277]: `?` couldn't convert the error to `String`
--> src/main.rs:92:79
|
92 | let href = Url::parse("https://finance.yahoo.com")?.join(href)?.as_str().to_owned();
| ^ the trait `From<url::ParseError>` is not implemented for `String`, which is required by `Result<(), String>: FromResidual<Result<Infallible, url::ParseError>>`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= help: the following other types implement trait `From<T>`:
<String as From<char>>
<String as From<tendril::tendril::Tendril<tendril::fmt::UTF8, A>>>
<String as From<Box<str>>>
<String as From<AssetKey>>
<String as From<Cow<'a, str>>>
<String as From<tauri::Url>>
<String as From<uuid::Uuid>>
<String as From<&'a tendril::tendril::Tendril<tendril::fmt::UTF8, A>>>
and 3 others
= note: required for `Result<(), String>` to implement `FromResidual<Result<Infallible, url::ParseError>>`
error[E0277]: `?` couldn't convert the error to `String`
--> src/main.rs:110:19
|
110 | task.await?;
| ^ the trait `From<tokio::task::JoinError>` is not implemented for `String`, which is required by `Result<(), String>: FromResidual<Result<Infallible, tokio::task::JoinError>>`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= help: the following other types implement trait `From<T>`:
<String as From<char>>
<String as From<tendril::tendril::Tendril<tendril::fmt::UTF8, A>>>
<String as From<Box<str>>>
<String as From<AssetKey>>
<String as From<Cow<'a, str>>>
<String as From<tauri::Url>>
<String as From<uuid::Uuid>>
<String as From<&'a tendril::tendril::Tendril<tendril::fmt::UTF8, A>>>
and 3 others
= note: required for `Result<(), String>` to implement `FromResidual<Result<Infallible, tokio::task::JoinError>>`
Some errors have detailed explanations: E0277, E0599.
For more information about an error, try `rustc --explain E0277`.
error: could not compile `news-screener` (bin "news-screener") due to 9 previous errors
I tried changing the code through various different ways but every time I switched to async Tauri refused to accept any of my solutions. What should’ve happened is when I invoked the js2rs function the code would fetch the latest articles of yahoo and log them.
Maxim Gannota is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
1