1#!/usr/bin/env python3
2"""
3
4"""
5# Imports:
6from __future__ import annotations
7
8# ##-- stdlib imports
9import datetime
10import enum
11import functools as ftz
12import itertools as itz
13import logging as logmod
14import pathlib as pl
15import re
16import time
17import types
18import collections
19import contextlib
20import hashlib
21from copy import deepcopy
22from uuid import UUID, uuid1
23from weakref import ref
24import atexit # for @atexit.register
25import faulthandler
26# ##-- end stdlib imports
27
28import jgdv
29from jgdv import Proto, Mixin
30from bibble import _interface as API # noqa: N812
31from bibble.util.middlecore import IdenBidiMiddleware
32from bibtexparser import model
33
34# ##-- types
35# isort: off
36import abc
37import collections.abc
38from typing import TYPE_CHECKING, cast, assert_type, assert_never
39from typing import Generic, NewType, Never
40# Protocols:
41from typing import Protocol, runtime_checkable
42# Typing Decorators:
43from typing import no_type_check, final, override, overload
44
45if TYPE_CHECKING:
46 from jgdv import Maybe
47 from typing import Final
48 from typing import ClassVar, Any, LiteralString
49 from typing import Self, Literal
50 from typing import TypeGuard
51 from collections.abc import Iterable, Iterator, Callable, Generator
52 from collections.abc import Sequence, Mapping, MutableMapping, Hashable
53
54 from bibtexparser import Library
55 type Entry = model.Entry
56 type String = model.String
57 type Field = model.Field
58##--|
59
60# isort: on
61# ##-- end types
62
63##-- logging
64logging = logmod.getLogger(__name__)
65##-- end logging
66
67# Vars:
68OBRACE : Final[str] = "{"
69CBRACE : Final[str] = "}"
70DQUOTE : Final[str] = '"'
71STRINGS_CAN_BE_UNESCAPED_INTS = False
72ENTRY_POTENTIALLY_INT_FIELDS = [
73 "year",
74 "month",
75 "volume",
76 "number",
77 "pages",
78 "edition",
79 "chapter",
80 "issue",
81]
82# Body:
83
[docs]
84@Proto(API.BidirectionalMiddleware_p)
85class BraceWrapper(IdenBidiMiddleware):
86 """ a Bidirectional replacement for bibtexparsers 'Add/RemoveEnclosingMiddleware' """
87
88 def __init__(self, *, wrappers:Maybe[tuple[str,str]]=None, on_ints:bool=True, **kwargs):
89 super().__init__(**kwargs)
90 self._wrapper = wrappers or (OBRACE, CBRACE)
91 self._on_ints = on_ints
92
[docs]
93 def _wrap(self, value: str, *, maybe_int_rule:bool=False) -> str:
94 """ Take 'value' and wrap it in (by default) braces """
95 if maybe_int_rule and not self._on_ints and value.isdigit():
96 return value
97
98 ochar, cchar = self._wrapper
99 return f"{ochar}{value}{cchar}"
100
[docs]
101 def _unwrap(self, value: str) -> str:
102 """ remove wrapping (by default) braces """
103 ochar, cchar = self._wrapper
104 start, end = len(ochar), 0 - len(cchar)
105 value = value.strip()
106 if value.startswith(ochar) and value.endswith(cchar):
107 return value[start:end]
108 else:
109 return value
110
[docs]
111 def write_transform_Entry(self, entry:Entry, *args, **kwargs) -> list[Entry]:
112 for field in entry.fields:
113 field.value = self._wrap(field.value, maybe_int_rule=field.key in ENTRY_POTENTIALLY_INT_FIELDS)
114 else:
115 return [entry]
116
120
[docs]
121 def read_transform_Entry(self, entry: Entry, library: Library) -> list[Entry]:
122 for field in entry.fields:
123 field.value = self._unwrap(field.value)
124 else:
125 return [entry]
126
127 Never()
128