fix: setup & build error (#147)

This commit is contained in:
BiggerRain
2025-02-18 11:51:50 +08:00
committed by GitHub
parent a61e973b16
commit 0844970a1f
13 changed files with 187 additions and 141 deletions

View File

@@ -1,8 +1,8 @@
use std::collections::HashMap;
// use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::common::health::Status;
// use crate::common::health::Status;
#[derive(Debug,Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RequestAccessTokenResponse {
pub access_token: String,
pub expire_at: u32,

View File

@@ -2,22 +2,18 @@ use crate::common::search::{QueryResponse, QuerySource};
use thiserror::Error;
use async_trait::async_trait;
use std::{future::Future, pin::Pin};
use serde::Serialize;
// use std::{future::Future, pin::Pin};
use crate::common::search::SearchQuery;
use serde::Serialize;
#[async_trait]
pub trait SearchSource: Send + Sync {
fn get_type (&self) -> QuerySource;
fn get_type(&self) -> QuerySource;
async fn search(
&self,
query: SearchQuery,
) -> Result<QueryResponse, SearchError>;
async fn search(&self, query: SearchQuery) -> Result<QueryResponse, SearchError>;
}
#[derive(Debug, Error,Serialize)]
#[derive(Debug, Error, Serialize)]
pub enum SearchError {
#[error("HTTP request failed: {0}")]
HttpError(String),
@@ -45,4 +41,4 @@ impl From<reqwest::Error> for SearchError {
SearchError::HttpError(err.to_string())
}
}
}
}

View File

