1#!/usr/bin/env python3
2"""
3
4"""
5
6# Imports:
7from __future__ import annotations
8
9# ##-- stdlib imports
10import datetime
11import enum
12import functools as ftz
13import itertools as itz
14import logging as logmod
15import pathlib as pl
16import re
17import time
18import types
19import weakref
20from uuid import UUID, uuid1
21
22# ##-- end stdlib imports
23
24from bibtexparser import model
25from bibtexparser.library import Library
26from bibble.io.writer import BibbleWriter
27
28# ##-- types
29# isort: off
30import abc
31import collections.abc
32from typing import TYPE_CHECKING, cast, assert_type, assert_never
33from typing import Generic, NewType
34# Protocols:
35from typing import Protocol, runtime_checkable
36# Typing Decorators:
37from typing import no_type_check, final, override, overload
38
39if TYPE_CHECKING:
40 from jgdv import Maybe
41 from typing import Final
42 from typing import ClassVar, Any, LiteralString
43 from typing import Never, Self, Literal
44 from typing import TypeGuard
45 from collections.abc import Iterable, Iterator, Callable, Generator
46 from collections.abc import Sequence, Mapping, MutableMapping, Hashable
47
48 type Block = model.Block
49##--|
50
51# isort: on
52# ##-- end types
53
54##-- logging
55logging = logmod.getLogger(__name__)
56##-- end logging
57
58SPACE : Final[str] = " "
59INDENT_AMOUNT : Final[int] = 3
60##--|
[docs]
61class RstWriter(BibbleWriter):
62 """ Write bibtex entries as Rst. """
63
64 # These need to match the BibEntryDirective of the sphinx domain
65 _label : ClassVar[str] = ".. _{}:"
66 _entry : ClassVar[str] = ".. bibtex:entry:: {}"
67 _entry_args : ClassVar[list[str]] = ["title","author", "editor", "year", "tags",
68 "journal", "booktitle", "within",
69 "platform", "publisher", "institution",
70 "series", "url", "doi", "isbn", "edition",
71 "crossref", "identifier",
72 ]
73 _indent : ClassVar[str] = SPACE*INDENT_AMOUNT
74
[docs]
75 def visit_entry(self, block:Block) -> list[str]:
76 result = [
77 self._entry.format(block.key), "\n",
78 ]
79 match block.entry_type:
80 case "case" | "legal" | "judicial" | "law":
81 result += self._build_legal_entry(block)
82 case "standard" | "online" | "blog" | "dataset":
83 result += self._build_online_entry(block)
84 case "tweet" | "thread":
85 result += self._build_social_media_entry(block)
86 case _:
87 result += self._build_simple_entry(block)
88
89 # Debug info:
90 # result += self._build_debug_info(block)
91 return result
92
113
114
[docs]
115 def _build_simple_entry(self, block:Block) -> list[str]:
116 result = []
117 result += self._title_add(block)
118 result += self._must_add(block, "tags")
119 result += self._can_add(block, "author", "editor", "year", "series")
120 result += self._can_add(block, "journal", "booktitle", "doi", "url", "isbn", "publisher")
121 result += self._can_add(block, "incollection", "institution", "crossref")
122 # TODO volume, number, pages, chapter
123 return result
124
[docs]
125 def _build_legal_entry(self, block:Block) -> list[str]:
126 assert(block.entry_type in ["case", "legal","judicial", "law"])
127 result = []
128 result += self._can_add(block, "title", "short_parties")
129 result += self._must_add(block, "tags")
130 result += self._can_add(block, "author", "editor", "year", "series")
131 result += self._can_add(block, "journal", "booktitle", "doi", "url", "isbn", "publisher")
132 result += self._can_add(block, "incollection", "institution")
133 # TODO volume, number, pages, chapter
134 return result
135
[docs]
136 def _build_online_entry(self, block:Block) -> list[str]:
137 assert(block.entry_type in ["standard", "online", "blog", "dataset"])
138 result = []
139 result += self._title_add(block)
140 result += self._must_add(block, "tags")
141 result += self._can_add(block, "author", "editor", "year", "series", "url")
142 result += self._can_add(block, "journal", "booktitle", "doi", "isbn", "publisher")
143 result += self._can_add(block, "incollection", "institution", "identifier")
144 # TODO volume, number, pages, chapter
145 return result
146
[docs]
147 def _build_social_media_entry(self, block:Block) -> list[str]:
148 assert(block.entry_type in ["tweet","thread"])
149 result = []
150 result += self._title_add(block)
151 result += self._must_add(block, "tags")
152 result += self._can_add(block, "author", "editor", "year", "series")
153 result += self._can_add(block, "journal", "booktitle", "doi", "url", "isbn", "publisher")
154 result += self._can_add(block, "incollection", "institution")
155 # TODO volume, number, pages, chapter
156 return result
157
158
[docs]
159 def _build_debug_info(self, block:Block) -> list[str]:
160 result = ["\n\n..\n",
161 f"{self._indent} Fields:\n",
162 "{} {}\n".format(self._indent, ", ".join(block.fields_dict.keys())),
163 f"{self._indent} Object Keys:\n",
164 "{} {}\n".format(self._indent,
165 ", ".join([x for x in dir(block) if "__" not in x])),
166 "\n\n",
167 ]
168 return result
169
170
[docs]
171 def _title_add(self, block:Block) -> list[str]:
172 """ Format and return the title """
173 match block.get('title', None), block.get("subtitle", None):
174 case model.Field(value=str() as title), model.Field(value=str() as subtitle):
175 return [f"{self._indent}:title: {title}: {subtitle}\n"] # type:ignore
176 case model.Field(value=str() as title), _:
177 return [f"{self._indent}:title: {title}\n"] # type: ignore
178 pass
179 case _:
180 raise KeyError("no title", block.key)
181
[docs]
182 def _must_add(self, block:Block, field:str) -> list[str]:
183 match block.get(field, None):
184 case model.Field(key=key, value=val):
185 return [f"{self._indent}:{key}: {val}\n"] # type: ignore
186 case _:
187 raise KeyError('Entry missing required field', block.key, field)
188
[docs]
189 def _can_add(self, block:Block, *keys:str) -> list[str]:
190 result = []
191 for key in keys:
192 match block.get(key, None):
193 case None:
194 continue
195 case model.Field(value=val):
196 result.append(f"{self._indent}:{key}: {val}\n") # type: ignore
197
198 else:
199 return result