From 90e3632d88d535f9667b5a35c90d3c350b76cbef Mon Sep 17 00:00:00 2001 From: cleissonalves Date: Tue, 24 Dec 2024 13:49:59 -0300 Subject: [PATCH] consultar aceita mais de uma aposta atraves do terminal --- Cargo.toml | 16 +-- src/cli_derive.rs | 25 +++-- src/client.rs | 4 +- src/commands.rs | 42 ++++---- src/data.rs | 226 ++++++++++++++++++++--------------------- src/deprecated/file.rs | 4 +- src/file.rs | 8 +- src/main.rs | 11 +- src/parsers.rs | 73 +++++++------ 9 files changed, 214 insertions(+), 195 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 790343d..dd66502 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,13 +14,17 @@ codegen-units = 1 # Allow for maximum size reduction optimizations. # panic = "abort" # remove unwinds of stack on panics to produce helpful backtraces. Use with caution, possible impact on behaviour. [dependencies] -anyhow = "1.0.93" +anyhow = "1.0.95" atty = "0.2.14" -clap = { version = "4.5.21", features = ["derive"] } -colored = "2.1.0" +clap = { version = "4.5.23", features = ["derive"] } +colored = "2.2.0" # indicatif = "0.17.9" # derive_more = { version = "1.0.0", features = ["display"] } -minreq = { version = "2.12.0", features = ["https"] } -serde = { version = "1.0.215", features = ["derive"] } -serde_json = "1.0.133" +minreq = { version = "2.13.0", features = ["https"] } +serde = { version = "1.0.216", features = ["derive"] } +serde_json = "1.0.134" strum = { version = "0.26.3", features = ["derive"] } + +[lints.clippy] +bool_comparison = "allow" +zero_prefixed_literal = "allow" \ No newline at end of file diff --git a/src/cli_derive.rs b/src/cli_derive.rs index 9c5710d..07f5a31 100644 --- a/src/cli_derive.rs +++ b/src/cli_derive.rs @@ -20,8 +20,8 @@ pub enum Commands { #[clap(about = "Consultar ultimo sorteio e comparar com aposta(s).")] Consultar { jogo: data::Jogo, - #[clap(help = "Opcional: Numeros de uma aposta. Ex. 04-08-15-16-23-42")] - numeros: Option, + #[clap(help = "Opcional: Apostas. Ex. 04-08-15-16-23-42 03-07-14-15-22-41")] + numeros: Option>, }, #[clap(about = "Mostrar historico de sorteios.")] Historico { @@ -71,12 +71,15 @@ pub fn run() -> Result<()> { Ok(()) } -fn consultar(jogo: &data::Jogo, numeros: &Option) -> Result<()> { +fn consultar(jogo: &data::Jogo, numeros: &Option>) -> Result<()> { match numeros { Some(nums) => { - let numeros = parsers::vec_u8_from_str(&nums)?; - jogo.validar(&numeros)?; - commands::consultar_numeros(&jogo, &vec![numeros.as_slice()])?; + let apostas = parsers::vstrings_to_vu8s(nums)?; + for aposta in &apostas { + jogo.validar(aposta)?; + } + let apostas_refs: Vec<&[u8]> = apostas.iter().map(|v| v.as_slice()).collect(); + commands::consultar_numeros(jogo, &apostas_refs)?; } None => { if atty::isnt(atty::Stream::Stdin) { @@ -84,18 +87,18 @@ fn consultar(jogo: &data::Jogo, numeros: &Option) -> Result<()> { let numbers_from_file = parsers::vec_u8_from_buffer()?; let numbers_refs: Vec<&[u8]> = numbers_from_file.iter().map(|v| v.as_slice()).collect(); - commands::consultar_numeros(&jogo, &numbers_refs)?; + commands::consultar_numeros(jogo, &numbers_refs)?; } else { - commands::consultar(&jogo)?; + commands::consultar(jogo)?; } } } Ok(()) } -fn analisar(jogo: &data::Jogo, numeros: &String, quantidade: usize) -> Result<()> { - let numeros = parsers::vec_u8_from_str(&numeros)?; +fn analisar(jogo: &data::Jogo, numeros: &str, quantidade: usize) -> Result<()> { + let numeros = parsers::vec_u8_from_str(numeros)?; jogo.validar(&numeros)?; - commands::analisar(&jogo, numeros.as_slice(), quantidade)?; + commands::analisar(jogo, numeros.as_slice(), quantidade)?; Ok(()) } diff --git a/src/client.rs b/src/client.rs index 7ff3c4c..900ef89 100644 --- a/src/client.rs +++ b/src/client.rs @@ -3,7 +3,7 @@ use anyhow::{bail, Context, Ok, Result}; use std::fs::File; // https://github.com/guto-alves/loterias-api -pub const API_PATH: &'static str = "https://loteriascaixa-api.herokuapp.com/api/"; +pub const API_PATH: &str = "https://loteriascaixa-api.herokuapp.com/api/"; pub fn fetch_latest(jogo: &Jogo) -> Result { let request_url = format!("{}{}/latest", API_PATH, jogo); @@ -53,7 +53,7 @@ pub fn download_json(jogo: &Jogo) -> Result<()> { let content = response.as_str().context("Parsing Response to json str")?; let json: serde_json::Value = - serde_json::from_str(&content).context("Deserializing json str to serde_json::Value")?; + serde_json::from_str(content).context("Deserializing json str to serde_json::Value")?; let mut file = File::create(format!("{jogo}.json")).context("Opening File")?; diff --git a/src/commands.rs b/src/commands.rs index 849ff43..7695d73 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -10,7 +10,7 @@ use anyhow::Result; use colored::{ColoredString, Colorize}; pub fn consultar_numeros(jogo: &Jogo, apostas: &Vec<&[u8]>) -> Result<()> { - let sorteio = client::fetch_latest(&jogo)?; + let sorteio = client::fetch_latest(jogo)?; let show_aposta_id = apostas.len() > 1; @@ -44,7 +44,7 @@ pub fn consultar_numeros(jogo: &Jogo, apostas: &Vec<&[u8]>) -> Result<()> { } pub fn consultar(jogo: &Jogo) -> Result<()> { - let sorteio = client::fetch_latest(&jogo)?; + let sorteio = client::fetch_latest(jogo)?; print_sorteio_info(&sorteio); @@ -99,7 +99,7 @@ fn analisar_sorteio(aposta: &[u8], sorteio: &Sorteio) -> (u8, Vec for n in &sorteio.numeros { let n_string = format!("{:02}", n).to_string(); - if aposta.contains(&n) { + if aposta.contains(n) { matches += 1; output.push(n_string.bright_green()); } else { @@ -125,10 +125,10 @@ fn analisar_sorteio(aposta: &[u8], sorteio: &Sorteio) -> (u8, Vec (matches, output) } -fn analisar_sorteios(jogo: &Jogo, aposta: &[u8], sorteios: &Vec) { +fn analisar_sorteios(jogo: &Jogo, aposta: &[u8], sorteios: &[Sorteio]) { let matches: Vec<(u8, Vec)> = sorteios .iter() - .map(|s| analisar_sorteio(&aposta, &s)) + .map(|s| analisar_sorteio(aposta, s)) .collect(); let prize_matches = jogo.get_prize_matches(); @@ -146,7 +146,7 @@ fn analisar_sorteios(jogo: &Jogo, aposta: &[u8], sorteios: &Vec) { .map(|s| s.1.clone()) .collect(); - if sorteios_com_n_acertos.len() == 0 { + if sorteios_com_n_acertos.is_empty() { continue; } @@ -160,28 +160,28 @@ fn analisar_sorteios(jogo: &Jogo, aposta: &[u8], sorteios: &Vec) { for m in s { print!("{} ", m); } - print!("\n"); + println!(); } - print!("\n"); + println!(); } - print!("Total de premiacoes: {}\n", matches.len()); + println!("Total de premiacoes: {}", matches.len()); } pub fn historico(jogo: &Jogo, quantidade: usize) -> Result<()> { let mut sorteios: Vec; - if file::is_update_needed(&jogo).unwrap_or(true) { - let content = client::fetch_all(&jogo)?; + if file::is_update_needed(jogo).unwrap_or(true) { + let content = client::fetch_all(jogo)?; file::save_json(&content)?; } - if let Ok(value) = file::load_json(&jogo) { + if let Ok(value) = file::load_json(jogo) { sorteios = value; } else { - let content = client::fetch_all(&jogo)?; + let content = client::fetch_all(jogo)?; file::save_json(&content)?; - sorteios = file::load_json(&jogo)?; + sorteios = file::load_json(jogo)?; } if quantidade > 0 { @@ -246,7 +246,7 @@ pub fn get_numeros_e_ocorrencias(sorteios: &Vec) -> Vec<(u8, u32)> { } pub fn imprimir_numeros_mais_sorteados(sorteios: &Vec, quantidade: usize) { - let ocorrencias = get_numeros_e_ocorrencias(&sorteios); + let ocorrencias = get_numeros_e_ocorrencias(sorteios); let ocorrencias = ocorrencias.iter().take(quantidade); let mut output = Vec::new(); @@ -267,17 +267,17 @@ pub fn imprimir_numeros_mais_sorteados(sorteios: &Vec, quantidade: usiz pub fn analisar(jogo: &Jogo, numeros: &[u8], quantidade: usize) -> Result<()> { let mut sorteios: Vec; - if file::is_update_needed(&jogo).unwrap_or(true) { - let content = client::fetch_all(&jogo)?; + if file::is_update_needed(jogo).unwrap_or(true) { + let content = client::fetch_all(jogo)?; file::save_json(&content)?; } - if let Ok(value) = file::load_json(&jogo) { + if let Ok(value) = file::load_json(jogo) { sorteios = value; } else { - let content = client::fetch_all(&jogo)?; + let content = client::fetch_all(jogo)?; file::save_json(&content)?; - sorteios = file::load_json(&jogo)?; + sorteios = file::load_json(jogo)?; } if quantidade > 0 { @@ -286,7 +286,7 @@ pub fn analisar(jogo: &Jogo, numeros: &[u8], quantidade: usize) -> Result<()> { sorteios.reverse(); - analisar_sorteios(&jogo, &numeros, &sorteios); + analisar_sorteios(jogo, numeros, &sorteios); println!("\nPara mais resultados, adicione a opcao '--quantidade ' ou '-q '."); diff --git a/src/data.rs b/src/data.rs index e43bb27..0e4e0d3 100644 --- a/src/data.rs +++ b/src/data.rs @@ -9,150 +9,150 @@ use strum::{Display, EnumString}; #[strum(serialize_all = "lowercase")] // for internal usage #[serde(rename_all = "lowercase")] // for parsing json pub enum Jogo { - Lotofacil, - Lotomania, - Megasena, - Quina, + Lotofacil, + Lotomania, + Megasena, + Quina, } fn are_all_distinct(vec: &[T]) -> bool { - let set: HashSet<_> = vec.iter().collect(); - set.len() == vec.len() + let set: HashSet<_> = vec.iter().collect(); + set.len() == vec.len() } impl Jogo { - pub fn get_prize_matches(&self) -> Vec { - match self { - Jogo::Lotofacil => vec![11, 12, 13, 14, 15], - Jogo::Lotomania => vec![0, 15, 16, 17, 18, 19, 20], - Jogo::Megasena => vec![4, 5, 6], - Jogo::Quina => vec![2, 3, 4, 5], - } - } - - fn get_quantity_range(&self) -> RangeInclusive { - match self { - Jogo::Lotofacil => 15..=18, - Jogo::Lotomania => 50..=50, - Jogo::Megasena => 6..=20, - Jogo::Quina => 5..=15, - } - } - fn get_numbers_range(&self) -> RangeInclusive { - match self { - Jogo::Lotofacil => 1..=25, - Jogo::Lotomania => 0..=99, - Jogo::Megasena => 1..=60, - Jogo::Quina => 1..=80, - } - } - pub fn validar(&self, numeros: &[u8]) -> Result<()> { - if are_all_distinct(&numeros) == false { - bail!("Numeros invalidos. Contem valores repetidos."); - } - - let quantity_range = self.get_quantity_range(); - let numbers_range = self.get_numbers_range(); - - if quantity_range.contains(&numeros.len()) == false { - bail!( - "Quantidade invalida de numeros selecionados ({}). Escolha entre {} e {} numeros.", - numeros.len(), - quantity_range.start(), - quantity_range.end() - ); - } - - if numeros - .iter() - .any(|n| numbers_range.contains(&(*n as usize)) == false) - { - bail!( - "Numeros invalidos selecionados. Escolha valores entre {} e {}.", - numbers_range.start(), - numbers_range.end() - ); - } - - Ok(()) - } + pub fn get_prize_matches(&self) -> Vec { + match self { + Jogo::Lotofacil => vec![11, 12, 13, 14, 15], + Jogo::Lotomania => vec![0, 15, 16, 17, 18, 19, 20], + Jogo::Megasena => vec![4, 5, 6], + Jogo::Quina => vec![2, 3, 4, 5], + } + } + + fn get_quantity_range(&self) -> RangeInclusive { + match self { + Jogo::Lotofacil => 15..=18, + Jogo::Lotomania => 50..=50, + Jogo::Megasena => 6..=20, + Jogo::Quina => 5..=15, + } + } + fn get_numbers_range(&self) -> RangeInclusive { + match self { + Jogo::Lotofacil => 1..=25, + Jogo::Lotomania => 0..=99, + Jogo::Megasena => 1..=60, + Jogo::Quina => 1..=80, + } + } + pub fn validar(&self, numeros: &[u8]) -> Result<()> { + if are_all_distinct(numeros) == false { + bail!("Numeros invalidos. Contem valores repetidos."); + } + + let quantity_range = self.get_quantity_range(); + let numbers_range = self.get_numbers_range(); + + if quantity_range.contains(&numeros.len()) == false { + bail!( + "Quantidade invalida de numeros selecionados ({}). Escolha entre {} e {} numeros.", + numeros.len(), + quantity_range.start(), + quantity_range.end() + ); + } + + if numeros + .iter() + .any(|n| numbers_range.contains(&(*n as usize)) == false) + { + bail!( + "Numeros invalidos selecionados. Escolha valores entre {} e {}.", + numbers_range.start(), + numbers_range.end() + ); + } + + Ok(()) + } } #[derive(serde::Serialize, serde::Deserialize, Debug)] pub struct Premiacao { - pub descricao: String, - pub ganhadores: u32, - #[serde(rename = "valorPremio")] - pub premio: f32, + pub descricao: String, + pub ganhadores: u32, + #[serde(rename = "valorPremio")] + pub premio: f32, } impl std::fmt::Display for Premiacao { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{} ({}, {})", - self.descricao, - self.ganhadores, - util::formatar_dinheiro(&self.premio) - ) - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{} ({}, {})", + self.descricao, + self.ganhadores, + util::formatar_dinheiro(&self.premio) + ) + } } #[derive(Serialize, Deserialize, Debug)] pub struct Sorteio { - #[serde(rename = "loteria")] - pub jogo: Jogo, - #[serde(rename = "dezenas")] - #[serde(deserialize_with = "deserialize_u8_from_strings")] - #[serde(serialize_with = "serialize_u8_to_strings")] - pub numeros: Vec, - pub concurso: u32, - pub acumulou: bool, - #[serde(rename = "valorEstimadoProximoConcurso")] - pub valor_acumulado: f32, - pub premiacoes: Vec, - pub data: String, - #[serde(rename = "dataProximoConcurso")] - pub data_prox_sorteiro: String, + #[serde(rename = "loteria")] + pub jogo: Jogo, + #[serde(rename = "dezenas")] + #[serde(deserialize_with = "deserialize_u8_from_strings")] + #[serde(serialize_with = "serialize_u8_to_strings")] + pub numeros: Vec, + pub concurso: u32, + pub acumulou: bool, + #[serde(rename = "valorEstimadoProximoConcurso")] + pub valor_acumulado: f32, + pub premiacoes: Vec, + pub data: String, + #[serde(rename = "dataProximoConcurso")] + pub data_prox_sorteiro: String, } impl Sorteio { - pub fn premiacoes(&self) -> String { - let lista_premiacoes: Vec = self - .premiacoes - .iter() - .filter(|p| p.ganhadores > 0) - .map(|p| format!("{}", p)) - .collect(); - lista_premiacoes.join("; ") - } + pub fn premiacoes(&self) -> String { + let lista_premiacoes: Vec = self + .premiacoes + .iter() + .filter(|p| p.ganhadores > 0) + .map(|p| format!("{}", p)) + .collect(); + lista_premiacoes.join("; ") + } } -fn serialize_u8_to_strings(vec: &Vec, serializer: S) -> Result +fn serialize_u8_to_strings(vec: &[u8], serializer: S) -> Result where - S: Serializer, + S: Serializer, { - // Convert each u8 to a String (in base 10) - let strings: Vec = vec.iter().map(|&x| x.to_string()).collect(); + // Convert each u8 to a String (in base 10) + let strings: Vec = vec.iter().map(|&x| x.to_string()).collect(); - // Serialize the Vec - serializer.serialize_some(&strings) + // Serialize the Vec + serializer.serialize_some(&strings) } // Custom deserialization function fn deserialize_u8_from_strings<'de, D>(deserializer: D) -> Result, D::Error> where - D: Deserializer<'de>, + D: Deserializer<'de>, { - // First, we deserialize the field as Vec - let s: Vec = Vec::deserialize(deserializer)?; + // First, we deserialize the field as Vec + let s: Vec = Vec::deserialize(deserializer)?; - // Try to decode the strings into a Vec - let mut results = Vec::new(); - for value in s { - let number = value.parse::().map_err(serde::de::Error::custom)?; - results.push(number); - } + // Try to decode the strings into a Vec + let mut results = Vec::new(); + for value in s { + let number = value.parse::().map_err(serde::de::Error::custom)?; + results.push(number); + } - Ok(results) + Ok(results) } diff --git a/src/deprecated/file.rs b/src/deprecated/file.rs index afc094f..5d30eca 100644 --- a/src/deprecated/file.rs +++ b/src/deprecated/file.rs @@ -11,7 +11,7 @@ use std::{ time::{Duration, SystemTime}, }; -pub const APP_NAME: &'static str = "loto"; +pub const APP_NAME: &str = "loto"; pub fn save_as_json(sorteios: &Vec) -> Result<()> { let json_data = serde_json::to_string(sorteios)?; @@ -49,7 +49,7 @@ pub fn is_update_needed(jogo: &Jogo) -> bool { return true; } - return false; + false } fn was_file_recently_modified(filepath: &str) -> Result { diff --git a/src/file.rs b/src/file.rs index 2236719..378c6a3 100644 --- a/src/file.rs +++ b/src/file.rs @@ -8,7 +8,7 @@ use std::{ time::{Duration, SystemTime}, }; -pub const APP_NAME: &'static str = "loto"; +pub const APP_NAME: &str = "loto"; pub const UPDATE_HOURS_INTERVAL: u64 = 24; // USES SYSTEM TEMP FOLDER @@ -30,7 +30,7 @@ pub fn save_json(sorteios: &Vec) -> Result<()> { // On Windows: C:\Users\YourUser\AppData\Local\Temp\loto\.json // On Linux: /tmp/loto/.json pub fn load_json(jogo: &Jogo) -> Result> { - let filepath = get_filepath(&jogo)?; + let filepath = get_filepath(jogo)?; // Open the file let mut file = File::open(&filepath)?; @@ -71,14 +71,14 @@ pub fn get_filepath_sys_tmp(jogo: &Jogo) -> Result { } pub fn is_update_needed(jogo: &Jogo) -> Result { - let filepath = get_filepath(&jogo)?; + let filepath = get_filepath(jogo)?; let was_updated = was_file_recently_modified(&filepath).unwrap_or(false); if was_updated == false { return Ok(true); } - return Ok(false); + Ok(false) } fn was_file_recently_modified(filepath: &PathBuf) -> Result { diff --git a/src/main.rs b/src/main.rs index 53c4315..b030993 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,11 +2,12 @@ use loto::{cli_derive, commands, data::Jogo}; #[allow(dead_code)] fn tmp_test() { - let mut apostas: Vec<&[u8]> = Vec::new(); - apostas.push(&[01, 12, 34, 45, 50, 55]); - apostas.push(&[01, 13, 38, 40, 50, 57]); - apostas.push(&[01, 12, 34, 40, 50, 60]); - apostas.push(&[38, 52, 4, 45, 02, 55]); + let apostas: Vec<&[u8]> = vec![ + &[01, 12, 34, 45, 50, 55], + &[01, 13, 38, 40, 50, 57], + &[01, 12, 34, 40, 50, 60], + &[38, 52, 4, 45, 02, 55], + ]; _ = commands::consultar_numeros(&Jogo::Megasena, &apostas); } diff --git a/src/parsers.rs b/src/parsers.rs index 95d8fb8..ad4747c 100644 --- a/src/parsers.rs +++ b/src/parsers.rs @@ -2,41 +2,52 @@ use anyhow::{anyhow, bail, Result}; use std::io::{self, BufRead, BufReader}; pub fn vec_u8_from_str_ignore_err(input: &str) -> Vec { - input - .split(|c| c == ' ' || c == '-' || c == ',') - .map(|s| s.trim()) - .filter(|s| s.is_empty() == false) - .filter_map(|s| s.parse::().ok()) - .collect() + input + .split([' ', '-', ',']) + .map(|s| s.trim()) + .filter(|s| s.is_empty() == false) + .filter_map(|s| s.parse::().ok()) + .collect() } pub fn vec_u8_from_str(input: &str) -> Result> { - input - .split(|c| c == ' ' || c == '-' || c == ',') - .map(|s| s.trim()) - .filter(|s| s.is_empty() == false) - .map(|s| { - s.parse::() - .map_err(|_| anyhow!("Numero invalido '{}'", s)) - }) - .collect() + input + .split([' ', '-', ',']) + .map(|s| s.trim()) + .filter(|s| s.is_empty() == false) + .map(|s| { + s.parse::() + .map_err(|_| anyhow!("Numero invalido '{}'", s)) + }) + .collect() +} + +pub fn vstrings_to_vu8s(input: &Vec) -> Result>> { + let mut vecs_u8: Vec> = Vec::new(); + + for st in input { + let vu8 = vec_u8_from_str(st)?; + vecs_u8.push(vu8); + } + + Ok(vecs_u8) } pub fn vec_u8_from_buffer() -> Result>> { - let stdin = io::stdin(); - let reader = BufReader::new(stdin); - - let mut numbers: Vec> = Vec::new(); - - for (index, line) in reader.lines().enumerate() { - match line { - Ok(line_content) => { - let ns = vec_u8_from_str(&line_content)?; - numbers.push(ns); - } - Err(e) => bail!("Erro ao ler linha {}: {}", index, e), - } - } - - Ok(numbers) + let stdin = io::stdin(); + let reader = BufReader::new(stdin); + + let mut numbers: Vec> = Vec::new(); + + for (index, line) in reader.lines().enumerate() { + match line { + Ok(line_content) => { + let ns = vec_u8_from_str(&line_content)?; + numbers.push(ns); + } + Err(e) => bail!("Erro ao ler linha {}: {}", index, e), + } + } + + Ok(numbers) }