# 2. Estruturas Condicionais

### **📋 ÍNDICE**

1. Filosofia de Tratamento de Erros em Elixir
2. Os Três Mecanismos de Erro
3. Errors (Exceções) e Rescue
4. Throws e Catch
5. Exits (Saídas de Processo)
6. After e Else - Limpeza e Fallback
7. Reraise - Relançando Exceções
8. Escopo de Variáveis
9. Boas Práticas e Padrões
10. Resumo Rápido

***

### 🎯 **1. FILOSOFIA DE TRATAMENTO DE ERROS EM ELIXIR**

#### **1.1 A Abordagem "Let It Crash" (Deixe Quebrar)**

```yaml
FILOSOFIA FUNDAMENTAL:
  Diferente de linguagens como Java, Python ou Ruby, Elixir (herdando do Erlang)
  adota a filosofia "Let it crash" (Deixe quebrar). Isso significa que, em vez de
  tentar recuperar cada erro possível, você frequentemente permite que o processo
  falhe e o supervisor o reinicie em um estado limpo.

POR QUÊ?
  - Processos em Elixir são leves e isolados
  - Um processo pode falhar sem afetar outros
  - Supervisores monitoram e reiniciam processos
  - Código fica mais limpo sem blocos try/catch aninhados

QUANDO USAR TRY/RESCUE:
  - Interação com bibliotecas externas (não Elixir)
  - Operações que não têm versão "bang" (com !)
  - Logging e monitoramento
  - Fallback para APIs de terceiros
```

#### **1.2 Comparação com Outras Linguagens**

| Característica                      | Elixir            | Ruby        | Java               | Python  |
| ----------------------------------- | ----------------- | ----------- | ------------------ | ------- |
| **Filosofia**                       | "Let it crash"    | Rescue tudo | Checked exceptions | EAFP    |
| **Exceções como controle de fluxo** | ❌ Desencorajado   | ✅ Comum     | ❌ Apenas exceções  | ✅ Comum |
| **Exceções em funções padrão**      | Versão `!` (bang) | Sim         | Sim                | Sim     |
| **Pattern matching em erros**       | ✅ Sim (case)      | ❌ Não       | ❌ Não              | ❌ Não   |
| **Supervisores**                    | ✅ Sim             | ❌ Não       | ❌ Não              | ❌ Não   |

#### **1.3 O Padrão de Duas Versões (foo / foo!)**

```elixir
# Elixir frequentemente fornece DUAS versões de funções que podem falhar:

# 1. Versão segura: retorna {:ok, resultado} ou {:error, reason}
case File.read("config.txt") do
  {:ok, conteudo} -> IO.puts("Arquivo lido: #{conteudo}")
  {:error, reason} -> IO.puts("Erro: #{reason}")
end

# 2. Versão "bang" (!): levanta exceção se falhar
try do
  conteudo = File.read!("config.txt")
  IO.puts("Arquivo lido: #{conteudo}")
rescue
  e in File.Error -> IO.puts("Erro fatal: #{e.message}")
end

# A versão "bang" é usada quando você TEM CERTEZA que o arquivo existe
# e a falha seria um erro REAL (não um caso esperado)
```

***

### ⚠️ **2. OS TRÊS MECANISMOS DE ERRO**

#### **2.1 Visão Geral**

```yaml
ELIXIR POSSUI TRÊS MECANISMOS DISTINTOS PARA SITUAÇÕES DIFERENTES:

  ERRORS (EXCEÇÕES):
    - Uso: Situações excepcionais e inesperadas
    - Mecanismo: `raise` / `rescue`
    - Exemplo: ArgumentError, ArithmeticError, File.Error
    - Quando usar: Algo que não deveria acontecer aconteceu

  THROWS:
    - Uso: Controle de fluxo não-local (como early exit)
    - Mecanismo: `throw` / `catch`
    - Exemplo: Interromper uma deep recursion
    - Quando usar: Raramente, geralmente com bibliotecas externas

  EXITS:
    - Uso: Sinalização de morte de processo
    - Mecanismo: `exit` (não costuma ser "catch")
    - Exemplo: Processo morreu, supervisor vai reiniciar
    - Quando usar: Quase nunca diretamente
```

