1#!/usr/bin/env python3
2"""
3
4See EOF for license/metadata/notes as applicable
5"""
6
7# Imports:
8from __future__ import annotations
9
10# ##-- stdlib imports
11import datetime
12import enum
13import functools as ftz
14import itertools as itz
15import logging as logmod
16import pathlib as pl
17import re
18import time
19import types
20import weakref
21from uuid import UUID, uuid1
22
23# ##-- end stdlib imports
24
25# ##-- 3rd party imports
26from jgdv import Proto, Mixin
27import bibtexparser
28import bibtexparser.model as model
29from bibtexparser.library import Library
30from bibtexparser import middlewares as ms
31from bibtexparser.middlewares.middleware import (BlockMiddleware, LibraryMiddleware)
32
33# ##-- end 3rd party imports
34
35# ##-- 1st party imports
36import bibble._interface as API
37from bibble.util.mixins import ErrorRaiser_m, FieldMatcher_m
38from bibble.util.middlecore import IdenBlockMiddleware
39from bibble.model import MetaBlock
40
41# ##-- end 1st party imports
42
43# ##-- types
44# isort: off
45import abc
46import collections.abc
47from typing import TYPE_CHECKING, cast, assert_type, assert_never
48from typing import Generic, NewType
49# Protocols:
50from typing import Protocol, runtime_checkable
51# Typing Decorators:
52from typing import no_type_check, final, override, overload
53
54if TYPE_CHECKING:
55 from jgdv import Maybe
56 from typing import Final
57 from typing import ClassVar, Any, LiteralString
58 from typing import Never, Self, Literal
59 from typing import TypeGuard
60 from collections.abc import Iterable, Iterator, Callable, Generator
61 from collections.abc import Sequence, Mapping, MutableMapping, Hashable
62
63 type Entry = model.Entry
64 type Field = model.Field
65##--|
66
67# isort: on
68# ##-- end types
69
70##-- logging
71logging = logmod.getLogger(__name__)
72##-- end logging
73
[docs]
74@Proto(API.WriteTime_p)
75@Mixin(ErrorRaiser_m, FieldMatcher_m)
76class PathWriter(IdenBlockMiddleware):
77 """
78 Relativize library paths back to strings
79
80 Can suppress errors from certain path roots on relativize,
81 using MetaBlock data:
82 MetaBlock(PathWriter.SuppressKey=[pl.Path()...])
83
84 """
85
86 _whitelist = ("file",)
87 SuppressKey = "PathWriter.suppress"
88 _suppress_in : list[pl.Path]
89
90 def __init__(self, *, lib_root:Maybe[pl.Path]=None, **kwargs):
91 super().__init__(**kwargs)
92 self.set_field_matchers(white=self._whitelist, black=[])
93 self._lib_root = lib_root
94 self._suppress_in = []
95
[docs]
96 def handle_meta_entry(self, library:Library):
97 match MetaBlock.find_in(library):
98 case None:
99 return
100 case block if PathWriter.SuppressKey not in block.data:
101 return
102 case block:
103 pass
104
105 match block.data[PathWriter.SuppressKey]:
106 case list() as xs:
107 self._suppress_in += xs
108 return
109 case x:
110 raise TypeError(f"{PathWriter.SuppressKey} is not a list, but a {type(x)}")
111
[docs]
112 def on_write(self):
113 Never()
114
[docs]
115 def transform_Entry(self, entry:Entry, library:Library):
116 match self.match_on_fields(entry, library):
117 case model.Entry() as x:
118 return [x]
119 case Exception() as err:
120 logging.warning(err)
121 return [entry]
122 case x:
123 raise TypeError(type(x), x)
124
[docs]
125 def field_h(self, field:Field, entry:Entry):
126 match field.value:
127 case str():
128 pass
129 case pl.Path() as val if not val.exists():
130 return ValueError(f"On Export file does not exist: {entry.key} : {val}")
131 case pl.Path() as val:
132 try:
133 as_str = val.relative_to(self._lib_root)
134 field.value = as_str
135 except ValueError:
136 if self._suppress_relative_fail(val):
137 pass
138 else:
139 field.value = str(val)
140 return ValueError(f"Failed to Relativize path {entry.key}: {val}")
141
142 return [field]
143
[docs]
144 def _suppress_relative_fail(self, val:pl.Path) -> bool:
145 for x in self._suppress_in:
146 try:
147 val.relative_to(x)
148 return True
149 except ValueError:
150 pass
151 else:
152 return False