13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111 | class Formatter:
"""类似于 string.Formatter 的消息链格式化器"""
format_string: str
def __init__(self, format_string: str) -> None:
self.format_string = format_string
self._fields: tuple[tuple[str, str | None, str | None, str | None], ...] = tuple(
_global_formatter.parse(format_string)
)
for field in self._fields:
# validation, forbidding the expansion for format spec
*_, format_spec, _ = field
if format_spec:
sub_spec = tuple(_global_formatter.parse(format_spec))
if len(sub_spec) > 1 or any(sub_spec[1:]): # Definitely not right spec
raise ValueError("Format specification expansion is disallowed, found ")
@staticmethod
def _convert_field(value: Any, conversion: None | str) -> Any:
# do any conversion on the resulting object
if conversion is None:
return value
elif conversion == "s":
return str(value)
elif conversion == "r":
return repr(value)
elif conversion == "a":
return ascii(value)
raise ValueError(f"Unknown conversion specifier {conversion!s}")
@staticmethod
def _transform(obj: object) -> list[Element]:
if isinstance(obj, MessageChain):
return obj.content
elif isinstance(obj, (Element, str)):
return [Plain(obj) if isinstance(obj, str) else obj]
else:
return [Plain(str(obj))]
def format(
self,
*args: Element | MessageChain | str | Any,
**kwargs: Element | MessageChain | str | Any,
) -> MessageChain:
"""通过初始化时传入的格式字符串 格式化消息链
Args:
*args (Union[Element, MessageChain, str, Any]): 格式化时传入的位置参数
**kwargs (Union[Element, MessageChain, str, Any]): 格式化时传入的关键字参数
Returns:
MessageChain: 格式化后的消息链
"""
result: list[Element] = []
auto_arg_index: int | Literal[False] = 0
used_args: set[str] = set()
for field in self._fields:
literal_text, field_name, format_spec, conversion = field
if literal_text:
result.append(Plain(literal_text))
if field_name is None:
continue
# if there's a field, output it
# this is some markup, find the object and do
# the formatting
# handle arg indexing when empty field_names are given.
if field_name == "":
if auto_arg_index is False:
raise ValueError(
"cannot switch from manual field specification to automatic field numbering"
)
field_name = str(auto_arg_index)
auto_arg_index += 1
elif field_name.isdigit():
if auto_arg_index:
raise ValueError(
"cannot switch from manual field specification to automatic field numbering"
)
# disable auto arg incrementing, if it gets
# used later on, then an exception will be raised
auto_arg_index = False
# given the field_name, find the object it references
# and the argument it came from
obj, arg_used = _global_formatter.get_field(field_name, args, kwargs)
used_args.add(arg_used)
# do any conversion on the resulting object
obj = self._convert_field(obj, conversion)
# format the object and append to the result
if format_spec:
obj = format(obj, format_spec)
result.extend(self._transform(obj))
return MessageChain(result, inline=True).merge()
|