File size: 2,459 Bytes
5db9737
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import re
from email_validator import validate_email, EmailNotValidError
from ..models import SyntaxResult

# Extended RFC 5321 regex for edge cases
RFC_5321_REGEX = re.compile(
    r'^(?:[a-zA-Z0-9!#$%&\'*+/=?^_`{|}~-]+'
    r'(?:\.[a-zA-Z0-9!#$%&\'*+/=?^_`{|}~-]+)*'
    r'|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]'
    r'|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")'
    r'@(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)'
    r'+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?'
    r'|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}'
    r'(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?'
    r'|[a-zA-Z0-9-]*[a-zA-Z0-9]:'
    r'(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]'
    r'|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$'
)


def validate_syntax(email: str) -> SyntaxResult:
    """

    Layer 1: Multi-method syntax validation.

    Uses email-validator library (RFC 5321/5322) + regex fallback.

    """
    # Quick length checks
    if not email or len(email) > 254:
        return SyntaxResult(
            passed=False,
            error=f"Email length invalid: {len(email)} chars (max 254)"
        )

    if '@' not in email:
        return SyntaxResult(passed=False, error="Missing @ symbol")

    local_part, _, domain_part = email.rpartition('@')

    if len(local_part) > 64:
        return SyntaxResult(
            passed=False,
            error="Local part exceeds 64 characters (RFC 5321)"
        )

    # Try email-validator (most comprehensive)
    try:
        validated = validate_email(
            email,
            check_deliverability=False,
            allow_smtputf8=True,
            allow_empty_local=False
        )
        return SyntaxResult(
            passed=True,
            normalized_email=validated.normalized,
            local_part=validated.local_part,
            domain=validated.domain
        )
    except EmailNotValidError as e:
        # Fallback: check with regex
        if RFC_5321_REGEX.match(email):
            return SyntaxResult(
                passed=True,
                normalized_email=email,
                local_part=local_part,
                domain=domain_part
            )
        return SyntaxResult(
            passed=False,
            error=str(e)
        )
    except Exception as e:
        return SyntaxResult(
            passed=False,
            error=f"Unexpected syntax error: {str(e)}"
        )