1# hgconfig.py - unicode wrapper for Mercurial's ui object
2#
3# Copyright 2019 Yuya Nishihara <yuya@tcha.org>
4#
5# This software may be used and distributed according to the terms of the
6# GNU General Public License version 2 or any later version.
7
8from __future__ import absolute_import
9
10from mercurial import (
11    encoding,
12    pycompat,
13    ui as uimod,
14)
15
16from ..util import (
17    hglib,
18)
19
20if hglib.TYPE_CHECKING:
21    from typing import (
22        List,
23        Text,
24        Tuple,
25    )
26
27UNSET_DEFAULT = uimod._unset
28
29class HgConfig(object):
30    """Unicode wrapper for Mercurial's ui object
31
32    This provides Qt-like API on top of the ui object. Almost all methods
33    are proxied through RepoAgent. Use these methods unless necessary.
34
35    All config*() getter functions never return None, nor take None as
36    default value. That's because Qt C++ API is strict about data types
37    in general. Use hasConfig() to test if the config value is set.
38    """
39
40    # Design notes:
41    # - It's probably better to fetch bytes data from ui at once, and cache
42    #   the unicode representation in this object. We'll have to be careful
43    #   to keep the data sync with the underlying ui object.
44    # - No setter functions are provided right now because we can't propagate
45    #   new values to the command process.
46
47    def __init__(self, ui):
48        # type: (uimod.ui) -> None
49        self._ui = ui
50
51    def rawUi(self):
52        # type: () -> uimod.ui
53        return self._ui
54
55    def configBool(self, section, name, default=UNSET_DEFAULT):
56        # type: (Text, Text, bool) -> bool
57        data = self._ui.configbool(_tobytes(section), _tobytes(name),
58                                   default=default)
59        return bool(data)
60
61    def configInt(self, section, name, default=UNSET_DEFAULT):
62        # type: (Text, Text, int) -> int
63        data = self._ui.configint(_tobytes(section), _tobytes(name),
64                                  default=default)
65        return int(data)
66
67    def configString(self, section, name, default=UNSET_DEFAULT):
68        # type: (Text, Text, Text) -> Text
69        if default is not UNSET_DEFAULT:
70            default = _tobytes(default)
71        data = self._ui.config(_tobytes(section), _tobytes(name),
72                               default=default)
73        if data is None:
74            return ''
75        return _tostr(data)
76
77    def configStringList(self, section, name, default=UNSET_DEFAULT):
78        # type: (Text, Text, List[Text]) -> List[Text]
79        if default is not UNSET_DEFAULT:
80            default = pycompat.maplist(_tobytes, default)
81        data = self._ui.configlist(_tobytes(section), _tobytes(name),
82                                   default=default)
83        return pycompat.maplist(_tostr, data)
84
85    def configStringItems(self, section):
86        # type: (Text) -> List[Tuple[Text, Text]]
87        """Returns a list of string (key, value) pairs under the specified
88        section"""
89        items = self._ui.configitems(_tobytes(section))
90        return [(_tostr(k), _tostr(v)) for k, v in items]
91
92    def hasConfig(self, section, name):
93        # type: (Text, Text) -> bool
94        return self._ui.hasconfig(_tobytes(section), _tobytes(name))
95
96
97if pycompat.ispy3:
98    _tostr = hglib.tounicode
99    _tobytes = hglib.fromunicode
100else:
101    def _tostr(s):
102        # keep ascii string as byte string on Python 2, which is supposedly
103        # faster and safer.
104        if encoding.isasciistr(s):
105            return s
106        return hglib.tounicode(s)
107
108    def _tobytes(s):
109        if not isinstance(s, pycompat.unicode):
110            return s
111        return hglib.fromunicode(s)
112