#### **2.2 Tabela Comparativa**

| Característica           | raise/rescue                    | throw/catch        | exit                 |
| ------------------------ | ------------------------------- | ------------------ | -------------------- |
| **Uso típico**           | Erros inesperados               | Controle de fluxo  | Morte de processo    |
| **Frequência**           | Rara (usar versões `!`)         | Muito rara         | Quase nunca          |
| **Parte da API pública** | Sim (versões `!`)               | Não                | Não                  |
| **Pega em `try/catch`**  | Com `rescue`                    | Sim (padrão)       | Sim (com padrão)     |
| **Linka processos**      | Não                             | Não                | Sim                  |
| **Recomendado para**     | Bibliotecas, casos excepcionais | Interop com Erlang | Não usar diretamente |

***

### 🔴 **3. ERRORS (EXCEÇÕES) E RESCUE**

#### **3.1 Levantando Exceções (raise)**

```elixir
# 1. Exceção mais simples (RuntimeError)
raise "Algo deu errado!"
# ** (RuntimeError) Algo deu errado!

# 2. Exceção específica com mensagem
raise ArgumentError, message: "Argumento inválido: esperado inteiro"
# ** (ArgumentError) Argumento inválido: esperado inteiro

# 3. Re-levantando exceção existente
try do
  File.read!("inexistente.txt")
rescue
  e in File.Error -> reraise e, __STACKTRACE__
end

# 4. Exceção personalizada
defmodule MeuErro do
  defexception message: "Erro padrão"
end

raise MeuErro
# ** (MeuErro) Erro padrão

raise MeuErro, message: "Mensagem personalizada"
# ** (MeuErro) Mensagem personalizada
```

#### **3.2 Exceções Comuns no Elixir**

```elixir
# ArithmeticError - erro aritmético
try do
  1 / 0
rescue
  e in ArithmeticError -> IO.puts("Erro aritmético: #{e.message}")
end

# ArgumentError - argumento inválido
try do
  String.to_integer("abc")
rescue
  e in ArgumentError -> IO.puts("Argumento inválido")
end

# CaseClauseError - case sem cláusula correspondente
try do
  case :ok do
    :error -> "não vai casar"
  end
rescue
  e in CaseClauseError -> IO.puts("Case sem match: #{e.term}")
end

# FunctionClauseError - função sem cláusula
defmodule Math do
  def zero?(0), do: true
end

try do
  Math.zero?("não é número")
rescue
  e in FunctionClauseError -> IO.puts("Nenhuma cláusula casou")
end

# File.Error - erro de arquivo
try do
  File.read!("/nao/existe.txt")
rescue
  e in File.Error -> IO.puts("Erro de arquivo: #{e.reason}")
end

# KeyError - chave não encontrada
try do
  %{a: 1}.b
rescue
  e in KeyError -> IO.puts("Chave #{e.key} não encontrada")
end
```

#### **3.3 Resgatando Exceções (rescue)**

```elixir
# 1. Resgatando qualquer exceção (CUIDADO!)
try do
  raise "Erro"
rescue
  _ -> "Algo deu errado"  # Pega TUDO
end

# 2. Resgatando tipos específicos
try do
  File.read!("inexistente.txt")
rescue
  e in File.Error -> "Erro de arquivo: #{e.message}"
  e in ArgumentError -> "Erro de argumento"
end

# 3. Resgatando múltiplos tipos de uma vez
try do
  # código que pode levantar vários erros
rescue
  e in [File.Error, ArgumentError] -> "Erro de arquivo ou argumento"
end

# 4. Acessando o erro sem variável
try do
  raise "Erro"
rescue
  RuntimeError -> "Um RuntimeError ocorreu"
end

# 5. Pattern matching na mensagem de erro
try do
  raise ArgumentError, message: "valor muito alto"
rescue
  %ArgumentError{message: "valor muito alto"} -> "Valor excedeu limite"
  %ArgumentError{} -> "Outro erro de argumento"
end
```

