Files
OllaHub/src-tauri/src/sources_config.rs
T
Evandro Fonseca Junior 4006a72eec feat(ui): enhance chat UI with new components and styling
refactor(chat): improve message display and actions
feat(chat): add high contrast mode toggle
style(chat): update spacing and sizing for better readability
feat(chat): add new Section and Stack layout components
docs: add UI style guide documentation
fix(chat): improve browser connection handling in scraper
feat(settings): expand default sources and improve UI
refactor: consolidate chat page layout and components
2025-11-20 15:13:54 -03:00

217 lines
8.4 KiB
Rust

use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
use tauri::{AppHandle, Manager};
use chrono::Utc;
/// Categoria de fonte de busca
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct SourceCategory {
pub id: String,
pub name: String,
pub base_sites: Vec<String>,
pub enabled: bool,
}
/// Configuração completa de fontes de busca
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct SourcesConfig {
#[serde(default = "default_version")]
pub version: u32,
pub categories: Vec<SourceCategory>,
#[serde(default = "default_last_updated")]
pub last_updated: String,
}
fn default_version() -> u32 {
1
}
fn default_last_updated() -> String {
Utc::now().to_rfc3339()
}
impl Default for SourcesConfig {
fn default() -> Self {
Self {
version: 1,
last_updated: Utc::now().to_rfc3339(),
categories: vec![
SourceCategory {
id: "academico".to_string(),
name: "Acadêmico".to_string(),
base_sites: vec![
"scholar.google.com".to_string(),
"arxiv.org".to_string(),
"pubmed.ncbi.nlm.nih.gov".to_string(),
"ieee.org".to_string(),
"acm.org".to_string(),
"sciencedirect.com".to_string(),
"springer.com".to_string(),
"jstor.org".to_string(),
"researchgate.net".to_string(),
"plos.org".to_string(),
"nature.com".to_string(),
"sciencemag.org".to_string(),
"mit.edu".to_string(),
"phys.org".to_string(),
"semanticscholar.org".to_string(),
"core.ac.uk".to_string(),
"paperswithcode.com".to_string(),
"base-search.net".to_string(),
"doaj.org".to_string(),
"worldscientific.com".to_string(),
],
enabled: true,
},
SourceCategory {
id: "tech".to_string(),
name: "Tech".to_string(),
base_sites: vec![
"stackoverflow.com".to_string(),
"github.com".to_string(),
"reddit.com/r/programming".to_string(),
"dev.to".to_string(),
"hackernoon.com".to_string(),
"tabnews.com.br".to_string(),
"lobste.rs".to_string(),
"news.ycombinator.com".to_string(),
"hashnode.com".to_string(),
"codesandbox.io".to_string(),
"developer.mozilla.org".to_string(),
"web.dev".to_string(),
"smashingmagazine.com".to_string(),
"martinfowler.com".to_string(),
"infoq.com".to_string(),
"dzone.com".to_string(),
"freecodecamp.org".to_string(),
"digitalocean.com".to_string(),
"realpython.com".to_string(),
"css-tricks.com".to_string(),
],
enabled: true,
},
SourceCategory {
id: "news".to_string(),
name: "News".to_string(),
base_sites: vec![
"theverge.com".to_string(),
"techcrunch.com".to_string(),
"arstechnica.com".to_string(),
"engadget.com".to_string(),
"wired.com".to_string(),
"venturebeat.com".to_string(),
"cnet.com".to_string(),
"mashable.com".to_string(),
"gizmodo.com".to_string(),
"zdnet.com".to_string(),
"tomshardware.com".to_string(),
"anandtech.com".to_string(),
"bleepingcomputer.com".to_string(),
"theregister.com".to_string(),
"9to5mac.com".to_string(),
"androidauthority.com".to_string(),
"techradar.com".to_string(),
"fastcompany.com".to_string(),
"g1.globo.com".to_string(),
"techtudo.com.br".to_string(),
],
enabled: true,
},
SourceCategory {
id: "financeiro".to_string(),
name: "Financeiro".to_string(),
base_sites: vec![
"bloomberg.com".to_string(),
"reuters.com".to_string(),
"wsj.com".to_string(),
"ft.com".to_string(),
"cnbc.com".to_string(),
"marketwatch.com".to_string(),
"economist.com".to_string(),
"forbes.com".to_string(),
"investopedia.com".to_string(),
"seekingalpha.com".to_string(),
"infomoney.com.br".to_string(),
"valor.globo.com".to_string(),
"exame.com".to_string(),
"braziljournal.com".to_string(),
"coindesk.com".to_string(),
"cointelegraph.com".to_string(),
"moneytimes.com.br".to_string(),
"suno.com.br".to_string(),
"b3.com.br".to_string(),
"imf.org".to_string(),
],
enabled: true,
},
],
}
}
}
/// Helper para obter o caminho do arquivo sources.json
pub fn get_sources_config_path(app_handle: &AppHandle) -> Result<PathBuf, String> {
let app_data_dir = app_handle
.path()
.app_data_dir()
.map_err(|e| format!("Failed to get app data dir: {}", e))?;
Ok(app_data_dir.join("sources.json"))
}
/// Carrega a configuração de fontes do arquivo
/// Se o arquivo não existir, retorna uma configuração padrão robusta
pub fn load_sources_config(app_handle: &AppHandle) -> Result<SourcesConfig, String> {
let config_path = get_sources_config_path(app_handle)?;
// Se o arquivo não existir, retornar Default
if !config_path.exists() {
log::info!("sources.json não encontrado, usando configuração padrão");
return Ok(SourcesConfig::default());
}
// Tentar ler e parsear o arquivo
let content = fs::read_to_string(&config_path)
.map_err(|e| format!("Failed to read sources.json: {}", e))?;
let config: SourcesConfig = serde_json::from_str(&content)
.map_err(|e| format!("Failed to parse sources.json: {}", e))?;
Ok(config)
}
/// Salva a configuração de fontes no arquivo
pub fn save_sources_config(app_handle: &AppHandle, config: SourcesConfig) -> Result<(), String> {
let config_path = get_sources_config_path(app_handle)?;
// Garantir que o diretório pai existe
if let Some(parent) = config_path.parent() {
if !parent.exists() {
fs::create_dir_all(parent)
.map_err(|e| format!("Failed to create config directory: {}", e))?;
}
}
// Atualizar timestamp
let mut config_to_save = config;
config_to_save.last_updated = Utc::now().to_rfc3339();
// Serializar e escrever
let json = serde_json::to_string_pretty(&config_to_save)
.map_err(|e| format!("Failed to serialize sources config: {}", e))?;
// Escrever em arquivo temporário primeiro, depois renomear (atomic write)
let temp_path = config_path.with_extension("json.tmp");
fs::write(&temp_path, json)
.map_err(|e| format!("Failed to write temp sources config file: {}", e))?;
// Renomear atomicamente
fs::rename(&temp_path, &config_path)
.map_err(|e| format!("Failed to rename temp file to sources.json: {}", e))?;
log::info!("Sources config salvo com sucesso em {:?}", config_path);
Ok(())
}