### ----------------------------------------------------
### -- YAMLish
### -- Licenses: Artistic-2.0
### -- Authors: Leon Timmermans
### -- File: lib/YAMLish.rakumod
### ----------------------------------------------------
### -- Chunk 2 of 2
 ~ $single.tag.full-name($namespace) ~ '>' with $single.tag;
	@parts.push: $single.type ~ escape-chars($single.value);
	take @parts.join(' ');
}

multi emit-yaml(Mapping $mapping, Namespace $namespace) {
	take '+MAP';
	for $mapping.elems -> $pair {
		emit-yaml($pair.key, $namespace);
		emit-yaml($pair.value, $namespace);
	}
	take '-MAP';
}

multi emit-yaml(Sequence $sequence, Namespace $namespace) {
	take '+SEQ';
	for $sequence.elems -> $elem {
		emit-yaml($elem, $namespace);
	}
	take '-SEQ';
}

multi emit-yaml(Alias $alias, Namespace $namespace) {
	take '=ALI *' ~ $alias.identifier
}

our sub stream-yaml(Str $input) is export {
	gather {
		if Grammar.parse($input) -> $match {
			take '+STR';
			for $match.ast -> $document {
				take '+DOC' ~ ($document.explicit ?? ' ---' !! '');
				emit-yaml($document.root, $document.namespace);
				take '-DOC';
			}
			take '-STR';
		}
	}
}

proto to-yaml($;$ = Str, :$sorted) {*}

multi to-yaml(Real:D $d; $ = Str, :$sorted) { ~$d }
multi to-yaml(Bool:D $d; $ = Str, :$sorted) { $d ?? 'true' !! 'false'; }
multi to-yaml(Str:D  $d where /^ <!Schema::Core::element> <[\w.-]>+ $/; $ = Str) {
	return $d;
}
multi to-yaml(Str:D  $d; $, :$sorted) {
	return '"' ~ escape-chars($d).trans(['"'] => ['\"']) ~ '"'
}
multi to-yaml(Positional:D $d, Str $indent, :$sorted) {
	return ' []' unless ?$d;
	return "\n" ~ $d.map({ "$indent\- " ~ to-yaml($_, $indent ~ '  ', :$sorted) }).join("\n");
}
multi to-yaml(Associative:D $d, Str $indent, :$sorted) {
	return ' {}' unless ?$d;
	my @elems = $sorted ?? $d.sort !! $d.pairs;
	return "\n" ~ @elems.map({ $indent ~ to-yaml(.key, $indent, :$sorted) ~ ': ' ~ to-yaml(.value, $indent ~ '  ', :$sorted) }).join("\n")
}

multi to-yaml(Mu:U $, $, :$sorted) { '~' }
multi to-yaml(Mu:D $s, $, :$sorted) {
	die "Can't serialize an object of type " ~ $s.WHAT.perl
}

subset Collection where Positional|Associative;

proto to-yaml-doc($, :$sorted) { * }
multi to-yaml-doc(Collection $document, :$sorted) {
	return '---' ~ to-yaml($document, '', :$sorted) ~ "\n";
}
multi to-yaml-doc(Any $document, :$sorted) {
	return '--- ' ~ to-yaml($document, '', :$sorted) ~ "\n";
}

sub save-yaml($document, :$sorted = True --> Str) is export {
	to-yaml-doc($document, :$sorted) ~ "...";
}
sub save-yamls(**@documents, :$sorted = True --> Str) is export {
	@documents.map({ to-yaml-doc($_, :$sorted) }).join('') ~ "...";
}

=begin pod

=head1 NAME

YAMLish - a YAML parser/emitter written in pure raku

=head1 DESCRIPTION

This is a YAML parser written in pure-raku.
It aims at being feature complete (though there still a few features left to implement). Patches are welcome.

=head1 INSTALLATION

=begin code :lang<console>

$ zef install YAMLish

=end code

=head1 EXPORTED SUBS

=item C<load-yaml(Str $input, ::Grammar:U :$schema = ::Schema::Core, :%tags)>
=item C<load-yamls(Str $input, ::Grammar:U :$schema = ::Schema::Core, :%tags)>
=item C<save-yaml($document, :$sorted = True)>
=item C<save-yamls(**@documents, :$sorted = True)>

=head1 Example use for a configuration file

This configuration file begins with a comment
followed by three lines of key/value:

=begin code
# key: value
lang: en
lat: 46.12345
lon: -82.6231
=end code

Access the file in your code like this:

=begin code
use YAMLish;
my $str = "config.yml".IO.slurp:
my %conf = load-yaml $str;
say %conf<lang>; # OUTPUT: «en␤»
say %conf<lat>;  # OUTPUT: «46.12345␤»
say %conf<lon>;  # OUTPUT: «-82.6231␤»
=end code

=head1 TODO

Please have a look at L<./TODO.md>

=head1 AUTHOR

Leon Timmermans

=head1 LICENSE

Artistic License 2.0

=end pod


