Passed
Push — main ( fedced...62aa48 )
by Jochen
01:57
created

syslog2irc.config._get_irc_config()   A

Complexity

Conditions 2

Size

Total Lines 16
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 13
dl 0
loc 16
ccs 9
cts 9
cp 1
rs 9.75
c 0
b 0
f 0
cc 2
nop 1
crap 2
1
"""
2
syslog2irc.config
3
~~~~~~~~~~~~~~~~~
4
5
Configuration loading
6
7
:Copyright: 2007-2021 Jochen Kupperschmidt
8
:License: MIT, see LICENSE for details.
9
"""
10
11 1
from dataclasses import dataclass
12 1
import logging
13 1
from pathlib import Path
14 1
from typing import Any, Dict, Iterator, Optional, Set
15
16 1
import rtoml
17
18 1
from .irc import IrcChannel, IrcConfig, IrcServer
19 1
from .router import Route
20
21
22 1
DEFAULT_IRC_SERVER_PORT = 6667
23 1
DEFAULT_IRC_REALNAME = 'syslog'
24
25
26 1
logger = logging.getLogger(__name__)
27
28
29 1
class ConfigurationError(Exception):
30
    """Indicates a configuration error."""
31
32
33 1
@dataclass(frozen=True)
34
class Config:
35 1
    irc: IrcConfig
36 1
    routes: Set[Route]
37
38
39 1
def load_config(path: Path) -> Config:
40
    """Load configuration from file."""
41 1
    data = rtoml.load(path)
42
43 1
    irc_config = _get_irc_config(data)
44 1
    routes = _get_routes(data, irc_config.channels)
45
46 1
    return Config(irc=irc_config, routes=routes)
47
48
49 1
def _get_irc_config(data: Dict[str, Any]) -> IrcConfig:
50 1
    data_irc = data['irc']
51
52 1
    server = _get_irc_server(data_irc)
53 1
    nickname = data_irc['bot']['nickname']
54 1
    realname = data_irc['bot'].get('realname', DEFAULT_IRC_REALNAME)
55 1
    channels = set(_get_irc_channels(data_irc))
56
57 1
    if not channels:
58 1
        logger.warning('No IRC channels to join have been configured.')
59
60 1
    return IrcConfig(
61
        server=server,
62
        nickname=nickname,
63
        realname=realname,
64
        channels=channels,
65
    )
66
67
68 1
def _get_irc_server(data_irc: Any) -> Optional[IrcServer]:
69 1
    data_server = data_irc.get('server')
70 1
    if data_server is None:
71 1
        return None
72
73 1
    host = data_server.get('host')
74 1
    if not host:
75 1
        return None
76
77 1
    port = int(data_server.get('port', DEFAULT_IRC_SERVER_PORT))
78 1
    ssl = data_server.get('ssl', False)
79 1
    password = data_server.get('password')
80 1
    rate_limit_str = data_server.get('rate_limit')
81 1
    rate_limit = float(rate_limit_str) if rate_limit_str else None
82
83 1
    return IrcServer(
84
        host=host, port=port, ssl=ssl, password=password, rate_limit=rate_limit
85
    )
86
87
88 1
def _get_irc_channels(data_irc: Any) -> Iterator[IrcChannel]:
89 1
    for channel in data_irc.get('channels', []):
90 1
        name = channel['name']
91 1
        password = channel.get('password')
92 1
        yield IrcChannel(name, password)
93
94
95 1
def _get_routes(
96
    data: Dict[str, Any], irc_channels: Set[IrcChannel]
97
) -> Set[Route]:
98 1
    data_routes = data.get('routes', {})
99 1
    if not data_routes:
100 1
        logger.warning('No routes have been configured.')
101
102 1
    known_irc_channel_names = {c.name for c in irc_channels}
103
104 1
    def iterate() -> Iterator[Route]:
105 1
        for port, irc_channel_names in data_routes.items():
106 1
            for irc_channel_name in irc_channel_names:
107 1
                if irc_channel_name not in known_irc_channel_names:
108
                    raise ConfigurationError(
109
                        f'Route target IRC channel "{irc_channel_name}" '
110
                        'is not configured to be joined.'
111
                    )
112
113 1
                yield Route(port=int(port), irc_channel_name=irc_channel_name)
114
115
    return set(iterate())
116