@@ -9,7 +9,7 @@ mod util;
mod setup;
use crate::common::register::SearchSourceRegistry;
use crate::common::traits::SearchSource;
// use crate::common::traits::SearchSource;
use crate::common::{MAIN_WINDOW_LABEL, SETTINGS_WINDOW_LABEL};
use crate::server::search::CocoSearchSource;
use crate::server::servers::{load_or_insert_default_server, load_servers_token};
@@ -18,7 +18,10 @@ use reqwest::Client;
use std::path::PathBuf;
#[cfg(target_os = "macos")]
use tauri::ActivationPolicy;
use tauri::{AppHandle, Emitter, Listener, Manager, PhysicalPosition, Runtime, State, WebviewWindow, Window, WindowEvent};
use tauri::{
AppHandle, Emitter, Listener, Manager, PhysicalPosition, Runtime, State, WebviewWindow, Window,
WindowEvent,
};
use tauri_plugin_autostart::MacosLauncher;
use tauri_plugin_deep_link::DeepLinkExt;
use tokio::runtime::Runtime as RT;
@@ -64,7 +67,7 @@ pub fn run() {
app.emit("single-instance", Payload { args: argv, cwd })
.unwrap();
}))
.plugin(tauri_plugin_store::Builder::default().build())
.plugin(tauri_plugin_store::Builder::default().build());
// Conditional compilation for macOS
#[cfg(target_os = "macos")]
@@ -187,7 +190,7 @@ pub async fn init<R: Runtime>(app_handle: &AppHandle<R>) {
let coco_servers = server::servers::get_all_servers();
// Get the registry from Tauri's state
let registry:State<SearchSourceRegistry> = app_handle.state::<SearchSourceRegistry>();
let registry: State<SearchSourceRegistry> = app_handle.state::<SearchSourceRegistry>();
for server in coco_servers {
let source = CocoSearchSource::new(server.clone(), Client::new());

View File

@@ -31,15 +31,22 @@ fn extract_icon_from_app_bundle(app_dir: &Path, app_data_folder: &Path) -> Optio
if icns_path.exists() {
// If the icon exists, convert it to PNG
if let Some(output_path) = convert_icns_to_png(&app_dir, &icns_path, app_data_folder) {
if let Some(output_path) =
convert_icns_to_png(&app_dir, &icns_path, app_data_folder)
{
return Some(output_path);
}
} else {
// If the icon name doesn't end with .icns, try appending it
if !icon_name.ends_with(".icns") {
let icns_path_with_extension = app_dir.join(format!("Contents/Resources/{}.icns", icon_name));
let icns_path_with_extension =
app_dir.join(format!("Contents/Resources/{}.icns", icon_name));
if icns_path_with_extension.exists() {
if let Some(output_path) = convert_icns_to_png(&app_dir, &icns_path_with_extension, app_data_folder) {
if let Some(output_path) = convert_icns_to_png(
&app_dir,
&icns_path_with_extension,
app_data_folder,
) {
return Some(output_path);
}
}
@@ -64,7 +71,8 @@ fn extract_icon_from_app_bundle(app_dir: &Path, app_data_folder: &Path) -> Optio
// Fallback: If no icon found, return a default system icon
if let Some(system_icon_path) = get_system_icon(app_dir) {
if let Some(output_path) = convert_icns_to_png(&app_dir, &system_icon_path, app_data_folder) {
if let Some(output_path) = convert_icns_to_png(&app_dir, &system_icon_path, app_data_folder)
{
return Some(output_path);
}
}
@@ -135,7 +143,11 @@ fn get_png_from_resources(app_dir: &Path) -> Option<PathBuf> {
}
/// Converts an ICNS file to PNG using macOS's `sips` command.
fn convert_icns_to_png(app_dir: &Path, icns_path: &Path, app_data_folder: &Path) -> Option<PathBuf> {
fn convert_icns_to_png(
app_dir: &Path,
icns_path: &Path,
app_data_folder: &Path,
) -> Option<PathBuf> {
if let Some(app_name) = app_dir.file_name().and_then(|name| name.to_str()) {
let icon_storage_dir = app_data_folder.join("coco-appIcons");
fs::create_dir_all(&icon_storage_dir).ok();
@@ -156,8 +168,8 @@ fn convert_icns_to_png(app_dir: &Path, icns_path: &Path, app_data_folder: &Path)
.arg(icns_path)
.arg("--out")
.arg(&output_png_path)
.stdout(Stdio::null()) // Redirect stdout to null
.stderr(Stdio::null()) // Redirect stderr to null
.stdout(Stdio::null()) // Redirect stdout to null
.stderr(Stdio::null()) // Redirect stderr to null
.status();
if let Ok(status) = status {
@@ -173,14 +185,18 @@ fn convert_icns_to_png(app_dir: &Path, icns_path: &Path, app_data_folder: &Path)
/// Converts a PNG file to PNG (essentially just copying it to a new location).
fn convert_png_to_png(png_path: &Path, app_data_folder: &Path) -> Option<PathBuf> {
if let Some(app_name) = png_path.parent().and_then(|p| p.file_name()).and_then(|name| name.to_str()) {
if let Some(app_name) = png_path
.parent()
.and_then(|p| p.file_name())
.and_then(|name| name.to_str())
{
let icon_storage_dir = app_data_folder.join("coco-appIcons");
fs::create_dir_all(&icon_storage_dir).ok();
let output_png_path = icon_storage_dir.join(format!("{}.png", app_name));
// Copy the PNG file to the output directory
if let Err(e) = fs::copy(png_path, &output_png_path) {
if let Err(_e) = fs::copy(png_path, &output_png_path) {
return None;
}
@@ -190,7 +206,7 @@ fn convert_png_to_png(png_path: &Path, app_data_folder: &Path) -> Option<PathBuf
}
/// Fallback function to fetch a system icon if the app doesn't have its own.
fn get_system_icon(app_dir: &Path) -> Option<PathBuf> {
fn get_system_icon(_app_dir: &Path) -> Option<PathBuf> {
// Just a placeholder for getting a default icon if no app-specific icon is found
let default_icon_path = Path::new("/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/GenericApplicationIcon.icns");
@@ -201,13 +217,12 @@ fn get_system_icon(app_dir: &Path) -> Option<PathBuf> {
}
}
impl ApplicationSearchSource {
pub fn new(base_score: f64, app_dirs: Vec<PathBuf>) -> Self {
let mut icons = HashMap::new();
// Collect search locations as strings
let mut applications = Trie::new();
let applications = Trie::new();
// Iterate over the directories to find .app files and extract icons
for app_dir in &app_dirs {
@@ -217,15 +232,22 @@ impl ApplicationSearchSource {
if file_path.is_dir() && file_path.extension() == Some("app".as_ref()) {
if let Some(app_data_folder) = data_dir() {
let file_path_str = file_path.to_string_lossy().to_string(); // Convert to owned String if needed
if file_path.parent().unwrap().to_str().unwrap().contains(".app/Contents/") {
if file_path
.parent()
.unwrap()
.to_str()
.unwrap()
.contains(".app/Contents/")
{
continue;
}
let search_word = file_path.file_name()
.unwrap() // unwrap() might panic if there's no file name
let search_word = file_path
.file_name()
.unwrap() // unwrap() might panic if there's no file name
.to_str()
.unwrap() // unwrap() might panic if it's not valid UTF-8
.unwrap() // unwrap() might panic if it's not valid UTF-8
.trim_end_matches(".app")
.to_lowercase(); // to_lowercase returns a String, which is owned
.to_lowercase(); // to_lowercase returns a String, which is owned
//TODO, replace this hard-coded name to actual local app name in case it may change
if search_word.is_empty() || search_word.eq("coco ai") {
@@ -234,7 +256,9 @@ impl ApplicationSearchSource {
let search_word_ref = search_word.as_str(); // Get a reference to the string slice
applications.insert(search_word_ref, file_path_str.clone());
if let Some(icon_path) = extract_icon_from_app_bundle(&file_path, &app_data_folder) {
if let Some(icon_path) =
extract_icon_from_app_bundle(&file_path, &app_data_folder)
{
icons.insert(file_path_str, icon_path);
} else {
dbg!("No icon found for:", &file_path);
@@ -253,7 +277,6 @@ impl ApplicationSearchSource {
}
}
/// Extracts the clean app name by removing `.app`
fn clean_app_name(path: &Path) -> Option<String> {
path.file_name()?
@@ -266,7 +289,10 @@ impl SearchSource for ApplicationSearchSource {
fn get_type(&self) -> QuerySource {
QuerySource {
r#type: LOCAL_QUERY_SOURCE_TYPE.into(),
name: hostname::get().unwrap_or("My Computer".into()).to_string_lossy().into(),
name: hostname::get()
.unwrap_or("My Computer".into())
.to_string_lossy()
.into(),
id: "local_applications".into(),
}
}
@@ -289,7 +315,9 @@ impl SearchSource for ApplicationSearchSource {
let mut total_hits = 0;
let mut hits = Vec::new();
let mut results = self.application_paths.search_within_distance_scored(&query_string, 3);
let mut results = self
.application_paths
.search_within_distance_scored(&query_string, 3);
// Check for NaN or extreme score values and handle them properly
results.sort_by(|a, b| {
@@ -298,7 +326,9 @@ impl SearchSource for ApplicationSearchSource {
std::cmp::Ordering::Equal
} else {
// Otherwise, compare the scores as usual
b.score.partial_cmp(&a.score).unwrap_or(std::cmp::Ordering::Equal)
b.score
.partial_cmp(&a.score)
.unwrap_or(std::cmp::Ordering::Equal)
}
});

View File

@@ -1,6 +1,8 @@
use crate::common::register::SearchSourceRegistry;
use crate::common::search::{FailedRequest, MultiSourceQueryResponse, QueryHits, QuerySource, SearchQuery};
use crate::common::traits::{SearchError, SearchSource};
use crate::common::search::{
FailedRequest, MultiSourceQueryResponse, QueryHits, QuerySource, SearchQuery,
};
use crate::common::traits::SearchError;
use futures::stream::FuturesUnordered;
use futures::StreamExt;
use std::collections::HashMap;
@@ -52,7 +54,8 @@ pub async fn query_coco_fusion<R: Runtime>(
all_hits.push((source_id.clone(), query_hit.clone(), score));
hits_per_source.entry(source_id.clone())
hits_per_source
.entry(source_id.clone())
.or_insert_with(Vec::new)
.push((query_hit, score));
}
@@ -90,7 +93,11 @@ pub async fn query_coco_fusion<R: Runtime>(
}
let total_sources = hits_per_source.len();
let max_hits_per_source = if total_sources > 0 { size as usize / total_sources } else { size as usize };
let max_hits_per_source = if total_sources > 0 {
size as usize / total_sources
} else {
size as usize
};
let mut final_hits = Vec::new();
let mut seen_docs = std::collections::HashSet::new(); // To track documents we've already added
@@ -113,7 +120,8 @@ pub async fn query_coco_fusion<R: Runtime>(
// Sort all hits by score descending, removing duplicates by document ID
all_hits.sort_by(|a, b| b.2.partial_cmp(&a.2).unwrap_or(std::cmp::Ordering::Equal));
let extra_hits = all_hits.into_iter()
let extra_hits = all_hits
.into_iter()
.filter(|(source_id, _, _)| hits_per_source.contains_key(source_id)) // Only take from known sources
.filter_map(|(_, doc, _)| {
if !seen_docs.contains(&doc.document.id) {
@@ -130,11 +138,15 @@ pub async fn query_coco_fusion<R: Runtime>(
}
// **Sort final hits by score descending**
final_hits.sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap_or(std::cmp::Ordering::Equal));
final_hits.sort_by(|a, b| {
b.score
.partial_cmp(&a.score)
.unwrap_or(std::cmp::Ordering::Equal)
});
Ok(MultiSourceQueryResponse {
failed: failed_requests,
hits: final_hits,
total_hits,
})
}
}

View File

@@ -8,7 +8,8 @@ use std::sync::{Arc, RwLock};
use tauri::{AppHandle, Runtime};
lazy_static! {
static ref CONNECTOR_CACHE: Arc<RwLock<HashMap<String, HashMap<String, Connector>>>> = Arc::new(RwLock::new(HashMap::new()));
static ref CONNECTOR_CACHE: Arc<RwLock<HashMap<String, HashMap<String, Connector>>>> =
Arc::new(RwLock::new(HashMap::new()));
}
pub fn save_connectors_to_cache(server_id: &str, connectors: Vec<Connector>) {
@@ -21,15 +22,13 @@ pub fn save_connectors_to_cache(server_id: &str, connectors: Vec<Connector>) {
}
pub fn get_connector_by_id(server_id: &str, connector_id: &str) -> Option<Connector> {
let cache = CONNECTOR_CACHE.read().unwrap(); // Async read lock
let cache = CONNECTOR_CACHE.read().unwrap(); // Async read lock
let server_cache = cache.get(server_id)?;
let connector = server_cache.get(connector_id)?;
Some(connector.clone())
}
pub async fn refresh_all_connectors<R: Runtime>(
app_handle: &AppHandle<R>,
) -> Result<(), String> {
pub async fn refresh_all_connectors<R: Runtime>(app_handle: &AppHandle<R>) -> Result<(), String> {
// dbg!("Attempting to refresh all connectors");
let servers = get_all_servers();
@@ -38,7 +37,8 @@ pub async fn refresh_all_connectors<R: Runtime>(
let mut server_map = HashMap::new();
for server in servers {
// dbg!("start fetch connectors for server: {}", &server.id);
let connectors = match get_connectors_by_server(app_handle.clone(), server.id.clone()).await {
let connectors = match get_connectors_by_server(app_handle.clone(), server.id.clone()).await
{
Ok(connectors) => {
let connectors_map: HashMap<String, Connector> = connectors
.into_iter()
@@ -46,7 +46,7 @@ pub async fn refresh_all_connectors<R: Runtime>(
.collect();
connectors_map
}
Err(e) => {
Err(_e) => {
// dbg!("Failed to get connectors for server {}: {}", &server.id, e);
HashMap::new() // Return empty map on failure
}
@@ -57,7 +57,7 @@ pub async fn refresh_all_connectors<R: Runtime>(
}
// After all tasks have finished, perform a read operation on the cache
let cache_size = {
let _cache_size = {
// Insert connectors into the cache (async write lock)
let mut cache = CONNECTOR_CACHE.write().unwrap(); // Async write lock
cache.clear();
@@ -71,7 +71,9 @@ pub async fn refresh_all_connectors<R: Runtime>(
Ok(())
}
pub async fn get_connectors_from_cache_or_remote(server_id: &str) -> Result<Vec<Connector>, String> {
pub async fn get_connectors_from_cache_or_remote(
server_id: &str,
) -> Result<Vec<Connector>, String> {
// Acquire the read lock and check cache for connectors
let cache = CONNECTOR_CACHE.read().unwrap(); // Acquire read lock
if let Some(connectors) = cache.get(server_id).cloned() {
@@ -85,9 +87,10 @@ pub async fn get_connectors_from_cache_or_remote(server_id: &str) -> Result<Vec<
let connectors = fetch_connectors_by_server(server_id).await?;
// Convert the Vec<Connector> into HashMap<String, Connector>
let connectors_map: HashMap<String, Connector> = connectors.clone()
let connectors_map: HashMap<String, Connector> = connectors
.clone()
.into_iter()
.map(|connector| (connector.id.clone(), connector)) // Assuming Connector has an `id` field
.map(|connector| (connector.id.clone(), connector)) // Assuming Connector has an `id` field
.collect();
// Optionally, update the cache after fetching data from remote
@@ -116,7 +119,10 @@ pub async fn fetch_connectors_by_server(id: &str) -> Result<Vec<Connector>, Stri
if resp.status().is_success() {
dbg!("Received successful response for id: {}", &id);
} else {
dbg!("Failed to fetch connectors. Response status: {}", resp.status());
dbg!(
"Failed to fetch connectors. Response status: {}",
resp.status()
);
}
// Parse the search results directly from the response body
@@ -137,7 +143,7 @@ pub async fn fetch_connectors_by_server(id: &str) -> Result<Vec<Connector>, Stri
#[tauri::command]
pub async fn get_connectors_by_server<R: Runtime>(
app_handle: AppHandle<R>,
_app_handle: AppHandle<R>,
id: String,
) -> Result<Vec<Connector>, String> {
//fetch_connectors_by_server

View File

@@ -9,30 +9,27 @@ use std::sync::{Arc, RwLock};
use tauri::{AppHandle, Runtime};
lazy_static! {
static ref DATASOURCE_CACHE: Arc<RwLock<HashMap<String,HashMap<String,DataSource>>>> = Arc::new(RwLock::new(HashMap::new()));
static ref DATASOURCE_CACHE: Arc<RwLock<HashMap<String, HashMap<String, DataSource>>>> =
Arc::new(RwLock::new(HashMap::new()));
}
pub fn save_datasource_to_cache(server_id: &str, datasources: Vec<DataSource>) {
let mut cache = DATASOURCE_CACHE.write().unwrap(); // Acquire write lock
let datasources_map: HashMap<String, DataSource> = datasources
.into_iter()
.map(|datasource| {
(datasource.id.clone(), datasource)
})
.map(|datasource| (datasource.id.clone(), datasource))
.collect();
cache.insert(server_id.to_string(), datasources_map);
}
pub fn get_datasources_from_cache(server_id: &str) -> Option<HashMap<String, DataSource>> {
let cache = DATASOURCE_CACHE.read().unwrap(); // Acquire read lock
// dbg!("cache: {:?}", &cache);
// dbg!("cache: {:?}", &cache);
let server_cache = cache.get(server_id)?; // Get the server's cache
Some(server_cache.clone())
}
pub async fn refresh_all_datasources<R: Runtime>(
app_handle: &AppHandle<R>,
) -> Result<(), String> {
pub async fn refresh_all_datasources<R: Runtime>(app_handle: &AppHandle<R>) -> Result<(), String> {
// dbg!("Attempting to refresh all datasources");
let servers = get_all_servers();
@@ -43,26 +40,25 @@ pub async fn refresh_all_datasources<R: Runtime>(
// dbg!("fetch datasources for server: {}", &server.id);
// Attempt to get datasources by server, and continue even if it fails
let mut connectors = match get_datasources_by_server(app_handle.clone(), server.id.clone()).await {
Ok(connectors) => {
// Process connectors only after fetching them
let connectors_map: HashMap<String, DataSource> = connectors
.into_iter()
.map(|mut connector| {
(connector.id.clone(), connector)
})
.collect();
// dbg!("connectors_map: {:?}", &connectors_map);
connectors_map
}
Err(e) => {
// dbg!("Failed to get dataSources for server {}: {}", &server.id, e);
HashMap::new()
}
};
let connectors =
match get_datasources_by_server(app_handle.clone(), server.id.clone()).await {
Ok(connectors) => {
// Process connectors only after fetching them
let connectors_map: HashMap<String, DataSource> = connectors
.into_iter()
.map(|connector| (connector.id.clone(), connector))
.collect();
// dbg!("connectors_map: {:?}", &connectors_map);
connectors_map
}
Err(_e) => {
// dbg!("Failed to get dataSources for server {}: {}", &server.id, e);
HashMap::new()
}
};
let mut new_map = HashMap::new();
for (id, mut datasource) in connectors.iter() {
for (id, datasource) in connectors.iter() {
// dbg!("connector: {:?}", &datasource);
if let Some(existing_connector) = get_connector_by_id(&server.id, &datasource.id) {
// If found in cache, update the connector's info
@@ -77,7 +73,7 @@ pub async fn refresh_all_datasources<R: Runtime>(
}
// Perform a read operation after all writes are done
let cache_size = {
let _cache_size = {
let mut cache = DATASOURCE_CACHE.write().unwrap();
cache.clear();
cache.extend(server_map);
@@ -90,7 +86,7 @@ pub async fn refresh_all_datasources<R: Runtime>(
#[tauri::command]
pub async fn get_datasources_by_server<R: Runtime>(
app_handle: AppHandle<R>,
_app_handle: AppHandle<R>,
id: String,
) -> Result<Vec<DataSource>, String> {
// dbg!("get_datasources_by_server: id = {}", &id);
@@ -104,7 +100,7 @@ pub async fn get_datasources_by_server<R: Runtime>(
})?;
// Parse the search results from the response
let mut datasources: Vec<DataSource> = parse_search_results(resp).await.map_err(|e| {
let datasources: Vec<DataSource> = parse_search_results(resp).await.map_err(|e| {
// dbg!("Error parsing search results: {}", &e);
e.to_string()
})?;

View File

@@ -1,18 +1,18 @@
use std::future::Future;
// use std::future::Future;
use std::time::Duration;
use lazy_static::lazy_static;
use tauri::AppHandle;
// use lazy_static::lazy_static;
// use tauri::AppHandle;
use crate::server::servers::{get_server_by_id, get_server_token};
use once_cell::sync::Lazy;
use reqwest::{Client, Method, RequestBuilder};
use tokio::sync::Mutex;
use reqwest::{Client, Method, RequestBuilder, Response, StatusCode};
pub static HTTP_CLIENT: Lazy<Mutex<Client>> = Lazy::new(|| {
let client = Client::builder()
.read_timeout(Duration::from_secs(3)) // Set a timeout of 3 second
.connect_timeout(Duration::from_secs(3)) // Set a timeout of 3 second
.timeout(Duration::from_secs(10)) // Set a timeout of 10 seconds
.timeout(Duration::from_secs(10)) // Set a timeout of 10 seconds
.danger_accept_invalid_certs(true) // example for self-signed certificates
.build()
.expect("Failed to build client");
@@ -35,7 +35,6 @@ impl HttpClient {
headers: Option<reqwest::header::HeaderMap>,
body: Option<reqwest::Body>,
) -> Result<reqwest::Response, String> {
let request_builder = Self::get_request_builder(method, url, headers, body).await;
// Send the request
@@ -91,28 +90,25 @@ impl HttpClient {
// Retrieve the token for the server (token is optional)
let token = get_server_token(server_id).map(|t| t.access_token.clone());
// Create headers map (optional "X-API-TOKEN" header)
let mut headers = reqwest::header::HeaderMap::new();
if let Some(t) = token {
headers.insert("X-API-TOKEN", reqwest::header::HeaderValue::from_str(&t).unwrap());
headers.insert(
"X-API-TOKEN",
reqwest::header::HeaderValue::from_str(&t).unwrap(),
);
}
// dbg!(&headers);
Self::send_raw_request(method, &url, Some(headers), body).await
} else {
Err("Server not found".to_string())
}
}
// Convenience method for GET requests (as it's the most common)
pub async fn get(
server_id: &str,
path: &str,
) -> Result<reqwest::Response, String> {
pub async fn get(server_id: &str, path: &str) -> Result<reqwest::Response, String> {
HttpClient::send_request(server_id, Method::GET, path, None).await
}
@@ -122,7 +118,7 @@ impl HttpClient {
path: &str,
body: reqwest::Body,
) -> Result<reqwest::Response, String> {
HttpClient::send_request( server_id, Method::POST, path, Some(body)).await
HttpClient::send_request(server_id, Method::POST, path, Some(body)).await
}
// Convenience method for PUT requests
@@ -131,14 +127,11 @@ impl HttpClient {
path: &str,
body: reqwest::Body,
) -> Result<reqwest::Response, String> {
HttpClient::send_request( server_id, Method::PUT, path, Some(body)).await
HttpClient::send_request(server_id, Method::PUT, path, Some(body)).await
}
// Convenience method for DELETE requests
pub async fn delete(
server_id: &str,
path: &str,
) -> Result<reqwest::Response, String> {
pub async fn delete(server_id: &str, path: &str) -> Result<reqwest::Response, String> {
HttpClient::send_request(server_id, Method::DELETE, path, None).await
}
}
}

View File

@@ -1,20 +1,19 @@
//! This file contains Rust APIs related to Coco Server management.
use futures::FutureExt;
use futures::StreamExt;
use futures::TryFutureExt;
use reqwest::Client;
use serde::Serialize;
use std::sync::LazyLock;
use tauri::Runtime;
use tauri_plugin_store::StoreExt;
// use futures::FutureExt;
// use futures::StreamExt;
// use futures::TryFutureExt;
// use reqwest::Client;
// use serde::Serialize;
// use std::sync::LazyLock;
// use tauri::Runtime;
// use tauri_plugin_store::StoreExt;
pub mod servers;
pub mod auth;
pub mod servers;
// pub mod health;
pub mod datasource;
pub mod connector;
pub mod search;
pub mod profile;
pub mod datasource;
pub mod http_client;
pub mod profile;
pub mod search;

View File

@@ -4,11 +4,11 @@ use tauri::{AppHandle, Runtime};
#[tauri::command]
pub async fn get_user_profiles<R: Runtime>(
app_handle: AppHandle<R>,
_app_handle: AppHandle<R>,
server_id: String,
) -> Result<UserProfile, String> {
// Use the generic GET method from HttpClient
let response = HttpClient::get( &server_id, "/account/profile")
let response = HttpClient::get(&server_id, "/account/profile")
.await
.map_err(|e| format!("Error fetching profile: {}", e))?;

View File

@@ -1,15 +1,17 @@
use crate::common::document::Document;
use crate::common::search::{parse_search_response, QueryHits, QueryResponse, QuerySource, SearchQuery};
use crate::common::search::{
parse_search_response, QueryHits, QueryResponse, QuerySource, SearchQuery,
};
use crate::common::server::Server;
use crate::common::traits::{SearchError, SearchSource};
use crate::server::http_client::HttpClient;
use crate::server::servers::get_server_token;
use async_trait::async_trait;
use futures::stream::StreamExt;
// use futures::stream::StreamExt;
use ordered_float::OrderedFloat;
use reqwest::{Client, Method, RequestBuilder};
use std::collections::HashMap;
use std::hash::Hash;
// use std::hash::Hash;
pub(crate) struct DocumentsSizedCollector {
size: u64,
/// Documents and scores
@@ -41,7 +43,7 @@ impl DocumentsSizedCollector {
}
}
fn documents(self) -> impl ExactSizeIterator<Item=Document> {
fn documents(self) -> impl ExactSizeIterator<Item = Document> {
self.docs.into_iter().map(|(_, doc, _)| doc)
}
@@ -81,7 +83,12 @@ impl CocoSearchSource {
self.build_request(query.from, query.size, &query.query_strings)
}
fn build_request(&self, from: u64, size: u64, query_strings: &HashMap<String, String>) -> RequestBuilder {
fn build_request(
&self,
from: u64,
size: u64,
query_strings: &HashMap<String, String>,
) -> RequestBuilder {
let url = HttpClient::join_url(&self.server.endpoint, "/query/_search");
let mut request_builder = self.client.request(Method::GET, url);
@@ -91,7 +98,8 @@ impl CocoSearchSource {
}
}
request_builder.query(&[("from", &from.to_string()), ("size", &size.to_string())])
request_builder
.query(&[("from", &from.to_string()), ("size", &size.to_string())])
.query(query_strings)
}
}
@@ -107,12 +115,9 @@ impl SearchSource for CocoSearchSource {
}
// Directly return Result<QueryResponse, SearchError> instead of Future
async fn search(
&self,
query: SearchQuery,
) -> Result<QueryResponse, SearchError> {
let server_id = self.server.id.clone();
let server_name = self.server.name.clone();
async fn search(&self, query: SearchQuery) -> Result<QueryResponse, SearchError> {
let _server_id = self.server.id.clone();
let _server_name = self.server.name.clone();
let request_builder = self.build_request_from_query(&query);
// Send the HTTP request asynchronously
@@ -127,10 +132,13 @@ impl SearchSource for CocoSearchSource {
match parse_search_response(response).await {
Ok(response) => {
let total_hits = response.hits.total.value as usize;
let hits: Vec<(Document, f64)> = response.hits.hits.into_iter()
let hits: Vec<(Document, f64)> = response
.hits
.hits
.into_iter()
.map(|hit| {
// Handling Option<f64> in hit._score by defaulting to 0.0 if None
(hit._source, hit._score.unwrap_or(0.0)) // Use 0.0 if _score is None
(hit._source, hit._score.unwrap_or(0.0)) // Use 0.0 if _score is None
})
.collect();
@@ -160,4 +168,4 @@ impl SearchSource for CocoSearchSource {
}
}
}
}
}

View File

@@ -255,7 +255,7 @@ pub async fn load_or_insert_default_server<R: Runtime>(
#[tauri::command]
pub async fn list_coco_servers<R: Runtime>(
app_handle: AppHandle<R>,
_app_handle: AppHandle<R>,
) -> Result<Vec<Server>, String> {
let servers: Vec<Server> = get_all_servers();
Ok(servers)
@@ -443,7 +443,7 @@ pub async fn logout_coco_server<R: Runtime>(
dbg!("Attempting to log out server by id:", &id);
// Check if server token exists
if let Some(token) = get_server_token(id.as_str()) {
if let Some(_token) = get_server_token(id.as_str()) {
dbg!("Found server token for id:", &id);
// Remove the server token from cache