# Rendering ## Requirements ### Requirement: Renderable Protocol The `Renderable` protocol SHALL be owned by `ictr` and imported by appcore. The protocol uses the `as_` prefix naming convention. #### Scenario: Protocol compliance - **WHEN** a class implements `render_as_dictionary()` returning a `dict[str, Any]` - **THEN** it satisfies the `DictionaryRenderable` protocol - **AND** to also support JSON/Markdown rendering, it must additionally satisfy `JsonRenderable` and/or `MarkdownRenderable` #### Scenario: Runtime checkability - **WHEN** `isinstance(obj, Renderable)` is evaluated - **THEN** structural typing is checked at runtime - **AND** objects implementing the protocol are recognized without explicit inheritance #### Scenario: Appcore TOML extension - **WHEN** appcore needs TOML rendering - **THEN** it extends the ictr `Renderable` protocol with `render_as_toml()` - **AND** the extension is appcore-specific, not part of ictr's protocol ### Requirement: Dictionary Rendering `render_as_dictionary()` SHALL be the primary method from which all other rendering formats derive. #### Scenario: Default dictionary structure - **WHEN** `render_as_dictionary()` is called on a `Renderable` - **THEN** it returns a `dict[str, Any]` with the object's key attributes - **AND** nested `Renderable` objects have their `render_as_dictionary()` called recursively ### Requirement: JSON Rendering `render_as_json()` SHALL produce JSON output from the dictionary representation. #### Scenario: Pretty-printed JSON - **WHEN** `render_as_json()` is called with default arguments - **THEN** JSON is produced with 2-space indentation - **AND** non-ASCII characters are preserved (`ensure_ascii=False`) #### Scenario: Compact JSON - **WHEN** `render_as_json(compact=True)` is called - **THEN** JSON is produced with minimal whitespace (no indentation) - **AND** separators are `(',', ':')` ### Requirement: TOML Rendering `render_as_toml()` SHALL produce TOML output from the dictionary representation. This is an appcore-specific extension to ictr's `Renderable` protocol. #### Scenario: TOML serialization - **WHEN** `render_as_toml()` is called - **THEN** the dictionary is serialized to a valid TOML string - **AND** uses `tomli_w.dumps()` for serialization ### Requirement: Exception Rendering `Omniexception` SHALL provide rendering methods that all package exceptions inherit. Exception rendering for structured output (JSON/Markdown/TOML) is distinct from ictr's linearizer-based exception rendering for diagnostic messages — the two coexist and serve different purposes. #### Scenario: Exception dictionary rendering - **WHEN** `render_as_dictionary()` is called on any package exception - **THEN** the result contains `class`, `fqclass`, and `message` keys - **AND** subclasses can override to add context fields #### Scenario: Exception JSON rendering - **WHEN** `render_as_json()` is called on a package exception - **THEN** the exception is serialized to JSON via its dictionary representation #### Scenario: Exception Markdown rendering - **WHEN** `render_as_markdown()` is called on a package exception - **THEN** a single-line summary is returned in `[**fqclass**] message` format ### Requirement: Display-Driven Rendering `render_and_print()` SHALL render objects according to display options and write to the configured output stream (stdout). This is separate from ictr's diagnostic pipeline which targets stderr. #### Scenario: JSON presentation - **WHEN** `render_and_print()` is called with `presentation=Json` - **THEN** `render_as_json(compact=display.compact)` is called - **AND** the result is printed to the display stream #### Scenario: Markdown presentation - **WHEN** `render_and_print()` is called with `presentation=Markdown` - **THEN** `render_as_markdown(display)` is called - **AND** lines are joined with newlines and printed to the display stream #### Scenario: TOML presentation - **WHEN** `render_and_print()` is called with `presentation=Toml` - **THEN** `render_as_toml()` is called - **AND** the result is printed to the display stream