#### **3.4 Definindo Exceções Personalizadas**

```elixir
# 1. Exceção simples com valor padrão
defmodule ApiError do
  defexception message: "Erro na API"
end

# 2. Exceção com campos adicionais
defmodule ValidationError do
  defexception [:field, :message, :value]
  
  # Personalizando mensagem
  def exception(%{field: field, value: value}) do
    %ValidationError{
      field: field,
      value: value,
      message: "Campo #{field} com valor inválido: #{inspect(value)}"
    }
  end
end

# Usando
try do
  raise ValidationError, field: :email, value: "invalido"
rescue
  e in ValidationError -> IO.puts(e.message)
end

# 3. Exceção com comportamento personalizado
defmodule NegocioError do
  defexception [:codigo, :message]
  
  def exception(opts) do
    codigo = Keyword.get(opts, :codigo, 500)
    mensagem_padrao = case codigo do
      400 -> "Requisição inválida"
      401 -> "Não autorizado"
      404 -> "Não encontrado"
      _ -> "Erro interno"
    end
    
    %NegocioError{
      codigo: codigo,
      message: Keyword.get(opts, :message, mensagem_padrao)
    }
  end
end

# Usando
raise NegocioError, codigo: 404
# ** (NegocioError) Não encontrado

raise NegocioError, codigo: 400, message: "Email já cadastrado"
# ** (NegocioError) Email já cadastrado
```

***

### 🎲 **4. THROWS E CATCH**

#### **4.1 Quando Usar Throw/Catch**

```yaml
THROW/CATCH NÃO SÃO PARA ERROS!

Use throw/catch APENAS quando:
  - Você precisa interromper uma execução profunda (early exit)
  - Está interagindo com código Erlang que usa throw
  - Implementando controle de fluxo não local (MUITO raro)

NÃO USE throw/catch para:
  - Erros de validação (use tuples {:ok, result} ou raise)
  - Controle de fluxo normal (use case, cond, if)
  - O que você faria com exceptions em outras linguagens
```

#### **4.2 Sintaxe e Exemplos**

```elixir
# 1. Throw básico
try do
  throw(:valor)
catch
  :valor -> "Capturado!"
end
# "Capturado!"

# 2. Throw com valor
try do
  throw({:error, "mensagem"})
catch
  {:error, msg} -> "Erro: #{msg}"
end
# "Erro: mensagem"

# 3. Exemplo prático - early exit em recursão
defmodule Buscador do
  def encontrar(lista, alvo) do
    try do
      do_encontrar(lista, alvo)
      :nao_encontrado
    catch
      ^alvo -> :encontrado
    end
  end
  
  defp do_encontrar([head | _], alvo) when head == alvo do
    throw(alvo)
  end
  
  defp do_encontrar([_ | tail], alvo) do
    do_encontrar(tail, alvo)
  end
  
  defp do_encontrar([], _), do: nil
end

Buscador.encontrar([1, 2, 3, 4, 5], 3)  # :encontrado
Buscador.encontrar([1, 2, 3, 4, 5], 10) # :nao_encontrado
```

#### **4.3 Throw vs Erros vs Tuplas**

```elixir
# ❌ RUIM - Usando throw como erro
def dividir(a, b) do
  if b == 0 do
    throw({:error, "divisão por zero"})
  else
    a / b
  end
end

try do
  dividir(10, 0)
catch
  {:error, msg} -> "Erro: #{msg}"
end

# ✅ BOM - Usando tuple para casos esperados
def dividir_seguro(a, b) do
  if b == 0 do
    {:error, "divisão por zero"}
  else
    {:ok, a / b}
  end
end

case dividir_seguro(10, 0) do
  {:ok, resultado} -> "Resultado: #{resultado}"
  {:error, msg} -> "Erro: #{msg}"
end

# ✅ BOM - Usando raise para casos excepcionais
def dividir!(a, b) do
  if b == 0 do
    raise ArgumentError, "divisão por zero"
  else
    a / b
  end
end

try do
  dividir!(10, 0)
rescue
  e in ArgumentError -> "Erro: #{e.message}"
end
```

