NAME

Fugu::Config - the OpenBSD-style configuration grammar

SYNOPSIS

use Fugu::Config;

my $config = Fugu::Config->new(file => '/etc/mydaemon.conf');
$config->load or die $config->error . "\n";

my $port = $config->get('port', 8080);

for my $device ($config->blocks('device')) {
    say $device->{settings}{name};
}

my $vm = $config->block('vm', 'default');

DESCRIPTION

Fugu::Config parses the configuration grammar that OpenBSD daemons use. A file holds top-level settings and blocks:

log_level debug
hap_port = 51827

device tasmota thermostat bedroom {
	name = "Bedroom Thermostat"
	topic = tasmota_AABBCC
}

Both key value and key = value parse. A double-quoted value loses its quotes. A # starts a comment that runs to the end of the line.

A block header is a type, then one or more arguments, then an opening brace. Blocks nest one level; a block inside a block is an error.

The parser keeps the blocks of each type in file order, and also addresses each one by its name, which is the last argument of the header. Thus a caller that wants an ordered list of accessories and a caller that wants the machine called "default" read the same parse.

A malformed line is an error with a file and a line number. The module never skips a line it did not understand. A typo that a parser ignores is a setting that silently does not apply, and the operator finds out from the behavior of the daemon weeks later.

new

new(%args) creates a parser for one file. The method does not read the file. Call load().

This is the argument:

file

The configuration file. This argument is necessary.

load

load() reads and parses the file. A second call starts over, so a setting that the file no longer holds is gone.

An unreadable file is an error. A caller for whom an absent file is normal tests for the file first. A daemon that silently runs on defaults after a typo in a path is worse than one that refuses to start.

file

file() returns the path that this object parses.

error

error() returns the most recent failure, with its file and line.

get

get($key, $default) returns a top-level setting, or the default.

setting_names

setting_names() returns the names of the top-level settings, sorted.

parse_bool

parse_bool($value, $default) parses a value as a switch. A setting inside a block comes through here.

The accepted spellings are yes, true, on and 1, and their opposites no, false, off and 0. The comparison ignores case.

Any other value returns the default and records an error. Thus a caller that checks gets told, and a caller that does not still gets a defined answer instead of the opposite of what the file said.

blocks

blocks($type) returns every block of the type, in file order.

Each entry is a hash reference with these keys:

type

The block type.

args

The header arguments, in order, as an array reference.

name

The last header argument.

settings

The settings inside the block, as a hash reference.

order

The position of the block in the file, counted from zero over every block of every type. A caller that reads two block types and wants them interleaved as the file wrote them sorts on it.

block

block($type, $name) returns the block of the type whose name matches, or undef. When two blocks share a name, the last one in the file wins, as an assignment does.

find_project_root

find_project_root($marker) walks up from the working directory to the first directory that holds $marker. The method returns that directory, or undef when the walk reaches the root without a match.

RETURN VALUES

load() returns the object on success. It returns undef when the file does not open or holds a line that the grammar does not accept, with the reason in error().

get(), file() and error() return a string or undef. parse_bool() returns 1 or 0. blocks() and setting_names() return a list. block() returns a hash reference or undef.

EXAMPLES

This example loads a file that may not exist yet:

my $config = Fugu::Config->new(file => $path);
if (-f $path) {
    $config->load or die $config->error . "\n";
}

my $port = $config->get('port', 8080);

ERRORS

new() dies when file is absent or empty, and find_project_root() dies without a marker. Both are programming errors.

No other method dies. load() reports every parse failure through its return value and error().

SEE ALSO

Fugu::File, Fugu::StateFile

AUTHORS

Dick Olsson <hi@senzilla.io>

CAVEATS

The grammar has no escape for the comment character. A value cannot hold a #, even inside quotes.

Every value is a string. The module does not convert a number, and 0 as a string is true in Perl. Use parse_bool() for a switch.

A key with no value is not a setting, and the parser rejects the line. The grammar has no flag form.