--- /dev/null
+package App::Clifton::BridgeConfig;
+
+use App::Clifton::Types qw(object_of_class);
+use aliased 'App::Clifton::BridgeConfig::Jabber' => 'BridgeConfig_Jabber';
+use aliased 'App::Clifton::BridgeConfig::IRC' => 'BridgeConfig_IRC';
+use Moo;
+
+has jabber => (
+ is => 'ro', required => 1, isa => object_of_class(BridgeConfig_Jabber)
+);
+
+has irc => (
+ is => 'ro', required => 1, isa => object_of_class(BridgeConfig_IRC)
+);
+
+1;
--- /dev/null
+package App::Clifton::BridgeConfig::IRC;
+
+use Moo;
+
+has $_ => (is => 'ro', required => 1)
+ for qw(server channel);
+
+1;
--- /dev/null
+package App::Clifton::BridgeConfig::Jabber;
+
+use Moo;
+
+has $_ => (is => 'ro', required => 1)
+ for qw(server user pass);
+
+1;
--- /dev/null
+package App::Clifton::BridgeConfigSet;
+
+use aliased 'App::Clifton::BridgeConfig';
+use aliased 'App::Clifton::BridgeConfig::Jabber' => 'BridgeConfig_Jabber';
+use aliased 'App::Clifton::BridgeConfig::IRC' => 'BridgeConfig_IRC';
+use Moo;
+
+with 'App::Clifton::Set';
+
+sub _set_of_class { BridgeConfig }
+sub _set_over { 'name' }
+
+sub _spec_to_constructor_args {
+ my ($self, $spec) = @_;
+ +{
+ jabber => BridgeConfig_Jabber->new($spec->{jabber}),
+ irc => BridgeConfig_IRC->new($spec->{irc}),
+ }
+}
+
+1;
--- /dev/null
+package App::Clifton::Config;
+
+use strictures 1;
+use aliased 'App::Clifton::BridgeConfigSet';
+use aliased 'App::Clifton::UserConfigSet';
+use Moo;
+
+has $_ => (
+ is => 'ro',
+ is => 'lazy'
+) for qw(bridges users);
+
+sub _build_bridges { BridgeConfigSet->new }
+sub _build_users { UserConfigSet->new }
+
+1;
--- /dev/null
+package App::Clifton::ConfigLoader;
+
+use aliased 'App::Clifton::Config';
+use aliased 'App::Clifton::UserConfig';
+use Config::Settings;
+use Moo;
+
+has _cs => (is => 'lazy', handles => { '_parse_file' => 'parse_file' });
+
+sub _build__cs {
+ Config::Settings->new
+}
+
+sub _new_config { Config->new }
+
+sub config_from_file {
+ my ($self, $file) = @_;
+ my $data = $self->_parse_file($file);
+ my $config = $self->_new_config;
+ foreach my $name (keys %{$data->{bridge}}) {
+ $config->bridges->add({
+ name => $name,
+ %{$data->{bridge}{$name}}
+ });
+ }
+ foreach my $name (keys %{$data->{user}}) {
+ $config->users->add({
+ name => $name,
+ %{$data->{user}{$name}}
+ });
+ }
+ return $config;
+}
--- /dev/null
+package App::Clifton::Set;
+
+use Scalar::Util qw(blessed);
+use Moo::Role;
+
+requires '_set_of_class';
+requires '_set_over';
+
+has _set => (is => 'ro', default => sub { {} });
+
+sub add {
+ my ($self, $spec) = @_;
+ $self->_set->{$self->_spec_to_key($spec)} = $self->_spec_to_object($spec);
+}
+
+sub get {
+ my ($self, $spec) = @_;
+ $self->_set->{$self->_spec_to_key($spec)};
+}
+
+sub _spec_to_object {
+ my ($self, $spec) = @_;
+ if (blessed($spec) && $spec->isa($self->_set_of_class)) {
+ $spec;
+ } else {
+ $self->_new_member($self->_spec_to_constructor_args($spec));
+ }
+}
+
+sub _new_member {
+ my ($self, $args) = @_;
+ $self->_set_of_class->new($args);
+}
+
+sub _spec_to_constructor_args {
+ my ($self, $spec) = @_;
+ $spec;
+}
+
+sub _spec_to_key {
+ my ($self, $spec) = @_;
+ my $key = $self->_set_over;
+ die "key ${key} is required" unless $spec->{$key};
+ $spec->{$key};
+}
+
+1;
--- /dev/null
+package App::Clifton::Types;
+
+use strictures 1;
+use base qw(Exporter);
+use Scalar::Util qw(blessed);
+
+our @EXPORT_OK = qw(object_of_class);
+
+sub object_of_class {
+ my ($name) = @_;
+ sub {
+ my ($test) = @_;
+ blessed($test) && $test->isa($name) || die "$test !isa $name";
+ };
+}
+
+1;
--- /dev/null
+package App::Clifton::UserConfig;
+
+use Moo;
+
+has allow => (is => 'ro', required => 1);
+has irc_nick => (is => 'ro', required => 1);
+
+1;
--- /dev/null
+package App::Clifton::UserConfigSet;
+
+use aliased 'App::Clifton::UserConfig';
+use Moo;
+
+with 'App::Clifton::Set';
+
+sub _set_of_class { UserConfig }
+sub _set_over { 'name' }
+
+1;
--- /dev/null
+use strictures 1;
+use Test::More;
+
+use App::Clifton::ConfigLoader;
+
+my $cl = App::Clifton::ConfigLoader->new;
+
+use Devel::Dwarn;
+
+Dwarn($cl->config_from_file('t/test.conf'));
--- /dev/null
+bridge "example" {
+ jabber {
+ server "example.org";
+ user "user@example.org";
+ pass "redacted";
+ };
+ irc {
+ server "irc.example.org";
+ channel "#example";
+ }
+};
+
+user "mst@example.org" {
+ allow [ "example" ];
+ irc_nick "mst_clifton";
+};