***

### 💀 **5. EXITS (SAÍDAS DE PROCESSO)**

#### **5.1 O que são Exits?**

```yaml
DEFINIÇÃO:
  Exits são sinais enviados quando um processo morre. Eles são fundamentais
  para o sistema de tolerância a falhas do Elixir/Erlang.

CARACTERÍSTICAS:
  - São enviados AUTOMATICAMENTE quando um processo termina
  - Podem ser enviados MANUALMENTE com `exit/1`
  - Processos linkados recebem o sinal de exit
  - Supervisores usam exits para reiniciar processos

QUASE NUNCA se deve capturar exits diretamente!
```

#### **5.2 Exits na Prática**

```elixir
# 1. Processo termina normalmente (exit normal)
spawn(fn -> :ok end)  # Processo termina, exit normal

# 2. Processo termina com erro
spawn(fn -> raise "Erro" end)  # Exit com razão do erro

# 3. Enviando exit manualmente (RARO)
pid = spawn(fn -> receive do: (msg -> IO.puts(msg)) end)
Process.exit(pid, :kill)  # Mata o processo
Process.exit(pid, :something_went_wrong)  # Exit com razão customizada

# 4. Linkando processos e capturando exits (APENAS EDUCACIONAL)
parent = self()
child = spawn_link(fn ->
  receive do
    :crash -> raise "Falha"
  end
end)

# Isso vai causar o processo PAI também a morrer!
# send(child, :crash)

# 5. Trap exits (HABILITAR PARA SUPERVISÃO)
Process.flag(:trap_exit, true)
spawn_link(fn -> exit(:boom) end)

receive do
  {:EXIT, pid, reason} -> IO.puts("Processo #{inspect(pid)} morreu: #{reason}")
end
```

#### **5.3 Níveis de Exit**

| Razão              | Descrição                               | Uso                             |
| ------------------ | --------------------------------------- | ------------------------------- |
| `:normal`          | Processo terminou normalmente           | Fim natural da execução         |
| `:kill`            | Matança brutal (não pode ser capturada) | Forçar término imediato         |
| `:shutdown`        | Desligamento controlado                 | Supervisores fechando processos |
| `{:error, reason}` | Erro específico                         | Biblioteca comunicando falha    |
| Qualquer termo     | Razão customizada                       | Comunicação entre processos     |

***

### 🧹 **6. AFTER E ELSE - LIMPEZA E FALLBACK**

#### **6.1 After - Garantindo Limpeza**

```elixir
# 1. After básico (sempre executado)
{:ok, file} = File.open("test.txt", [:write])
try do
  IO.write(file, "conteúdo")
  raise "Ops"
after
  File.close(file)  # Será executado mesmo com erro!
end

# 2. After sem try (em função)
defmodule Limpeza do
  def processar_arquivo(caminho) do
    {:ok, file} = File.open(caminho, [:read])
    # Se houver erro, mesmo assim fecha
    dados = File.read(file)
  after
    File.close(file)
    dados  # Retorna o valor
  end
end

# 3. After com múltiplas ações
try do
  # código
after
  IO.puts("1. Sempre executado")
  IO.puts("2. Mesmo com erro")
  IO.puts("3. Ordem garantida")
end

# 4. After não altera o valor de retorno
resultado = try do
  1 + 1
after
  IO.puts("Limpeza")
end
# resultado = 2 (after não interfere)
```

#### **6.2 Else - Pattern Matching no Sucesso**

