feat: add emoji-regex and remove-accents dependencies; enhance chat and setup pages
- Added `emoji-regex` and `remove-accents` packages to improve text processing capabilities. - Implemented automatic Ollama startup in the chat page for better user experience. - Enhanced setup page to include GPU selection based on hardware specifications. - Updated chat input to support real-time validation and improved user feedback. - Refactored message handling in chat to incorporate new processing features.
This commit is contained in:
1 parent
4275295e4f
commit
84ea2dec19
51 files changed
+7857
-573
No files matched your search
+491
-27
@@ -1,6 +1,15 @@
|
||||
use serde::Serialize;
|
||||
use sysinfo::System;
|
||||
|
||||
/// Informações sobre uma GPU
|
||||
#[derive(Serialize, Clone, Debug)]
|
||||
pub struct GpuInfo {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub vendor: Option<String>,
|
||||
pub memory_mb: Option<u64>,
|
||||
}
|
||||
|
||||
/// Estatísticas do sistema em tempo real
|
||||
#[derive(Serialize, Clone, Debug)]
|
||||
pub struct SystemStats {
|
||||
@@ -17,6 +26,7 @@ pub struct SystemStats {
|
||||
/// Estado persistente do sistema para cálculo de CPU
|
||||
pub struct SystemMonitorState {
|
||||
system: System,
|
||||
#[allow(dead_code)]
|
||||
last_cpu_check: std::time::Instant,
|
||||
}
|
||||
|
||||
@@ -89,65 +99,519 @@ impl SystemMonitorState {
|
||||
}
|
||||
|
||||
/// Tenta detectar o nome da GPU (implementação básica)
|
||||
/// Mantido para compatibilidade com SystemStats
|
||||
fn detect_gpu_name() -> Option<String> {
|
||||
let gpus = detect_all_gpus();
|
||||
gpus.first().map(|gpu| gpu.name.clone())
|
||||
}
|
||||
|
||||
/// Detecta todas as GPUs disponíveis no sistema
|
||||
pub fn detect_all_gpus() -> Vec<GpuInfo> {
|
||||
log::info!("Iniciando detecção de GPUs...");
|
||||
let mut gpus = Vec::new();
|
||||
|
||||
// Primeiro, tentar obter informações do Ollama se estiver rodando
|
||||
if let Ok(ollama_gpus) = detect_gpus_ollama_api() {
|
||||
log::info!("GPUs detectadas via API do Ollama: {}", ollama_gpus.len());
|
||||
gpus.extend(ollama_gpus);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
use std::process::Command;
|
||||
// Tentar múltiplas fontes de detecção
|
||||
let windows_gpus = detect_gpus_windows();
|
||||
|
||||
// Tenta usar wmic para detectar GPU no Windows
|
||||
if let Ok(output) = Command::new("wmic")
|
||||
.args(&["path", "win32_VideoController", "get", "name"])
|
||||
.output()
|
||||
{
|
||||
if let Ok(stdout) = String::from_utf8(output.stdout) {
|
||||
for line in stdout.lines() {
|
||||
let line = line.trim();
|
||||
if !line.is_empty() && line != "Name" {
|
||||
return Some(line.to_string());
|
||||
// Mesclar com GPUs do Ollama, evitando duplicatas
|
||||
for windows_gpu in windows_gpus {
|
||||
if !gpus.iter().any(|g| g.name == windows_gpu.name) {
|
||||
log::info!("GPU detectada via wmic: {}", windows_gpu.name);
|
||||
gpus.push(windows_gpu);
|
||||
}
|
||||
}
|
||||
|
||||
// Se não encontrou GPUs NVIDIA, tentar nvidia-smi
|
||||
let has_nvidia = gpus.iter().any(|g| g.vendor.as_ref().map(|v| v == "NVIDIA").unwrap_or(false));
|
||||
if !has_nvidia {
|
||||
log::info!("Tentando nvidia-smi como fallback...");
|
||||
if let Ok(nvidia_gpus) = detect_gpus_nvidia_smi() {
|
||||
// Mesclar resultados, evitando duplicatas
|
||||
for nvidia_gpu in nvidia_gpus {
|
||||
if !gpus.iter().any(|g| g.name == nvidia_gpu.name) {
|
||||
log::info!("GPU detectada via nvidia-smi: {}", nvidia_gpu.name);
|
||||
gpus.push(nvidia_gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log::info!("Total de GPUs detectadas no Windows: {}", gpus.len());
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
use std::process::Command;
|
||||
log::info!("Tentando detectar GPUs no Linux...");
|
||||
gpus = detect_gpus_linux();
|
||||
log::info!("Total de GPUs detectadas no Linux: {}", gpus.len());
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
log::info!("Tentando detectar GPUs no macOS...");
|
||||
gpus = detect_gpus_macos();
|
||||
log::info!("Total de GPUs detectadas no macOS: {}", gpus.len());
|
||||
}
|
||||
|
||||
// Se não encontrou nenhuma GPU, retornar GPU genérica
|
||||
if gpus.is_empty() {
|
||||
log::warn!("Nenhuma GPU detectada, usando fallback genérico");
|
||||
gpus.push(GpuInfo {
|
||||
id: "gpu_default".to_string(),
|
||||
name: "GPU não detectada".to_string(),
|
||||
vendor: None,
|
||||
memory_mb: None,
|
||||
});
|
||||
}
|
||||
|
||||
log::info!("Detecção de GPUs concluída: {} GPU(s) encontrada(s)", gpus.len());
|
||||
gpus
|
||||
}
|
||||
|
||||
/// Detecta GPUs no Windows usando wmic (formato CSV melhorado)
|
||||
#[cfg(target_os = "windows")]
|
||||
fn detect_gpus_windows() -> Vec<GpuInfo> {
|
||||
use std::process::Command;
|
||||
let mut gpus = Vec::new();
|
||||
|
||||
log::info!("Tentando detectar GPUs via wmic...");
|
||||
|
||||
// Tentar formato CSV primeiro (mais confiável)
|
||||
if let Ok(output) = Command::new("wmic")
|
||||
.args(&["path", "win32_VideoController", "get", "name,AdapterRAM,PNPDeviceID", "/format:csv"])
|
||||
.output()
|
||||
{
|
||||
// wmic pode retornar UTF-16LE no Windows, precisamos converter
|
||||
let stdout = if let Ok(utf8) = String::from_utf8(output.stdout.clone()) {
|
||||
utf8
|
||||
} else {
|
||||
// Tentar UTF-16LE (little-endian)
|
||||
let bytes = output.stdout;
|
||||
let mut utf16_chars = Vec::new();
|
||||
let mut i = 0;
|
||||
while i + 1 < bytes.len() {
|
||||
let low = bytes[i] as u16;
|
||||
let high = bytes[i + 1] as u16;
|
||||
utf16_chars.push(low | (high << 8));
|
||||
i += 2;
|
||||
}
|
||||
String::from_utf16_lossy(&utf16_chars)
|
||||
};
|
||||
|
||||
// Tenta usar lspci no Linux
|
||||
if let Ok(output) = Command::new("lspci")
|
||||
.args(&["-v", "-s", "$(lspci | grep VGA | cut -d' ' -f1)"])
|
||||
log::debug!("wmic output (primeiros 500 chars): {}", stdout.chars().take(500).collect::<String>());
|
||||
|
||||
// Parse CSV: Node,Name,AdapterRAM,PNPDeviceID
|
||||
let lines: Vec<&str> = stdout.lines().collect();
|
||||
for (idx, line) in lines.iter().enumerate() {
|
||||
if idx == 0 {
|
||||
continue; // Skip header
|
||||
}
|
||||
|
||||
let parts: Vec<&str> = line.split(',').map(|s| s.trim()).collect();
|
||||
if parts.len() >= 4 {
|
||||
let name = parts[1].trim_matches('"').to_string();
|
||||
if !name.is_empty() && name != "Name" {
|
||||
let adapter_ram_str = parts[2].trim_matches('"');
|
||||
let pnp_id = parts[3].trim_matches('"').to_string();
|
||||
|
||||
let memory_mb = if !adapter_ram_str.is_empty() && adapter_ram_str != "AdapterRAM" {
|
||||
adapter_ram_str.parse::<u64>().ok().map(|bytes| bytes / (1024 * 1024))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let vendor = detect_vendor_from_name(&name);
|
||||
let id = if !pnp_id.is_empty() && pnp_id != "PNPDeviceID" {
|
||||
format!("gpu_{}", pnp_id.replace("\\", "_").replace("/", "_"))
|
||||
} else {
|
||||
format!("gpu_{}", gpus.len())
|
||||
};
|
||||
|
||||
log::info!("GPU detectada via wmic: {} (VRAM: {:?} MB)", name, memory_mb);
|
||||
|
||||
gpus.push(GpuInfo {
|
||||
id,
|
||||
name,
|
||||
vendor,
|
||||
memory_mb,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::warn!("Falha ao executar wmic, tentando formato list...");
|
||||
|
||||
// Fallback para formato list
|
||||
if let Ok(output) = Command::new("wmic")
|
||||
.args(&["path", "win32_VideoController", "get", "name,AdapterRAM,PNPDeviceID", "/format:list"])
|
||||
.output()
|
||||
{
|
||||
if let Ok(stdout) = String::from_utf8(output.stdout) {
|
||||
let mut current_gpu: Option<GpuInfo> = None;
|
||||
|
||||
for line in stdout.lines() {
|
||||
if line.contains("VGA") || line.contains("Display") {
|
||||
return Some(line.to_string());
|
||||
let line = line.trim();
|
||||
if line.is_empty() {
|
||||
if let Some(gpu) = current_gpu.take() {
|
||||
gpus.push(gpu);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if line.starts_with("Name=") {
|
||||
let name = line.replace("Name=", "").trim().to_string();
|
||||
if !name.is_empty() {
|
||||
let vendor = detect_vendor_from_name(&name);
|
||||
current_gpu = Some(GpuInfo {
|
||||
id: format!("gpu_{}", gpus.len()),
|
||||
name,
|
||||
vendor,
|
||||
memory_mb: None,
|
||||
});
|
||||
}
|
||||
} else if line.starts_with("AdapterRAM=") {
|
||||
if let Some(gpu) = &mut current_gpu {
|
||||
if let Ok(memory_bytes) = line.replace("AdapterRAM=", "").trim().parse::<u64>() {
|
||||
gpu.memory_mb = Some(memory_bytes / (1024 * 1024));
|
||||
}
|
||||
}
|
||||
} else if line.starts_with("PNPDeviceID=") {
|
||||
if let Some(gpu) = &mut current_gpu {
|
||||
let pnp_id = line.replace("PNPDeviceID=", "").trim().to_string();
|
||||
if !pnp_id.is_empty() {
|
||||
gpu.id = format!("gpu_{}", pnp_id.replace("\\", "_").replace("/", "_"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(gpu) = current_gpu {
|
||||
gpus.push(gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gpus
|
||||
}
|
||||
|
||||
/// Detecta GPUs NVIDIA usando nvidia-smi
|
||||
#[cfg(target_os = "windows")]
|
||||
fn detect_gpus_nvidia_smi() -> Result<Vec<GpuInfo>, String> {
|
||||
use std::process::Command;
|
||||
|
||||
log::info!("Executando nvidia-smi...");
|
||||
|
||||
let output = Command::new("nvidia-smi")
|
||||
.args(&["--query-gpu=name,memory.total", "--format=csv,noheader,nounits"])
|
||||
.output()
|
||||
.map_err(|e| format!("nvidia-smi não encontrado: {}", e))?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err("nvidia-smi falhou".to_string());
|
||||
}
|
||||
|
||||
let stdout = String::from_utf8(output.stdout)
|
||||
.map_err(|e| format!("Erro ao parsear output do nvidia-smi: {}", e))?;
|
||||
|
||||
log::debug!("nvidia-smi output: {}", stdout);
|
||||
|
||||
let mut gpus = Vec::new();
|
||||
for (idx, line) in stdout.lines().enumerate() {
|
||||
let parts: Vec<&str> = line.split(',').map(|s| s.trim()).collect();
|
||||
if parts.len() >= 2 {
|
||||
let name = parts[0].trim().to_string();
|
||||
let memory_mb = parts[1].trim().parse::<u64>().ok();
|
||||
|
||||
if !name.is_empty() {
|
||||
log::info!("GPU NVIDIA detectada via nvidia-smi: {} ({} MB)", name, memory_mb.unwrap_or(0));
|
||||
|
||||
gpus.push(GpuInfo {
|
||||
id: format!("nvidia_gpu_{}", idx),
|
||||
name,
|
||||
vendor: Some("NVIDIA".to_string()),
|
||||
memory_mb,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(gpus)
|
||||
}
|
||||
|
||||
/// Tenta detectar GPUs via API do Ollama (quando disponível)
|
||||
/// Nota: O Ollama não expõe diretamente informações de GPU via API pública
|
||||
/// Esta função verifica se o Ollama está rodando, mas a detecção real
|
||||
/// é feita via métodos do sistema operacional (wmic, nvidia-smi, etc)
|
||||
fn detect_gpus_ollama_api() -> Result<Vec<GpuInfo>, String> {
|
||||
// Por enquanto, retornamos vazio pois o Ollama não tem endpoint público de GPU
|
||||
// A detecção é feita via métodos do sistema operacional que são mais confiáveis
|
||||
// Esta função pode ser expandida no futuro se o Ollama adicionar um endpoint de GPU
|
||||
|
||||
log::debug!("Verificação de API do Ollama não implementada (não há endpoint público de GPU)");
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
/// Detecta GPUs no Linux usando múltiplas fontes
|
||||
#[cfg(target_os = "linux")]
|
||||
fn detect_gpus_linux() -> Vec<GpuInfo> {
|
||||
use std::process::Command;
|
||||
let mut gpus = Vec::new();
|
||||
|
||||
// 1. Tentar lspci primeiro
|
||||
log::info!("Tentando lspci...");
|
||||
if let Ok(output) = Command::new("lspci")
|
||||
.args(&["-m", "-d", "::0300"]) // Apenas dispositivos VGA
|
||||
.output()
|
||||
{
|
||||
if let Ok(stdout) = String::from_utf8(output.stdout) {
|
||||
for (index, line) in stdout.lines().enumerate() {
|
||||
let parts: Vec<&str> = line.split('"').collect();
|
||||
if parts.len() >= 2 {
|
||||
let name = parts[1].trim().to_string();
|
||||
if !name.is_empty() {
|
||||
let vendor = detect_vendor_from_name(&name);
|
||||
log::info!("GPU detectada via lspci: {}", name);
|
||||
gpus.push(GpuInfo {
|
||||
id: format!("gpu_{}", index),
|
||||
name,
|
||||
vendor,
|
||||
memory_mb: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
use std::process::Command;
|
||||
|
||||
// Tenta usar system_profiler no macOS
|
||||
if let Ok(output) = Command::new("system_profiler")
|
||||
.args(&["SPDisplaysDataType"])
|
||||
// 2. Se não encontrou, tentar método alternativo
|
||||
if gpus.is_empty() {
|
||||
log::info!("Tentando lspci | grep como fallback...");
|
||||
if let Ok(output) = Command::new("sh")
|
||||
.args(&["-c", "lspci | grep -i vga"])
|
||||
.output()
|
||||
{
|
||||
if let Ok(stdout) = String::from_utf8(output.stdout) {
|
||||
for line in stdout.lines() {
|
||||
if line.contains("Chipset Model") {
|
||||
return Some(line.replace("Chipset Model:", "").trim().to_string());
|
||||
for (index, line) in stdout.lines().enumerate() {
|
||||
let line = line.trim();
|
||||
if !line.is_empty() && (line.contains("VGA") || line.contains("Display")) {
|
||||
let parts: Vec<&str> = line.splitn(2, ':').collect();
|
||||
if parts.len() >= 2 {
|
||||
let name = parts[1].trim().to_string();
|
||||
if !name.is_empty() {
|
||||
let vendor = detect_vendor_from_name(&name);
|
||||
log::info!("GPU detectada via lspci grep: {}", name);
|
||||
gpus.push(GpuInfo {
|
||||
id: format!("gpu_{}", index),
|
||||
name,
|
||||
vendor,
|
||||
memory_mb: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Tentar nvidia-smi se não encontrou GPUs NVIDIA
|
||||
let has_nvidia = gpus.iter().any(|g| g.vendor.as_ref().map(|v| v == "NVIDIA").unwrap_or(false));
|
||||
if !has_nvidia {
|
||||
log::info!("Tentando nvidia-smi...");
|
||||
if let Ok(nvidia_gpus) = detect_gpus_nvidia_smi_linux() {
|
||||
for nvidia_gpu in nvidia_gpus {
|
||||
if !gpus.iter().any(|g| g.name == nvidia_gpu.name) {
|
||||
gpus.push(nvidia_gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Tentar /sys/class/drm/ como último recurso
|
||||
if gpus.is_empty() {
|
||||
log::info!("Tentando /sys/class/drm/...");
|
||||
if let Ok(entries) = std::fs::read_dir("/sys/class/drm") {
|
||||
for (idx, entry) in entries.enumerate() {
|
||||
if let Ok(entry) = entry {
|
||||
let path = entry.path();
|
||||
if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
|
||||
if name.starts_with("card") && name != "card" {
|
||||
if let Ok(name_file) = std::fs::read_to_string(path.join("name")) {
|
||||
let gpu_name = name_file.trim().to_string();
|
||||
if !gpu_name.is_empty() {
|
||||
let vendor = detect_vendor_from_name(&gpu_name);
|
||||
log::info!("GPU detectada via /sys/class/drm/: {}", gpu_name);
|
||||
gpus.push(GpuInfo {
|
||||
id: format!("gpu_{}", idx),
|
||||
name: gpu_name,
|
||||
vendor,
|
||||
memory_mb: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gpus
|
||||
}
|
||||
|
||||
/// Detecta GPUs NVIDIA no Linux usando nvidia-smi
|
||||
#[cfg(target_os = "linux")]
|
||||
fn detect_gpus_nvidia_smi_linux() -> Result<Vec<GpuInfo>, String> {
|
||||
use std::process::Command;
|
||||
|
||||
let output = Command::new("nvidia-smi")
|
||||
.args(&["--query-gpu=name,memory.total", "--format=csv,noheader,nounits"])
|
||||
.output()
|
||||
.map_err(|e| format!("nvidia-smi não encontrado: {}", e))?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err("nvidia-smi falhou".to_string());
|
||||
}
|
||||
|
||||
let stdout = String::from_utf8(output.stdout)
|
||||
.map_err(|e| format!("Erro ao parsear output do nvidia-smi: {}", e))?;
|
||||
|
||||
let mut gpus = Vec::new();
|
||||
for (idx, line) in stdout.lines().enumerate() {
|
||||
let parts: Vec<&str> = line.split(',').map(|s| s.trim()).collect();
|
||||
if parts.len() >= 2 {
|
||||
let name = parts[0].trim().to_string();
|
||||
let memory_mb = parts[1].trim().parse::<u64>().ok();
|
||||
|
||||
if !name.is_empty() {
|
||||
log::info!("GPU NVIDIA detectada via nvidia-smi: {} ({} MB)", name, memory_mb.unwrap_or(0));
|
||||
gpus.push(GpuInfo {
|
||||
id: format!("nvidia_gpu_{}", idx),
|
||||
name,
|
||||
vendor: Some("NVIDIA".to_string()),
|
||||
memory_mb,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(gpus)
|
||||
}
|
||||
|
||||
/// Detecta GPUs no macOS usando system_profiler
|
||||
#[cfg(target_os = "macos")]
|
||||
fn detect_gpus_macos() -> Vec<GpuInfo> {
|
||||
use std::process::Command;
|
||||
let mut gpus = Vec::new();
|
||||
|
||||
log::info!("Executando system_profiler...");
|
||||
|
||||
if let Ok(output) = Command::new("system_profiler")
|
||||
.args(&["SPDisplaysDataType"])
|
||||
.output()
|
||||
{
|
||||
if let Ok(stdout) = String::from_utf8(output.stdout) {
|
||||
let mut current_gpu: Option<GpuInfo> = None;
|
||||
let mut gpu_index = 0;
|
||||
|
||||
for line in stdout.lines() {
|
||||
let line = line.trim();
|
||||
|
||||
if line.starts_with("Chipset Model:") {
|
||||
// Finalizar GPU anterior se houver
|
||||
if let Some(gpu) = current_gpu.take() {
|
||||
gpus.push(gpu);
|
||||
}
|
||||
|
||||
let name = line.replace("Chipset Model:", "").trim().to_string();
|
||||
if !name.is_empty() {
|
||||
let vendor = detect_vendor_from_name(&name);
|
||||
log::info!("GPU detectada no macOS: {}", name);
|
||||
current_gpu = Some(GpuInfo {
|
||||
id: format!("gpu_{}", gpu_index),
|
||||
name,
|
||||
vendor,
|
||||
memory_mb: None,
|
||||
});
|
||||
gpu_index += 1;
|
||||
}
|
||||
} else if line.starts_with("VRAM (Total):") {
|
||||
if let Some(gpu) = &mut current_gpu {
|
||||
let vram_str = line.replace("VRAM (Total):", "").trim();
|
||||
if let Some(mb) = parse_memory_string(&vram_str) {
|
||||
gpu.memory_mb = Some(mb);
|
||||
log::info!("VRAM detectada: {} MB", mb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adicionar última GPU se houver
|
||||
if let Some(gpu) = current_gpu {
|
||||
gpus.push(gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gpus
|
||||
}
|
||||
|
||||
/// Detecta o vendor (fabricante) da GPU baseado no nome
|
||||
fn detect_vendor_from_name(name: &str) -> Option<String> {
|
||||
let name_lower = name.to_lowercase();
|
||||
|
||||
if name_lower.contains("nvidia") || name_lower.contains("geforce") || name_lower.contains("quadro") || name_lower.contains("tesla") {
|
||||
return Some("NVIDIA".to_string());
|
||||
}
|
||||
|
||||
if name_lower.contains("amd") || name_lower.contains("radeon") || name_lower.contains("firepro") {
|
||||
return Some("AMD".to_string());
|
||||
}
|
||||
|
||||
if name_lower.contains("intel") || name_lower.contains("iris") || name_lower.contains("uhd") || name_lower.contains("hd graphics") {
|
||||
return Some("Intel".to_string());
|
||||
}
|
||||
|
||||
if name_lower.contains("apple") {
|
||||
return Some("Apple".to_string());
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Parse string de memória (ex: "8 GB" -> 8192 MB)
|
||||
#[allow(dead_code)]
|
||||
fn parse_memory_string(s: &str) -> Option<u64> {
|
||||
let s = s.trim().to_lowercase();
|
||||
|
||||
// Tentar extrair número e unidade
|
||||
let parts: Vec<&str> = s.split_whitespace().collect();
|
||||
if parts.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Ok(value) = parts[0].parse::<f64>() {
|
||||
let multiplier = if s.contains("gb") || s.contains("g") {
|
||||
1024 // GB para MB
|
||||
} else if s.contains("mb") || s.contains("m") {
|
||||
1 // Já está em MB
|
||||
} else {
|
||||
// Assumir GB se não especificado
|
||||
1024
|
||||
};
|
||||
|
||||
return Some((value * multiplier as f64) as u64);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user