# SPDX-License-Identifier: BSD-3-Clause-CMU
# See COPYING file at the root of the distribution for more details.
package Cyrus::IMAPOptions::App::Command::manrst;
use warnings;
use strict;

use Cyrus::IMAPOptions::App -command;

use Cyrus::IMAPOptions;

sub abstract { 'build imapd.conf man page in rst format from imapoptions' }

sub usage_desc { 'manrst %o imapoptions' }

sub opt_spec { return Cyrus::IMAPOptions::App::common_opt_spec; }

sub validate_args
{
    my ($self, $opt, $args) = @_;

    $self->usage_error("need imapoptions path") if @$args < 1;
    $self->usage_error("too many arguments") if @$args > 1;

    $self->usage_error("imapoptions must be a directory")
        if ! -d $args->[0];
}

sub execute
{
    my ($self, $opt, $args) = @_;

    my $imapoptions = Cyrus::IMAPOptions->new(
        from_path => $args->[0],
        forbid_unreleased => $opt->forbid_unreleased,
    );

    $imapoptions->check_unreleased($self->app->arg0);

    $self->header();
    $imapoptions->iterate(\&_print_option);
    $self->footer();
}

sub header
{
    my ($self) = @_;

    my $rst = <<~'END_HEADER';
    .. auto-generated by tools/imapoptions.pl

    .. cyrusman:: imapd.conf(5)

    .. _imap-reference-manpages-configs-imapd.conf:

    ==============
    **imapd.conf**
    ==============

    IMAP configuration file

    DESCRIPTION
    ===========

    **/etc/imapd.conf**
    is the configuration file for the Cyrus IMAP server.  It defines
    local parameters for IMAP.

    Each line of the **/etc/imapd.conf** file has the form
    *option*: *value*

    where *option* is the name of the configuration option being set
    and *value* is the value that the configuration option is being
    set to.

    Although there is no limit to the length of a line, a \`\`\\''
    (backslash) character may be used as the last character on a line to
    force it to continue on the next one.  No additional whitespace is
    inserted before or after the \`\`\\''.  Note that a line that is split
    using \`\`\\'' character(s) is still considered a single line.

    For example
    *option*:\\

    *value*\ 1 *value*\ 2 \\

    *value*\ 3

    is equivalent to
    *option*: *value*\ 1 *value*\ 2   *value*\ 3

    Blank lines and lines beginning with \`\`#'' are ignored.

    For boolean and enumerated options, the values \`\`yes'', \`\`on'', \`\`t'',
    \`\`true'' and \`\`\ 1'' turn the option on, the values \`\`no'', \`\`off'',
    \`\`f'', \`\`false'' and \`\`\ 0'' turn the option off.

    Duration options take the form of a number followed by a unit, for example
    **\ 32m** (32 minutes).  Units are **d** (days), **h** (hours), **m**
    (minutes) and **s** (seconds).  Multiple units can be combined and will
    be summed together, for example **\ 1h30m** is equivalent to **\ 90m**.  If
    no unit is specified, an option-specific backward-compatible default unit
    is assumed (documented on an option-by-option basis).  These are simple time
    units: 1d=24h, 1h=60m, 1m=60s (daylight savings, timezones, leap adjustments,
    etc are not considered).

    Byte size options take the form of a number followed by a unit, for example
    **\ 1KiB** (1 kibibyte).  Units are **B** (bytes), **KiB** (kibibytes),
    **MiB** (mebibytes), and **GiB** (gibibytes), which may also be spelt
    **KB**, **MB**, and **GB**.  Units are parsed without regard to case.
    Note that regardless of spelling, these units are always powers of 2, and
    never metric.  That is, 1GiB = 1024MiB, 1MiB = 1024KiB, 1KiB = 1024B.  If no
    unit is specified, an option-specific backward-compatible default unit is
    assumed (documented on an option-by-option basis).

    OPTION DESCRIPTIONS
    ===================

    The sections below detail options that can be placed in the
    **/etc/imapd.conf** file, and show each option's default value.
    Some options have no default value, these are listed with
    \`\`<no default>''.  Some options default to the empty string, these
    are listed with \`\`<none>''.

    END_HEADER

    print $rst;
}

sub footer
{
    my ($self) = @_;

    my $rst = <<~'END_FOOTER';
    SEE ALSO
    ========

    :cyrusman:`imapd(8)`,
    :cyrusman:`pop3d(8)`,
    :cyrusman:`nntpd(8)`,
    :cyrusman:`lmtpd(8)`,
    :cyrusman:`httpd(8)`,
    :cyrusman:`timsieved(8)`,
    :cyrusman:`idled(8)`,
    :cyrusman:`notifyd(8)`,
    :cyrusman:`deliver(8)`,
    :cyrusman:`master(8)`,
    :manpage:`ciphers(1)`
    END_FOOTER

    print $rst;
}

sub _rst_heading
{
    my ($s, $u) = @_;

    $u //= '-';

    return "$s\n" . $u x length($s) . "\n\n";
}

sub _print_option
{
    my ($name, $option) = @_;

    my $dv = $option->docs_default_value;
    $dv =~ s/\*/\\\*/g;

    print ".. imapdconf:: $name\n\n";
    print _rst_heading($name);

    print "Default: $dv\n\n";

    if ($option->has_documentation) {
        foreach my $line ($option->documentation->@*) {
            chomp $line;
            print "$line\n";
        }

        print "\n";

        if ($option->can_have_units) {
            if ($option->has_default_unit) {
                print "For backward compatibility, if no unit is specified, ";
            }
            else {
                print "If no unit is specified, "
            }
            print $option->docs_default_unit;
            print " is assumed.\n\n";
        }
    }

    if ($option->has_deprecated_since) {
        if ($option->has_replaced_by) {
            print 'Deprecated in favour of :imapdconf:`';
            print $option->replaced_by;
            print "`.\n";
        }
        else {
            print "Deprecated. No longer used.\n";
        }
        print "\n";
    }
    elsif ($option->has_allowed_values) {
        print "Allowed values: ";
        print join ', ', map { '*' . $_ . '*' }
                         $option->allowed_values->value_alias_strings;
        print "\n\n";
    }
}

1;