```elixir
# 1. Else básico - só executa se NÃO houver erro
x = 2
try do
  10 / x
rescue
  ArithmeticError -> :infinito
else
  y when y < 1 and y > -1 -> :pequeno
  y when y > 10 -> :grande
  y -> y
end
# Se x = 2, resultado é 5.0 ("caiu no else padrão")
# Se x = 0, cai no rescue e retorna :infinito

# 2. Else com pattern matching
case_insensitive = false

resultado = try do
  if case_insensitive do
    "HELLO"
  else
    raise "Case sensitive habilitado"
  end
rescue
  _ -> "Erro: case sensitive"
else
  "HELLO" -> "Maiúsculas detectadas"
  "hello" -> "Minúsculas detectadas"
  other -> "Outro formato: #{other}"
end

# 3. Else útil para transformar sucessos
dados = try do
  File.read!("config.json")
  # Se leu, processa o JSON
else
  conteudo when is_binary(conteudo) ->
    Jason.decode!(conteudo)
end
```

#### **6.3 Combinação After + Else + Rescue**

```elixir
defmodule ProcessadorArquivo do
  def processar(caminho) do
    {:ok, file} = File.open(caminho, [:read, :write])
    
    try do
      # Tenta ler e processar
      conteudo = IO.read(file, :all)
      
      if String.trim(conteudo) == "" do
        raise "Arquivo vazio"
      end
      
      processar_conteudo(conteudo)
      
    rescue
      e in File.Error ->
        {:error, "Erro de arquivo: #{e.reason}"}
      e ->
        {:error, "Erro inesperado: #{e.message}"}
        
    else
      {:ok, resultado} ->
        IO.puts("Processamento bem-sucedido")
        {:ok, resultado}
        
      {:error, reason} when is_binary(reason) ->
        IO.puts("Erro de negócio: #{reason}")
        {:error, reason}
        
      other ->
        {:ok, other}
        
    after
      # Garantir que o arquivo seja fechado
      File.close(file)
      IO.puts("Arquivo fechado")
    end
  end
  
  defp processar_conteudo(conteudo) do
    # Processamento real
    {:ok, String.upcase(conteudo)}
  end
end
```

***

### 🔄 **7. RERAISE - RELANÇANDO EXCEÇÕES**

#### **7.1 Por que Reraise?**

```yaml
USO TÍPICO DE RERAISE:
  1. Você quer LOGAR o erro
  2. Você quer ADICIONAR contexto
  3. Você quer transformar a exceção
  4. Você NÃO quer engolir o erro (fail fast)

Quando NÃO usar:
  - Se você pode tratar o erro
  - Se você tem um fallback
  - Em código de biblioteca (use tuplas)
```

#### **7.2 Usando Reraise**

```elixir
# 1. Reraise básico (preserva stack trace)
try do
  File.read!("inexistente.txt")
rescue
  e ->
    IO.puts("Erro capturado: #{e.message}")
    reraise e, __STACKTRACE__
end

# 2. Logging + Reraise
require Logger

defmodule ServicoExternal do
  def chamar_api(url) do
    try do
      HTTPoison.get!(url)
    rescue
      e in HTTPoison.Error ->
        Logger.error("Falha na API #{url}: #{e.message}")
        reraise e, __STACKTRACE__
    end
  end
end

# 3. Transformando exceção
defmodule ApiWrapper do
  def chamar(url) do
    try do
      HTTPoison.get!(url)
    rescue
      e in HTTPoison.Error ->
        # Transforma em erro de negócio
        raise ApiError, 
          message: "Falha na comunicação com API externa",
          original: e
    end
  end
end

# 4. Adicionando contexto ao erro
defmodule BancoDados do
  def query(sql) do
    try do
      DB.query!(sql)
    rescue
      e in DB.Error ->
        reraise DB.Error, 
          message: "#{e.message} (SQL: #{sql})", 
          __STACKTRACE__
    end
  end
end

# 5. Reraise condicional
try do
  operacao_risco()
rescue
  e in RuntimeError ->
    if String.contains?(e.message, "timeout") do
      # Timeout podemos tentar recuperar
      {:error, :timeout}
    else
      # Outros erros: reraise
      reraise e, __STACKTRACE__
    end
end
```

