Metadata-Version: 2.4
Name: phone-utils
Version: 0.2.1
Summary: Biblioteca para normalização, validação e geração de variantes de números telefônicos.
Author-email: Alexandre Nahuz <alexandrenahuz@gmail.com>
Project-URL: Repository, https://dev.azure.com/hyperlocal-tech/Data/_git/data-app-libs
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: phonenumbers>=9.0.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0.0; extra == "dev"
Requires-Dist: pytest-cov>=6.0.0; extra == "dev"

# phone-utils

Biblioteca Python para normalização, validação e manipulação de números de telefone internacionais, com suporte especial à regra do 9º dígito brasileiro.

O formato oficial adotado é o **E.164** (`+CCDDN...N`), utilizado como identificador único em integrações, bancos de dados e filas de eventos.

## Requisitos

- Python >= 3.10
- [phonenumbers](https://github.com/daviddrysdale/python-phonenumbers) >= 9.0.0

## Instalação

```bash
pip install phone-utils
```

## API pública

```python
from phone_utils import normalize_phone, validate_phone, generate_br_variants, phone_info, InvalidPhoneError
```

| Função | Retorno | Lança exceção |
|---|---|---|
| `normalize_phone(phone)` | `str` — número em E.164 | `InvalidPhoneError` se inválido |
| `validate_phone(phone)` | `bool` | Nunca |
| `generate_br_variants(phone)` | `list[str]` | `InvalidPhoneError` se inválido |
| `phone_info(phone)` | `dict` | `InvalidPhoneError` se inválido |

---

### `normalize_phone(phone: str) -> str`

Converte qualquer formato de entrada para E.164. A operação é **idempotente**: normalizar um número já normalizado retorna o mesmo valor.

Números brasileiros no formato legado de 8 dígitos (anteriores à migração do 9º dígito) são automaticamente normalizados para a forma canônica de 9 dígitos.

```python
normalize_phone("+55 (11) 99999-9999")  # → "+5511999999999"
normalize_phone("5511999999999")         # → "+5511999999999"
normalize_phone("+1 (202) 555-0123")    # → "+12025550123"
normalize_phone("+353871234567")         # → "+353871234567"

# Formato legado BR (8 dígitos sem 9º) → normalizado para forma canônica
normalize_phone("+556791916772")         # → "+5567991916772"

normalize_phone("11999999999")  # ✗ InvalidPhoneError — sem DDI
normalize_phone("abc")          # ✗ InvalidPhoneError
normalize_phone("")             # ✗ InvalidPhoneError
```

---

### `validate_phone(phone: str) -> bool`

Verifica se o número tem estrutura válida conforme as regras do país. Nunca lança exceção — qualquer entrada inválida retorna `False`.

Números brasileiros no formato legado de 8 dígitos são considerados válidos.

```python
validate_phone("+5511999999999")  # → True
validate_phone("+556791916772")   # → True  (formato legado BR)
validate_phone("+12025550123")    # → True
validate_phone("11999999999")     # → False  (sem DDI)
validate_phone("abc")             # → False
validate_phone("")                # → False
```

---

### `generate_br_variants(phone: str) -> list[str]`

Gera todas as representações válidas de um número brasileiro considerando a presença ou ausência do 9º dígito, útil para consultas em bases legadas.

Para números de outros países, retorna uma lista com apenas o número normalizado.

```python
# Com 9º dígito → gera variante sem
generate_br_variants("+5511999999999")
# → ["+5511999999999", "+551199999999"]

# Sem 9º dígito → normaliza e gera variante com
generate_br_variants("+551199999999")
# → ["+5511999999999", "+551199999999"]

# Sem 9º dígito compatível (local não começa com 9)
generate_br_variants("+5511799999999")
# → ["+5511799999999"]

# Número internacional → retorna só o número normalizado
generate_br_variants("+12025550123")
# → ["+12025550123"]
```

A ordem retornada é sempre: **com 9º dígito primeiro**, sem 9º dígito em seguida.

---

### `phone_info(phone: str) -> dict`

Retorna os campos individuais do número: DDI, código ISO do país, nome do país e número nacional.

```python
phone_info("+5511999999999")
# → {
#     "ddi":             "55",
#     "country":         "BR",
#     "country_name":    "Brazil",
#     "national_number": "11999999999"
# }

phone_info("+12025550123")
# → {
#     "ddi":             "1",
#     "country":         "US",
#     "country_name":    "United States",
#     "national_number": "2025550123"
# }

# Formato legado BR também é aceito
phone_info("+556791916772")
# → {
#     "ddi":             "55",
#     "country":         "BR",
#     "country_name":    "Brazil",
#     "national_number": "67991916772"
# }
```

| Campo | Tipo | Descrição |
|---|---|---|
| `ddi` | `str` | Código de discagem internacional (ex: `"55"`) |
| `country` | `str` | Código ISO 3166-1 alpha-2 (ex: `"BR"`) |
| `country_name` | `str` | Nome do país em inglês (ex: `"Brazil"`) |
| `national_number` | `str` | Número sem DDI, sem formatação (ex: `"11999999999"`) |

---

### `InvalidPhoneError`

Exceção lançada quando um número não pode ser normalizado ou processado.

```python
from phone_utils import InvalidPhoneError

try:
    normalized = normalize_phone("numero-invalido")
except InvalidPhoneError as e:
    print(f"Número inválido: {e}")
```

---

## Formatos de entrada aceitos

| Formato | Exemplo | Resultado |
|---|---|---|
| E.164 | `+5511999999999` | `+5511999999999` |
| Sem `+` (com DDI) | `5511999999999` | `+5511999999999` |
| Com máscara | `+55 (11) 99999-9999` | `+5511999999999` |
| Com espaços | `+55 11 99999 9999` | `+5511999999999` |
| Internacional | `+1 (202) 555-0123` | `+12025550123` |
| Legado BR (8 dígitos) | `+556791916772` | `+5567991916772` |

---

## Escopo da biblioteca

**Faz:**
- Normalizar números para E.164 a partir de qualquer formato de entrada
- Validar a estrutura do número conforme as regras oficiais do país
- Gerar variantes brasileiras para compatibilidade com bases legadas (regra do 9º dígito)
- Decompor um número em DDI, país e número nacional (`phone_info`)
- Suportar números internacionais

**Não faz:**
- Verificar se a linha telefônica existe ou está ativa
- Consultar operadoras ou serviços externos
- Realizar enriquecimento de dados
- Registrar logs (responsabilidade do consumidor)
- Tratar ramais

---

## Desenvolvimento

```bash
# Instalar com dependências de desenvolvimento
pip install -e ".[dev]"

# Executar testes
pytest

# Executar testes com cobertura
pytest --cov=phone_utils
```
