# base-enums — Formation Recipe (FvW v8 §38)

**Module ID:** `base-enums`
**Module name:** Character Set & Encoding Enumerations
**Lifecycle phase:** formation
**Status:** INTENT ONLY. No code yet.
**Date:** 2026-07-21

---

## 1. Intent (the "why")
- Establish a canonical source of truth for character sets and encodings used throughout FreshCloud Mail's text processing pipeline
- Eliminate magic strings by providing typed, integer-based enumerations that enforce consistency across IMAP/SMTP/MIME operations
- Enable strict input validation for text-related configurations
- Support rendering engine decisions for charset-aware UI display
- Create a foundation for future text normalization without exposing conversion logic in this module

## 2. User-facing behaviour
- Retrieve predefined character sets (e.g., UTF-8, ISO-8859-1) as integer constants
- Validate arbitrary character set strings against known standards
- Access encoding types (base64, quoted-printable, 8bit) with guaranteed constant values
- Convert between string representations and enum values bidirectionally
- Confirm valid MIME encodings for outgoing message construction

## 3. Public contract (data shapes + signatures)
```typescript
// FreshCloud\Mail\BaseEnumerations\CharsetInterface
interface CharsetInterface {
  // Canonical definitions
  public const UTF_8 = 1;
  public const ISO_8859_1 = 2;
  public const US_ASCII = 3;
  public const ISO_8859_15 = 4;
  public const WINDOWS_1252 = 5;
  
  // Contract methods
  public static function isValid(string $charset): bool;
  public static function toString(int $enumValue): string;
  public static function fromString(string $charset): ?int;
}

// FreshCloud\Mail\BaseEnumerations\EncodingInterface
interface EncodingInterface {
  // Canonical definitions
  public const BASE64 = 1;
  public const QUOTED_PRINTABLE = 2;
  public const EIGHT_BIT = 3;
  public const BINARY = 4;
  public const SEVEN_BIT = 5;
  
  // Contract methods
  public static function isValid(string $encoding): bool;
  public static function toString(int $enumValue): string;
  public static function fromString(string $encoding): ?int;
}
```

## 4. Persistence (what gets stored where)
- No database interactions (PocketBase or otherwise)
- No file persistence required (pure constants)
- No in-memory state beyond class definition
- No audit logging (no operations to log)

## 5. Dependencies (what this module uses, what uses this)
- **Uses:**
  - PHP 8.0+ core language features (enums, strict types)
- **Used by:**
  - MIME processing modules for charset validation
  - Rendering engine for encoding decisions
  - UI components for charset display strings
  - SMTP/IMAP adapters for message construction
  - Configuration modules for input validation

## 6. Behaviour inventory (the N public methods)
| Method | Signature | Purpose | Side Effects | Errors |
|--------|-----------|---------|-------------|---------|
| `Charset::isValid` | `(string $charset): bool` | Validate charset string | None | `InvalidArgumentException` if non-string input |
| `Charset::toString` | `(int $enumValue): string` | Convert enum to string | None | `InvalidArgumentException` for invalid integer |
| `Charset::fromString` | `(string $charset): ?int` | Convert string to enum | None | None (returns `null` for invalid input) |
| `Encoding::isValid` | `(string $encoding): bool` | Validate encoding string | None | `InvalidArgumentException` if non-string input |
| `Encoding::toString` | `(int $enumValue): string` | Convert enum to string | None | `InvalidArgumentException` for invalid integer |
| `Encoding::fromString` | `(string $encoding): ?int` | Convert string to enum | None | None (returns `null` for invalid input) |

## 7. State machine
- **States:** `Initialized` (constant definitions loaded)
- **Transitions:** 
  - Startup: `Uninitialized` → `Initialized`
- **State Persistence:** Static class definition (no runtime state)

## 8. Side effects
- **Writes:** None (pure constants)
- **Reads:** None (no external resources)
- **Emits:** None (no events)

## 9. Input validation
- All string inputs must be non-empty strings
- `toString()` requires integer values matching defined constants
- `isValid()` rejects null/empty strings
- Case sensitivity: All comparisons case-insensitive (e.g., "utf-8" = "UTF-8")
- Return types: Strict bool/int/typing enforced