#### **7.3 Importância do Stacktrace**

```elixir
# ❌ RUIM - Perde o stack trace original
try do
  function_que_falha()
rescue
  e -> 
    IO.puts("Erro: #{e.message}")
    raise "Erro transformado"  # Perde a origem!
end

# ✅ BOM - Preserva usando reraise
try do
  function_que_falha()
rescue
  e -> 
    IO.puts("Erro original: #{e.message}")
    reraise e, __STACKTRACE__  # Preserva a causa raiz
end

# ✅ BOM - Wrap com contexto
try do
  function_que_falha()
rescue
  e in File.Error ->
    reraise File.Error, 
      message: "Falha ao processar arquivo de config: #{e.message}",
      __STACKTRACE__
end
```

***

### 📦 **8. ESCOPO DE VARIÁVEIS**

#### **8.1 Comportamento do Escopo**

```elixir
# ❌ INVÁLIDO - Variáveis do try não vazam
try do
  x = 10
  raise "erro"
rescue
  _ -> IO.puts(x)  # ERRO: x não existe aqui!
end

# ✅ CORRETO - Atribuir o resultado do try
resultado = try do
  x = 10
  x + 5
rescue
  _ -> 0
end
IO.puts(resultado)  # 15

# ✅ CORRETO - Usar o valor retornado
x = try do
  42
rescue
  _ -> 0
end
IO.puts(x)  # 42

# ⚠️ CUIDADO - Variáveis no else também não vazam
y = try do
  :ok
else
  :ok -> variavel = "dentro do else"
  _ -> "outro"
end
# IO.puts(variavel)  # ERRO! variavel não existe aqui
```

#### **8.2 Padrão Comum: Atribuição Segura**

```elixir
# Padrão para tentar algo com fallback
valor = try do
  File.read!("config.json")
  # Se falhar, rescue define fallback
rescue
  _ -> "{}"  # Fallback para JSON vazio
end

# Padrão para transformar resultado
resultado = try do
  {:ok, conteudo} = File.read("config.txt")
  processar(conteudo)
rescue
  e -> {:error, e.message}
end

# Padrão com múltiplos pontos de saída
defmodule Config do
  def carregar(caminho) do
    try do
      case File.read(caminho) do
        {:ok, conteudo} -> 
          {:ok, Jason.decode!(conteudo)}
        {:error, reason} -> 
          raise "Erro ao ler: #{reason}"
      end
    rescue
      e in Jason.DecodeError -> 
        {:error, "JSON inválido: #{e.message}"}
      e -> 
        {:error, "Falha geral: #{e.message}"}
    end
  end
end
```

***

### 📚 **9. BOAS PRÁTICAS E PADRÕES**

#### **9.1 Quando Usar Cada Mecanismo**

```yaml
✅ USE raise/rescue para:
  - Condições verdadeiramente excepcionais
  - Falhas de bibliotecas externas
  - Erros de programação (argumentos inválidos)
  - Quando você quer "fail fast"

✅ USE tuplas {:ok, result} / {:error, reason} para:
  - Casos de erro ESPERADOS (ex: arquivo não encontrado)
  - Validações de negócio
  - API pública de bibliotecas
  - Controle de fluxo normal

❌ EVITE throw/catch para:
  - Tratamento de erros (use tuples ou raise)
  - Controle de fluxo normal
  - A menos que interagindo com Erlang

❌ EVITE exit/rescue para:
  - Tratamento de erros normais
  - Use apenas em supervisores
```

#### **9.2 Padrões Recomendados**

