diff --git a/Cargo.toml b/Cargo.toml index e65bf79..aca8bb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,10 @@ name = "dmed" version = "0.1.0" edition = "2021" +[lib] +name = "dmed" +path = "src/lib.rs" + [dependencies] serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/RELATORIO_PROSOFT_MOD_51.XLS b/RELATORIO_PROSOFT_MOD_51.XLS new file mode 100644 index 0000000..bedba5f Binary files /dev/null and b/RELATORIO_PROSOFT_MOD_51.XLS differ diff --git a/examples/comparador.rs b/examples/comparador.rs new file mode 100644 index 0000000..6f9ef9d --- /dev/null +++ b/examples/comparador.rs @@ -0,0 +1,76 @@ +// Exemplo de como usar a lib dmed em um comparador + +use dmed::{processar_arquivo, Pessoa, buscar_por_cpf}; +use std::collections::HashMap; + +fn main() -> std::io::Result<()> { + // Processa dois arquivos diferentes + println!("=== COMPARADOR DE ARQUIVOS DMED ===\n"); + + let arquivo1 = "LfsIssDmed068325.txt"; + let pessoas1 = processar_arquivo(arquivo1)?; + + println!("Arquivo 1: {} registros", pessoas1.len()); + // let pessoas2 = processar_arquivo("arquivo2.txt")?; + // println!("Arquivo 2: {} registros\n", pessoas2.len()); + + // Cria um mapa de CPF -> Pessoa para busca rápida + let mapa1: HashMap<&str, &Pessoa> = pessoas1 + .iter() + .map(|p| (p.cpf.as_str(), p)) + .collect(); + + // Exemplo 1: Buscar CPF específico + println!("=== Busca de CPF ==="); + let cpf_busca = "00276808860"; + if let Some(pessoa) = buscar_por_cpf(&pessoas1, cpf_busca) { + println!("✓ CPF {} encontrado", cpf_busca); + println!(" Nome: {}", pessoa.nome); + println!(" Valor: R$ {:.2}", pessoa.valor); + } + + // Exemplo 2: Comparar valores de mesmos CPFs entre arquivos + println!("\n=== Comparação de valores ==="); + // Simulando segundo arquivo com mesmos CPFs mas valores diferentes + let cpfs_exemplo = ["00276808860", "00633999032", "01133252826"]; + + for cpf in &cpfs_exemplo { + if let Some(p1) = mapa1.get(cpf) { + println!("\nCPF: {}", cpf); + println!(" Arquivo 1: R$ {:.2}", p1.valor); + // if let Some(p2) = mapa2.get(cpf) { + // let diferenca = p2.valor - p1.valor; + // println!(" Arquivo 2: R$ {:.2}", p2.valor); + // println!(" Diferença: R$ {:.2} ({:+.1}%)", + // diferenca, + // (diferenca / p1.valor) * 100.0 + // ); + // } + } + } + + // Exemplo 3: Encontrar divergências + println!("\n=== Análise de divergências ==="); + let total_registros = pessoas1.len(); + let com_valor = pessoas1.iter().filter(|p| p.valor > 0.0).count(); + let sem_valor = total_registros - com_valor; + + println!("Total de registros: {}", total_registros); + println!("Com valor: {}", com_valor); + println!("Sem valor: {}", sem_valor); + + // Exemplo 4: Top 5 maiores valores + println!("\n=== Top 5 maiores valores ==="); + let mut pessoas_sorted = pessoas1.clone(); + pessoas_sorted.sort_by(|a, b| b.valor.partial_cmp(&a.valor).unwrap()); + + for (i, pessoa) in pessoas_sorted.iter().take(5).enumerate() { + println!("{}. {} - R$ {:.2}", + i + 1, + pessoa.nome, + pessoa.valor + ); + } + + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..99a0e12 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,109 @@ +use serde::{Deserialize, Serialize}; +use std::fs; +use std::io; + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct Pessoa { + pub cpf: String, + pub nome: String, + pub valor: f64, +} + +impl Pessoa { + /// Cria uma Pessoa a partir de uma linha do arquivo + pub fn from_line(line: &str) -> Option { + let parts: Vec<&str> = line.split('|').collect(); + + // Verifica se a linha começa com RPPSS e tem pelo menos 4 campos + if parts.len() >= 4 && parts[0] == "RPPSS" { + let cpf = parts[1].to_string(); + let nome = parts[2].to_string(); + + // Converte o valor: os 2 últimos dígitos são decimais + let valor_str = parts[3]; + let valor = if !valor_str.is_empty() { + valor_str.parse::().unwrap_or(0.0) / 100.0 + } else { + 0.0 + }; + + return Some(Pessoa { cpf, nome, valor }); + } + + None + } +} + +/// Processa um arquivo DMED e retorna um vetor de pessoas +pub fn processar_arquivo(caminho: &str) -> io::Result> { + let conteudo = fs::read_to_string(caminho)?; + processar_conteudo(&conteudo) +} + +/// Processa o conteúdo de um arquivo DMED (útil para testes ou strings) +pub fn processar_conteudo(conteudo: &str) -> io::Result> { + let linhas: Vec<&str> = conteudo.lines().collect(); + + let mut pessoas = Vec::new(); + + // Pula as 4 primeiras linhas e processa o resto + for linha in linhas.iter().skip(4) { + if let Some(pessoa) = Pessoa::from_line(linha) { + pessoas.push(pessoa); + } + } + + Ok(pessoas) +} + +/// Converte um vetor de pessoas para JSON +pub fn para_json(pessoas: &[Pessoa]) -> Result { + serde_json::to_string_pretty(pessoas) +} + +/// Busca uma pessoa pelo CPF +pub fn buscar_por_cpf<'a>(pessoas: &'a [Pessoa], cpf: &str) -> Option<&'a Pessoa> { + pessoas.iter().find(|p| p.cpf == cpf) +} + +/// Filtra pessoas com valor acima de um mínimo +pub fn filtrar_por_valor_minimo(pessoas: &[Pessoa], valor_minimo: f64) -> Vec<&Pessoa> { + pessoas.iter().filter(|p| p.valor >= valor_minimo).collect() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_pessoa_from_line() { + let linha = "RPPSS|00276808860|MARCO ANTONIO MACHADO|2200000|"; + let pessoa = Pessoa::from_line(linha).unwrap(); + + assert_eq!(pessoa.cpf, "00276808860"); + assert_eq!(pessoa.nome, "MARCO ANTONIO MACHADO"); + assert_eq!(pessoa.valor, 22000.0); + } + + #[test] + fn test_pessoa_from_line_invalida() { + let linha = "OUTRAS|123|TESTE|100|"; + assert!(Pessoa::from_line(linha).is_none()); + } + + #[test] + fn test_processar_conteudo() { + let conteudo = r#"Dmed|2026|2025|N||| +RESPO|21681939835|Anelisa Pirola Chalegre|17|35316200|205||anelisa@edegarpirolacontabilidade.com.br| +DECPJ|03364887000195|CLINICA DE CIRURGIA PLASTICA DR VIDAL LTDA|1|||01877281867|N||| +PSS| +RPPSS|00276808860|MARCO ANTONIO MACHADO|2200000| +RPPSS|00633999032|ELISANDRA BARBOSA TORRES|600000| +FIMDmed|"#; + + let pessoas = processar_conteudo(conteudo).unwrap(); + assert_eq!(pessoas.len(), 2); + assert_eq!(pessoas[0].cpf, "00276808860"); + assert_eq!(pessoas[1].valor, 6000.0); + } +} diff --git a/src/main.rs b/src/main.rs index 3d8686b..d7a095e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,53 +1,6 @@ -use serde::{Deserialize, Serialize}; +use dmed::{processar_arquivo, para_json, buscar_por_cpf, filtrar_por_valor_minimo}; use std::fs; -use std::io::{self, BufRead}; - -#[derive(Debug, Serialize, Deserialize, Clone)] -struct Pessoa { - cpf: String, - nome: String, - valor: f64, -} - -impl Pessoa { - fn from_line(line: &str) -> Option { - let parts: Vec<&str> = line.split('|').collect(); - - // Verifica se a linha começa com RPPSS e tem pelo menos 4 campos - if parts.len() >= 4 && parts[0] == "RPPSS" { - let cpf = parts[1].to_string(); - let nome = parts[2].to_string(); - - // Converte o valor: os 2 últimos dígitos são decimais - let valor_str = parts[3]; - let valor = if !valor_str.is_empty() { - valor_str.parse::().unwrap_or(0.0) / 100.0 - } else { - 0.0 - }; - - return Some(Pessoa { cpf, nome, valor }); - } - - None - } -} - -fn processar_arquivo(caminho: &str) -> io::Result> { - let conteudo = fs::read_to_string(caminho)?; - let linhas: Vec<&str> = conteudo.lines().collect(); - - let mut pessoas = Vec::new(); - - // Pula as 4 primeiras linhas e processa o resto - for linha in linhas.iter().skip(4) { - if let Some(pessoa) = Pessoa::from_line(linha) { - pessoas.push(pessoa); - } - } - - Ok(pessoas) -} +use std::io; fn main() -> io::Result<()> { let arquivo = "LfsIssDmed068325.txt"; @@ -68,7 +21,7 @@ fn main() -> io::Result<()> { println!("\n=== Convertendo para JSON ==="); // Converte para JSON (formatado) - let json = serde_json::to_string_pretty(&pessoas)?; + let json = para_json(&pessoas).expect("Erro ao converter para JSON"); // Exibe primeiros caracteres do JSON println!("JSON gerado ({} bytes):", json.len()); @@ -80,13 +33,21 @@ fn main() -> io::Result<()> { println!("\n✓ JSON salvo em output.json"); // Exemplo: buscar uma pessoa específica - println!("\n=== Exemplo de busca ==="); - if let Some(pessoa) = pessoas.iter().find(|p| p.cpf == "00276808860") { + println!("\n=== Exemplo de busca por CPF ==="); + if let Some(pessoa) = buscar_por_cpf(&pessoas, "00276808860") { println!("Pessoa encontrada:"); println!(" CPF: {}", pessoa.cpf); println!(" Nome: {}", pessoa.nome); println!(" Valor: R$ {:.2}", pessoa.valor); } + // Exemplo: filtrar por valor mínimo + println!("\n=== Pessoas com valor >= R$ 10.000,00 ==="); + let pessoas_alto_valor = filtrar_por_valor_minimo(&pessoas, 10000.0); + println!("Total: {} pessoas", pessoas_alto_valor.len()); + for pessoa in pessoas_alto_valor.iter().take(3) { + println!(" {} - R$ {:.2}", pessoa.nome, pessoa.valor); + } + Ok(()) }