=begin rakudoc

=TITLE Adding Formats

=SUBTITLE Parse and render support for JSON, CSV, XML, and similar data formats

=head1 Overview

Formats convert between external data representations and Qwiratry-visible data.
When a format produces custom tree nodes, add a tree navigator as a separate
extension point.

Use this extension point when the new thing is a representation such as JSON,
CSV, XML, YAML, TOML, binary encodings, or another parse/render representation. Do not use it for
databases, APIs, filesystems, or other live sources and destinations; those
belong in source/destination extensions.

To add support for a new format:

=item 1. B<Setup>: choose the format name, module path, and C<META6.json> entry.

=item 2. B<Parser>: implement a parser if Qwiratry should receive that representation.

=item 3. B<Renderer>: implement a renderer if Qwiratry should output that representation.

=item 4. B<Navigator>: optionally add a tree navigator if the parsed data outputs
custom tree nodes.

=item 5. B<Testing>: test discovery, parsing, rendering, navigation, and
missing-format errors.

The current base parser receives a C<Str> because the existing formats are
text-like, but the extension model is about data representations in general.
Future base classes or overloads may support binary blobs or other non-text
payloads.

=head1 Setup

=head2 Choose The Format Name

Choose the format name that callers will use, such as C<JSON>, C<CSV>, or
C<CBOR>. Format names are normalized by C<Qwiratry::Format>, so callers may use
labels like C<json> while the module remains named C<JSON>.

=head2 Add The Module

Add a module under C<lib/Qwiratry/Format/> named for that format.

A format module is named:

=begin code :lang<raku>
Qwiratry::Format::<FORMAT>
=end code

It defines one or more operation classes:

=begin code :lang<raku>
Qwiratry::Format::<FORMAT>::Parse
Qwiratry::Format::<FORMAT>::Render
=end code

For example, the built-in demonstration formats use C<*demo> names to make it
clear that they are not complete production parsers:

=begin code :lang<raku>
Qwiratry::Format::JSONdemo::Parse
Qwiratry::Format::JSONdemo::Render
=end code

Formats that parse into ordinary Raku data (eg. Positional/Associative) usually
do not need a tree navigator.

=head2 Update META6

Add the format module to C<META6.json> C<provides> so discovery can load it.

=begin code :lang<raku>
"Qwiratry::Format::Example": "lib/Qwiratry/Format/Example.rakumod"
=end code

=head1 Parser

=head2 Parser Contract

C<Qwiratry::Format::Base::ParseWalkable> is the base class for parsers. A custom parser
inherits from this and currently implements C<parse-walkable(Str $input-string --> Mu)>.

=head2 Minimal Parser Example

=begin code :lang<raku>
use Qwiratry::Format::Base;

class Qwiratry::Format::Example::Parse is Qwiratry::Format::Base::ParseWalkable {
    method parse-walkable(Str $input-data --> Mu) {
        %(raw => $input-data)
    }
}
=end code

=head1 Renderer

=head2 Renderer Contract

C<Qwiratry::Format::Base::RenderWalkable> is the base class for renderers. A custom
renderer inherits from this and implements
C<render-walkable(Mu $data, Associative :%options --> Str)>.

=head2 Minimal Renderer Example

=begin code :lang<raku>
use Qwiratry::Format::Base;

class Qwiratry::Format::Example::Render is Qwiratry::Format::Base::RenderWalkable {
    method render-walkable(Mu $data, Associative :%options --> Str) {
        $data.raku
    }
}
=end code

=head1 Tree Navigator

A format may need a C<Qwiratry::Tree::Navigator::<NAVIGATOR>> module when its
parser emits a custom tree-shaped object model.

Navigators are documented in L<Adding Tree Navigators|Adding-Tree-Navigators>.
Use a navigator when the generic tree walker is still the right traversal engine,
but Qwiratry needs format-specific child, parent, or attribute access.

A tree navigator does not need a matching C<Parse> or C<Render> class. For
example, C<RakuAST> is represented only as
C<Qwiratry::Tree::Navigator::RakuAST> because the Raku compiler owns parsing and
rendering.

=head1 Testing

Add tests that prove:

=item C<Qwiratry::Format.formats(:type<Parse>)> discovers the parser.

=item C<Qwiratry::Format.formats(:type<Render>)> discovers the renderer.

=item C<Qwiratry::Format.make(:type<Parse>, :format<FORMAT>)> returns a parser.

=item C<Qwiratry::Format.make(:type<Render>, :format<FORMAT>)> returns a renderer.

=item C<Qwiratry::Tree::Navigator.navigators> discovers any navigator needed for
custom tree nodes.

=item Parse operators accept the new format.

=item Render operators accept the new format.

=item Missing formats throw C<X::Qwiratry::Format::NotFound>.

=head1 Reference Material

=head2 Main Types

=item C<Qwiratry::Format>

Factory and discovery service for parse and render implementations.

=item C<Qwiratry::Format::Base::ParseWalkable>

Base class for parsers. A custom parser inherits from this and currently implements
C<parse-walkable(Str $input-string --> Mu)>.

=item C<Qwiratry::Format::Base::RenderWalkable>

Base class for renderers. A custom renderer inherits from this and implements
C<render-walkable(Mu $data, Associative :%options --> Str)>.

=item C<Qwiratry::Tree::Navigator::Base>

Role for tree navigators. A custom navigator does this and implements the
required tree access methods.

=item C<X::Qwiratry::Format::NotFound>

Thrown when parse or render operators request a format that cannot be found.

=head2 Discovery

C<Qwiratry::Format> discovers modules under C<Qwiratry::Format::*> and checks
whether the requested operation class inherits from the matching base class.

=begin code :lang<raku>
my @parse-formats = Qwiratry::Format.formats(:type<Parse>);
my @render-formats = Qwiratry::Format.formats(:type<Render>);

my $parser = Qwiratry::Format.make(:type<Parse>, :format<JSONdemo>);
my $renderer = Qwiratry::Format.make(:type<Render>, :format<JSONdemo>);
=end code

=end rakudoc