```elixir
# 1. Padrão para funções que PODEM falhar (retornam tuple)
def buscar_usuario(id) do
  case Repo.get(User, id) do
    nil -> {:error, :not_found}
    user -> {:ok, user}
  end
end

# 2. Padrão para funções que NÃO DEVEM falhar (versão bang)
def buscar_usuario!(id) do
  case Repo.get(User, id) do
    nil -> raise "Usuário #{id} não encontrado"
    user -> user
  end
end

# 3. Padrão with para encadeamento de operações que podem falhar
def processar_pagamento(usuario_id, valor) do
  with {:ok, usuario} <- buscar_usuario(usuario_id),
       {:ok, conta} <- buscar_conta(usuario),
       {:ok, transacao} <- debitar(conta, valor) do
    {:ok, transacao}
  else
    {:error, :not_found} -> {:error, "Usuário ou conta não encontrado"}
    {:error, :saldo_insuficiente} -> {:error, "Saldo insuficiente"}
    error -> error
  end
end

# 4. Padrão para APIs de terceiros (com logging e reraise)
def chamar_api_externa(url) do
  try do
    HTTPoison.get!(url)
  rescue
    e in HTTPoison.Error ->
      Logger.error("API falhou: #{e.message}")
      reraise e, __STACKTRACE__
  end
end

# 5. Padrão para fallback com timeout
def consultar_com_timeout(funcao, timeout_ms \\ 5000) do
  task = Task.async(funcao)
  
  case Task.yield(task, timeout_ms) || Task.shutdown(task) do
    {:ok, resultado} -> {:ok, resultado}
    nil -> {:error, :timeout}
  end
end
```

#### **9.3 Anti-padrões a Evitar**

```elixir
# ❌ ANTI-PADRÃO: Usar raise para controle de fluxo
def buscar_item(lista, id) do
  case Enum.find(lista, fn item -> item.id == id end) do
    nil -> raise "Item não encontrado"  # RUIM!
    item -> item
  end
end

# ✅ CORRETO: Retornar tuple ou nil
def buscar_item(lista, id) do
  Enum.find(lista, fn item -> item.id == id end)
end

# ❌ ANTI-PADRÃO: Engolir todas as exceções
try do
  codigo_arriscado()
rescue
  _ -> nil  # Perde informação importante!
end

# ✅ CORRETO: Tratar casos específicos ou logar
try do
  codigo_arriscado()
rescue
  e in ExpectedError -> {:error, e.message}
  e -> 
    Logger.error("Erro inesperado: #{e.message}")
    reraise e, __STACKTRACE__
end

# ❌ ANTI-PADRÃO: Usar rescue para validar entrada
def soma_ou_zero(valor) do
  try do
    valor + 5
  rescue
    _ -> 0
  end
end

# ✅ CORRETO: Validar antes
def soma_ou_zero(valor) when is_number(valor) do
  valor + 5
end
def soma_ou_zero(_), do: 0
```

***

### 📝 **10. RESUMO RÁPIDO**

#### **10.1 Tabela de Decisão**

| Situação                        | O que usar                              | Exemplo                    |
| ------------------------------- | --------------------------------------- | -------------------------- |
| **Resultado esperado**          | Tupla `{:ok, value} / {:error, reason}` | `File.read/1`              |
| **Caso que não deve falhar**    | Versão `!` (bang)                       | `File.read!/1`             |
| **Erro de programação**         | `raise`                                 | Argumento inválido         |
| **Recurso precisa ser fechado** | `after`                                 | Fechar arquivo, conexão DB |
| **Fallback em caso de erro**    | `rescue` + else                         | Timeout, API externa       |
| **Logging sem engolir erro**    | `reraise`                               | Monitoramento              |

#### **10.2 Checklist de Boas Práticas**

```yaml
✅ RECOMENDAÇÕES:
  - Prefira tuplas `{:ok, result}` para controle de fluxo normal
  - Use versões `!` (bang) quando a falha é um erro real
  - Evite `rescue` em código interno (prefira pattern matching)
  - Use `after` para garantir limpeza de recursos
  - Preserve stack trace com `reraise` ao relançar
  - Log erros antes de reraise
  - Defina exceções personalizadas com contextos ricos
  - Use `with` para encadear operações que podem falhar

❌ EVITE:
  - Usar `raise` para controle de fluxo
  - Engolir todas as exceções com `rescue _`
  - Usar `throw/catch` em código novo
  - Ignorar stack trace ao relançar
  - Resgatar exceções que você não pode tratar
```

