Passed
Push — main ( da4e6c...a03b0b )
by Jochen
01:53
created

syslog2irc.config   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 118
Duplicated Lines 0 %

Test Coverage

Coverage 98.31%

Importance

Changes 0
Metric Value
eloc 70
dl 0
loc 118
rs 10
c 0
b 0
f 0
ccs 58
cts 59
cp 0.9831
wmc 14

5 Functions

Rating   Name   Duplication   Size   Complexity  
A load_config() 0 8 1
A _get_routes() 0 21 5
A _get_irc_server() 0 17 4
A _get_irc_channels() 0 5 2
A _get_irc_config() 0 18 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
    commands = data_irc.get('commands', [])
56 1
    channels = set(_get_irc_channels(data_irc))
57
58 1
    if not channels:
59 1
        logger.warning('No IRC channels to join have been configured.')
60
61 1
    return IrcConfig(
62
        server=server,
63
        nickname=nickname,
64
        realname=realname,
65
        commands=commands,
66
        channels=channels,
67
    )
68
69
70 1
def _get_irc_server(data_irc: Any) -> Optional[IrcServer]:
71 1
    data_server = data_irc.get('server')
72 1
    if data_server is None:
73 1
        return None
74
75 1
    host = data_server.get('host')
76 1
    if not host:
77 1
        return None
78
79 1
    port = int(data_server.get('port', DEFAULT_IRC_SERVER_PORT))
80 1
    ssl = data_server.get('ssl', False)
81 1
    password = data_server.get('password')
82 1
    rate_limit_str = data_server.get('rate_limit')
83 1
    rate_limit = float(rate_limit_str) if rate_limit_str else None
84
85 1
    return IrcServer(
86
        host=host, port=port, ssl=ssl, password=password, rate_limit=rate_limit
87
    )
88
89
90 1
def _get_irc_channels(data_irc: Any) -> Iterator[IrcChannel]:
91 1
    for channel in data_irc.get('channels', []):
92 1
        name = channel['name']
93 1
        password = channel.get('password')
94 1
        yield IrcChannel(name, password)
95
96
97 1
def _get_routes(
98
    data: Dict[str, Any], irc_channels: Set[IrcChannel]
99
) -> Set[Route]:
100 1
    data_routes = data.get('routes', {})
101 1
    if not data_routes:
102 1
        logger.warning('No routes have been configured.')
103
104 1
    known_irc_channel_names = {c.name for c in irc_channels}
105
106 1
    def iterate() -> Iterator[Route]:
107 1
        for port, irc_channel_names in data_routes.items():
108 1
            for irc_channel_name in irc_channel_names:
109 1
                if irc_channel_name not in known_irc_channel_names:
110
                    raise ConfigurationError(
111
                        f'Route target IRC channel "{irc_channel_name}" '
112
                        'is not configured to be joined.'
113
                    )
114
115 1
                yield Route(port=int(port), irc_channel_name=irc_channel_name)
116
117
    return set(iterate())
118