# Task 04: Phone Number Normalization ## Objective Build `internal/phone/normalize.go` — bidirectional conversion between E.164 and Philippine local format. ## Functions ### ToLocal — E.164 → Philippine local ```go func ToLocal(e164 string) (string, error) ``` | Input | Output | Notes | |-------|--------|-------| | `+639171234567` | `09171234567` | Standard E.164 | | `639171234567` | `09171234567` | Missing `+` prefix | | `09171234567` | `09171234567` | Already local, pass through | | `9171234567` | `09171234567` | Missing leading `0` | | `+1234567890` | error | Non-PH country code | | `` | error | Empty | | `abc` | error | Non-numeric | Rules: 1. Strip all non-digit characters (spaces, dashes, parens, `+`) 2. If starts with `63` and length is 12: replace `63` with `0` 3. If starts with `9` and length is 10: prepend `0` 4. If starts with `0` and length is 11: pass through 5. Otherwise: return error "invalid Philippine phone number" 6. Validate result is exactly 11 digits starting with `09` ### ToE164 — Philippine local → E.164 ```go func ToE164(local string) (string, error) ``` | Input | Output | Notes | |-------|--------|-------| | `09171234567` | `+639171234567` | Standard local | | `9171234567` | `+639171234567` | Missing leading `0` | | `+639171234567` | `+639171234567` | Already E.164, pass through | | `` | error | Empty | Rules: 1. Strip all non-digit characters except leading `+` 2. If starts with `+63`: pass through 3. If starts with `63` and length is 12: prepend `+` 4. If starts with `0` and length is 11: replace `0` with `+63` 5. If starts with `9` and length is 10: prepend `+63` 6. Otherwise: return error "invalid Philippine phone number" 7. Validate result matches `+63` + 10 digits ## Tests (`internal/phone/normalize_test.go`) Test both functions with the table-driven test pattern: ```go func TestToLocal(t *testing.T) { tests := []struct { name string input string want string wantErr bool }{ {"e164 with plus", "+639171234567", "09171234567", false}, {"e164 without plus", "639171234567", "09171234567", false}, {"already local", "09171234567", "09171234567", false}, {"missing leading zero", "9171234567", "09171234567", false}, {"non-PH number", "+1234567890", "", true}, {"empty", "", "", true}, {"with spaces", "+63 917 123 4567", "09171234567", false}, {"with dashes", "0917-123-4567", "09171234567", false}, {"too short", "0917", "", true}, {"too long", "091712345678", "", true}, } // ... run tests } ``` Same pattern for `TestToE164`. ## Acceptance Criteria - [ ] `go build ./cmd/server/` succeeds - [ ] `ToLocal` handles all input formats in the table - [ ] `ToE164` handles all input formats in the table - [ ] Non-Philippine numbers return error - [ ] Empty strings return error - [ ] Non-numeric input returns error - [ ] Spaces, dashes, parens are stripped - [ ] Results are validated (length + prefix check) - [ ] All tests pass: `go test ./internal/phone/`