#### **10.3 Exemplo Integrado Completo**

```elixir
defmodule ProcessadorPagamentos do
  require Logger
  
  # API pública (usando tuplas para casos esperados)
  def processar(pagamento) do
    with {:ok, usuario} <- validar_usuario(pagamento.usuario_id),
         {:ok, cartao} <- validar_cartao(pagamento.cartao_token),
         {:ok, autorizacao} <- autorizar_pagamento(cartao, pagamento.valor),
         {:ok, transacao} <- registrar_transacao(usuario, autorizacao) do
      {:ok, transacao}
    else
      {:error, :usuario_invalido} -> {:error, "Usuário não encontrado"}
      {:error, :cartao_invalido} -> {:error, "Cartão recusado"}
      {:error, :saldo_insuficiente} -> {:error, "Saldo insuficiente"}
      error -> error
    end
  end
  
  # Versão bang (para uso interno ou quando se espera sucesso)
  def processar!(pagamento) do
    case processar(pagamento) do
      {:ok, resultado} -> resultado
      {:error, reason} -> raise "Falha no pagamento: #{reason}"
    end
  end
  
  # Função com fallback e logging
  def tentar_processar(pagamento) do
    try do
      processar!(pagamento)
    rescue
      e in RuntimeError when byte_size(e.message) > 0 ->
        Logger.error("Erro ao processar pagamento: #{e.message}")
        {:error, :falha_processamento}
    else
      resultado -> {:ok, resultado}
    after
      Logger.info("Processamento finalizado para usuário #{pagamento.usuario_id}")
    end
  end
  
  # Implementações privadas (exemplo)
  defp validar_usuario(id), do: {:ok, %{id: id, nome: "João"}}
  defp validar_cartao(token), do: {:ok, %{token: token, limite: 1000}}
  defp autorizar_pagamento(cartao, valor) when valor <= cartao.limite, do: {:ok, %{autorizado: true}}
  defp autorizar_pagamento(_, _), do: {:error, :saldo_insuficiente}
  defp registrar_transacao(usuario, autorizacao), do: {:ok, %{id: 123, valor: autorizacao}}
end

# Uso da API
case ProcessadorPagamentos.processar(%{usuario_id: 1, cartao_token: "tok_123", valor: 50}) do
  {:ok, transacao} -> IO.puts("Pagamento aprovado: #{transacao.id}")
  {:error, reason} -> IO.puts("Erro: #{reason}")
end
```

***

### 📚 **REFERÊNCIAS**

```yaml
DOCUMENTAÇÃO OFICIAL:
  - Try, catch, rescue: https://hexdocs.pm/elixir/Kernel.html#try/1
  - raise: https://hexdocs.pm/elixir/Kernel.html#raise/1
  - reraise: https://hexdocs.pm/elixir/Kernel.html#reraise/2
  - defexception: https://hexdocs.pm/elixir/Kernel.html#defexception/1

LEITURA RECOMENDADA:
  - "Elixir in Action" (Sasa Juric) - Capítulo sobre erros
  - "The Little Elixir & OTP Guidebook"
  - "Erlang and Elixir for Imperative Programmers"

PADRÕES AVANÇADOS:
  - "Fail fast" / "Let it crash" philosophy
  - Supervisor trees and error recovery
  - Railway oriented programming with `with`
```

***

**🔐 Lembre-se: Em Elixir, a filosofia "Let it crash" não é negligência - é uma estratégia consciente de construção de sistemas robustos onde processos falham, são reiniciados por supervisores e continuam funcionando.**


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://0xmorte.gitbook.io/bibliadopentestbr/conceitos/programacao-e-linguagens/elixir/2.-estruturas-condicionais.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