## 10. Failure modes
| Failure Condition | Handling | Caller Impact |
|-------------------|----------|---------------|
| Invalid integer in `toString` | Throws `InvalidArgumentException` | Exception propagation |
| Invalid string in `fromString` | Returns `null` | Graceful degradation |
| Non-string input in `isValid` | Throws `InvalidArgumentException` | Exception propagation |
| Unrecognized charset/encoding | Returns `false` from `isValid`, `null` from `fromString` | Safe fallbacks |

## 11. User simulation
**Scenario 1: Validate charset string**
```
Given charset string "UTF-8"
When calling Charset::isValid("UTF-8")
Then return true
```

**Scenario 2: Convert encoding to enum**
```
Given encoding string "base64"
When calling Encoding::fromString("base64")
Then return Encoding::BASE64 (integer 1)
```

**Scenario 3: Invalid encoding handling**
```
Given invalid encoding "rot13"
When calling Encoding::fromString("rot13")
Then return null
```

**Scenario 4: UI display string**
```
Given Encoding::BASE64 (integer 1)
When calling Encoding::toString(1)
Then return "BASE64"
```

**Scenario 5: Case-insensitive validation**
```
Given charset string "iso-8859-1"
When calling Charset::isValid("ISO-8859-1")
Then return true
```

**Scenario 6: Type enforcement**
```
Given non-string input 12345
When calling Charset::isValid(12345)
Then throw InvalidArgumentException
```

## 12. Reproducibility test
1. Create directory `src/Base/Enumerations/`
2. Implement `Charset.php` with defined constants and methods
3. Implement `Encoding.php` with defined constants and methods
4. Write PHPUnit tests covering all 6 user scenarios
5. Verify type safety using PHPStan level 8
6. Confirm zero dependencies in `composer.json`
7. Validate IANA charset names for canonical values
8. Test bidirectional conversion consistency
9. Verify no runtime state after initialization

## 13. Substitutability (FvW v8 §37.4)
A drop-in replacement must provide:
- Same constant integer values for all defined charset/encoding pairs
- Identical method signatures with identical return types
- Case-insensitive string matching behavior
- Same exception types thrown in identical conditions
- Zero public state or persistent side effects

## 14. Invariants (the 5 things that must always hold)
1. All defined constants are integers
2. No runtime state beyond static class definitions
3. Case-insensitive string validation/lookup
4. No database or network dependencies
5. Zero side effects from any public method

## 15. Sweep fields applied (the 14 design intent fields)
### Sweep 1: Multi-user + PocketBase (6 fields)
- **user_scoping**: N/A - constants global to all users
- **pb_integration**: None - no database interaction
- **auth_boundary**: None - no authentication required
- **state_isolation**: N/A - no state to isolate
- **logout_behaviour**: None - no action needed
- **multi_account**: No impact - same for all accounts

### Sweep 2: World-class Abilities (5 fields)
- **connection_pool**: ✗ Not applicable (no connections)
- **offline_cache**: ✗ Not applicable (no data to cache)
- **sieve_integration**: ✗ Not applicable (no Sieve support)
- **audit_logging**: ✗ Not applicable (no events)
- **ux_primitives**: ✓ Provides encoding enums for charset-aware UI rendering

### Sweep 3: Migration + Observability + Tests (3 fields)
- **migration**: ✓ No migration (constants only, no schema)
- **observability**: ✗ Not applicable (no runtime behavior)
- **tests**: ✓ Unit tests for enum conversion and validation

## 16. Cross-references
- §11: Constitutional module structure requirements
- §37: Substitutability contract for enumeration modules
- §38: Formation recipe lifecycle phase
- §19: Clean-room reconstruction of MailSo patterns
- Dependent modules: MIME, SMTP, IMAP, Rendering Engine

## 17. What we are intentionally NOT doing (deferred to future)
- Charset conversion utilities (deferred to `text-normalizer` module)
- Dynamic charset detection (future `content-type-analyzer` module)
- Full IANA charset database (core subset only)
- Locale-specific processing (global constants only)
- Encoding conversion logic (separate `encoder` module)
- MailSo's `Enum.php` class (direct constants only)
- MailSo's 7-file module structure (2 files only)
- Runtime character set autodetection
- MIME-part-level charset specification
- User-configurable charset preferences
- Fallback chain for unknown charsets
- Performance optimizations for string lookups
- Custom encoding types (MIME standard only)