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!
The code itself is some code made to fetch the latest articles from yahoo finance’s website and then log them.
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.
3