Source code for bibble.io.rst_writer

  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
[docs] 93 def make_header(self, library:Library, title:Maybe[str]=None) -> list[str]: 94 match title: 95 case None: 96 title = "A Bibtex File" 97 case str(): 98 pass 99 100 lines = [".. -*- mode: ReST -*-\n\n", 101 f".. _{title}:\n\n", 102 "="*len(title), "\n", 103 title, "\n", 104 "="*len(title), "\n\n", 105 ".. contents:: Entries:\n", 106 " :class: bib_entries\n", 107 " :local:\n\n", 108 # TODO mode this to a template: 109 # f"For the raw bibtex, see `the raw file <raw_{title}>`_.\n\n", 110 # f".. _`raw_{title}`: https://github.com/jgrey4296/bibliography/blob/main/main/{title}.bib\n\n", 111 ] 112 return lines
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