# 📝 Projeto: Gerenciador de Tarefas (To-Do CLI) > [!NOTE] Objetivo > Introduzir **Structs** para criar tipos de dados complexos, **Vectors (`Vec<T>`)** para armazenar listas dinâmicas e o conceito básico de **Ownership & Borrowing** (referências mutáveis) no [[Linguagem Rust|Rust]]. --- ## 🛠️ 1. Contexto e Desafio No desafio [[Construção de um Conversor de Escalas Termométricas em Rust]], o dado entrava, era processado e descartado. Mas a maioria dos programas precisa manter um "estado" (memória). **O Desafio:** Criar uma ferramenta de linha de comando onde o usuário possa adicionar tarefas, listar o que precisa fazer e marcar itens como concluídos. O programa deve manter essa lista na memória enquanto estiver rodando. --- ## 📋 2. Requisitos do Projeto ### Funcionais (O que o programa faz) Aqui está uma lita dos requisitos funcionais do programa. A implementação deve cumprir estes requisitos. Entretanto, a apresentação das informações no terminal é a critério do desenvolvedor. 1. **Adicionar Tarefa:** O usuário digita uma descrição (ex: "Comprar leite") e ela é salva. 2. **Listar Tarefas:** O programa mostra todas as tarefas com um ID (índice), descrição e status (Pendente ou Concluída). - Ex: `[ ] 0: Comprar leite` ou `[X] 1: Estudar Rust`. 3. **Concluir Tarefa:** O usuário digita o ID da tarefa e o status dela muda para "Concluída". 4. **Sair:** Encerra o programa. ### Técnicos (Como o Rust deve ser usado) A seguir são apresentados os requisitos técnicos da implementação do programa em [[Linguagem Rust|Rust]]. - **Structs:** Crie uma `struct Task` que agrupe a `descricao` (String) e o `status`. - **Enums:** Use um `enum Status` (Pendente, Concluido) dentro da Struct. - **Vectors:** Use um `Vec<Task>` na função `main` para armazenar a lista. - **Iteração:** Use um loop `for` para percorrer o vetor na hora de listar. - **Mutabilidade:** Você precisará acessar o vetor de forma mutável (`&mut`) para alterar o status de uma tarefa existente. --- ## 🧩 3. Estrutura Sugerida (Esqueleto) Abaixo está apenas a definição dos tipos e a estrutura da `main` para você preencher. Isso ajuda a começar sem dar a resposta pronta. ```rust use std::io::{self, Write}; // 1. Defina o Enum para o estado da tarefa #[derive(Debug)] // Adicione outros derives se necessário enum Status { // TODO: Defina as variantes (ex: Pendente, Concluido) } // 2. Defina a Struct que representa uma tarefa completa struct Task { // TODO: Defina os campos (descricao: String, status: Status) } impl Task { // Dica: Uma função construtora ajuda a criar novas tarefas limpas fn new(descricao: String) -> Task { // TODO: Retornar uma nova Task com status Pendente todo!() } } fn main() { // O Vetor que "segura" a memória do programa let mut tarefas: Vec<Task> = Vec::new(); println!("--- Gerenciador de Tarefas Rust ---"); loop { println!("\n1. Adicionar | 2. Listar | 3. Concluir | 4. Sair"); print!("Escolha: "); io::stdout().flush().unwrap(); // Use a lógica de input que você já aprendeu... let mut input = String::new(); io::stdin().read_line(&mut input).ok(); match input.trim() { "1" => { // TODO: Pedir o nome da tarefa, criar a Task e dar .push() no vetor }, "2" => { // TODO: Iterar sobre o vetor e imprimir (Dica: use .iter().enumerate()) }, "3" => { // TODO: Pedir o índice (número), acessar o vetor e mudar o status // Cuidado: Verifique se o índice existe para não dar crash! }, "4" => break, _ => println!("Opção inválida"), } } } ``` --- ## 🧠 4. Dicas para o Sucesso 1. **Vetores (`Vec`):** - Para adicionar: `vetor.push(item)`. - Para acessar: `vetor[0]` (mas cuidado com índices inválidos). - Melhor forma de acessar com segurança: `vetor.get_mut(indice)`. 2. **Formatando a Saída:** - Dentro do `impl Task`, você pode criar um método `fn formatar(&self) -> String` que retorna algo bonitinho como `"[X] Estudar"` para simplificar o `println!` na `main`. 3. **Lendo Strins:** - Lembre-se que `read_line` inclui o "Enter" (`\n`) no final. O `.trim()` é seu melhor amigo para limpar o texto da descrição da tarefa. --- # ✅ Solução Abaixo segue uma possível solução para o problema em questão. ```rust use std::io::{self, Write}; use std::fmt; /// Representa o estado atual de uma tarefa no sistema. #[derive(Debug, Clone, PartialEq)] enum Status { Pendente, Concluido, } /// Implementação da trait Display para formatar a exibição do Status. impl fmt::Display for Status { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let label = match self { Status::Pendente => "Pendente", Status::Concluido => "Concluído", }; write!(f, "{}", label) } } /// Estrutura principal que define uma Tarefa. /// Contém uma descrição textual e um status. struct Task { descricao: String, status: Status, } impl Task { /// Cria uma nova instância de `Task`. /// Por padrão, toda tarefa nasce com o status `Pendente`. fn new(descricao: String) -> Self { Self { descricao, status: Status::Pendente, } } /// Altera o status da tarefa para `Concluido`. fn concluir(&mut self) { self.status = Status::Concluido; } } /// Implementação da trait Display para facilitar o uso em macros de print. impl fmt::Display for Task { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "[{}] - {}", self.status, self.descricao) } } fn main() { let mut tarefas: Vec<Task> = Vec::new(); println!("--- Gerenciador de Tarefas Rust ---"); loop { println!("\n1. Adicionar | 2. Listar | 3. Concluir | 4. Sair"); let escolha = prompt("Escolha: "); match escolha.trim() { "1" => { let desc = prompt("Digite a descrição: "); if !desc.trim().is_empty() { tarefas.push(Task::new(desc.trim().to_string())); println!("✔ Tarefa adicionada!"); } }, "2" => { if tarefas.is_empty() { println!("ℹ Nenhuma tarefa cadastrada."); } else { println!("\n--- Lista de Tarefas ---"); for (i, tarefa) in tarefas.iter().enumerate() { println!("{}: {}", i, tarefa); } } }, "3" => { // Imprime a lista de tarefas para o usuário escolher qual concluir for (i, tarefa) in tarefas.iter().enumerate() { println!("{}: {}", i, tarefa); } let indice_str = prompt("Índice da tarefa: "); // Tratamento de erro seguro para conversão de string para número if let Ok(indice) = indice_str.trim().parse::<usize>() { if let Some(tarefa) = tarefas.get_mut(indice) { tarefa.concluir(); println!("✔ Tarefa {} marcada como concluída!", indice); } else { println!("⚠ Erro: Índice não encontrado."); } } else { println!("⚠ Erro: Digite um número válido."); } }, "4" => { println!("Saindo..."); break; }, _ => println!("⚠ Opção inválida!"), } } } /// Função auxiliar para capturar entrada do teclado de forma limpa. /// Reduz a repetição de `flush()` e `read_line()`. fn prompt(mensagem: &str) -> String { print!("{}", mensagem); io::stdout().flush().expect("Falha ao limpar o buffer de saída"); let mut input = String::new(); io::stdin() .read_line(&mut input) .expect("Falha ao ler a entrada"); input } ```