Skip to content

Commit c4eb112

Browse files
committed
pb diff now resolves and show file uid gid
1 parent b9a3d1a commit c4eb112

1 file changed

Lines changed: 97 additions & 11 deletions

File tree

prime_backup/mcdr/task/backup/diff_backup_task.py

Lines changed: 97 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
import functools
12
import operator
2-
from typing import Callable, List, TypeVar, Any, Optional
3+
from typing import Callable, Dict, List, TypeVar, Any, Optional, cast
34

45
from mcdreforged.api.all import CommandSource, RTextBase, RText, RTextList, RColor, PermissionLevel
56
from typing_extensions import override
@@ -15,6 +16,66 @@
1516
_R = TypeVar('_R')
1617

1718

19+
class _UnixUidGidNameResolver:
20+
def __init__(self):
21+
self.__uid_names: Dict[int, Optional[str]] = {}
22+
self.__gid_names: Dict[int, Optional[str]] = {}
23+
24+
@functools.cached_property
25+
def supported(self) -> bool:
26+
try:
27+
import pwd, grp
28+
except ImportError:
29+
return False
30+
else:
31+
return hasattr(pwd, 'getpwuid') and hasattr(grp, 'getgrgid')
32+
33+
def __get_uid_name(self, uid: int) -> Optional[str]:
34+
if not self.supported:
35+
return None
36+
if uid not in self.__uid_names:
37+
name = None
38+
try:
39+
import pwd
40+
name = cast(Any, pwd).getpwuid(uid).pw_name
41+
except (ImportError, KeyError):
42+
pass
43+
self.__uid_names[uid] = name
44+
return self.__uid_names[uid]
45+
46+
def __get_gid_name(self, gid: int) -> Optional[str]:
47+
if not self.supported:
48+
return None
49+
if gid not in self.__gid_names:
50+
name = None
51+
try:
52+
import grp
53+
name = cast(Any, grp).getgrgid(gid).gr_name
54+
except (ImportError, KeyError):
55+
pass
56+
self.__gid_names[gid] = name
57+
return self.__gid_names[gid]
58+
59+
@classmethod
60+
def __format_id(cls, id_: Optional[int], name_getter: Callable[[int], Optional[str]], id_name: str) -> RTextBase:
61+
if id_ is None:
62+
return RText('?', RColor.gray)
63+
if (name := name_getter(id_)) is not None:
64+
return RText(name, TextColors.number).h(f'{id_name}={id_}')
65+
return TextComponents.number(id_)
66+
67+
def uid(self, uid: Optional[int]) -> RTextBase:
68+
return self.__format_id(uid, self.__get_uid_name, 'uid')
69+
70+
def gid(self, gid: Optional[int]) -> RTextBase:
71+
return self.__format_id(gid, self.__get_gid_name, 'gid')
72+
73+
def pair_columns(self, file: FileInfo) -> List[RTextBase]:
74+
if not self.supported:
75+
return []
76+
return [self.uid(file.uid), self.gid(file.gid)]
77+
78+
1879
def _get_raw_size_and_hash_from_blob(blob: BlobInfo) -> SizeAndHash:
1980
return SizeAndHash(blob.raw_size, blob.hash)
2081

@@ -23,17 +84,40 @@ def _map_or_none(value: Optional[_T], maper: Callable[[_T], _R]) -> Optional[_R]
2384
return maper(value) if value is not None else None
2485

2586

87+
def _pretty_mode(mode: int) -> RTextBase:
88+
return TextComponents.file_mode(mode)
89+
90+
91+
def _get_file_size(file: FileInfo) -> int:
92+
if file.blob is not None:
93+
return file.blob.raw_size
94+
if file.content is not None:
95+
return len(file.content)
96+
return 0
97+
98+
99+
def _signed_file_size(sign: str, size: int, color: RColor) -> RTextBase:
100+
return RTextList(RText(sign, color), TextComponents.file_size(size, color=color))
101+
102+
26103
class DiffBackupTask(LightTask[None]):
27104
def __init__(self, source: CommandSource, backup_id_old: int, backup_id_new: int):
28105
super().__init__(source)
29106
self.backup_id_old = backup_id_old
30107
self.backup_id_new = backup_id_new
108+
self.__uid_gid_resolver = _UnixUidGidNameResolver()
31109

32110
@property
33111
@override
34112
def id(self) -> str:
35113
return 'backup_diff'
36114

115+
def __make_uid_gid_columns(self, file: FileInfo) -> RTextBase:
116+
columns = self.__uid_gid_resolver.pair_columns(file)
117+
if len(columns) == 0:
118+
return RTextList()
119+
return RTextList(RTextBase.join(' ', columns), ' ')
120+
37121
@override
38122
def run(self) -> None:
39123
result = DiffBackupAction(self.backup_id_old, self.backup_id_new, compare_status=False).run()
@@ -45,6 +129,8 @@ def run(self) -> None:
45129
self.reply_tr('no_diff', t_bid_old, t_bid_new)
46130
return
47131

132+
old_diff_size = sum(_get_file_size(file) for file in result.deleted) + sum(_get_file_size(file) for file, _ in result.changed)
133+
new_diff_size = sum(_get_file_size(file) for file in result.added) + sum(_get_file_size(file) for _, file in result.changed)
48134
self.reply_tr(
49135
'found_diff',
50136
TextComponents.number(result.diff_count),
@@ -53,17 +139,17 @@ def run(self) -> None:
53139
RText(f'+{len(result.added)}', RColor.green),
54140
RText(f'-{len(result.deleted)}', RColor.red),
55141
RText(f'*{len(result.changed)}', RColor.yellow),
142+
_signed_file_size('-', old_diff_size, RColor.red),
143+
_signed_file_size('+', new_diff_size, RColor.green),
56144
])
57145
)
58146

59-
def pretty_mode(mode: int) -> RTextBase:
60-
return TextComponents.file_mode(mode)
61-
62147
def reply_single(f: FileInfo, head: RTextBase):
63148
text = RTextBase.format(
64-
'{} {} {}',
149+
'{} {} {}{}',
65150
head,
66-
pretty_mode(f.mode),
151+
_pretty_mode(f.mode),
152+
self.__make_uid_gid_columns(f),
67153
TextComponents.file_path(f.path),
68154
)
69155
if f.is_link():
@@ -91,7 +177,7 @@ def make_hover(what_old: Optional[_T], what_new: Optional[_T], what_mapper: Call
91177

92178
if old_file.mode != new_file.mode:
93179
t_change = self.tr('diff.mode')
94-
make_hover(pretty_mode(old_file.mode), pretty_mode(new_file.mode))
180+
make_hover(_pretty_mode(old_file.mode), _pretty_mode(new_file.mode))
95181
elif (sah1 := _map_or_none(old_file.blob, _get_raw_size_and_hash_from_blob)) != (sah2 := _map_or_none(new_file.blob, _get_raw_size_and_hash_from_blob)):
96182
t_change = self.tr('diff.blob')
97183
n = 8
@@ -109,7 +195,7 @@ def blob_what_mapper(sah: SizeAndHash):
109195
make_hover(old_file.content, new_file.content, lambda t: t.decode('utf8'))
110196
elif old_file.uid != new_file.uid or old_file.gid != new_file.gid:
111197
def format_owner(f: FileInfo):
112-
return RTextBase.format('uid={} gid={}', TextComponents.number(f.uid), TextComponents.number(f.gid))
198+
return RTextBase.format('uid={} gid={}', self.__uid_gid_resolver.uid(f.uid), self.__uid_gid_resolver.gid(f.gid))
113199
t_change = self.tr('diff.owner')
114200
make_hover(format_owner(old_file), format_owner(new_file))
115201
elif old_file.mtime != new_file.mtime:
@@ -125,10 +211,10 @@ def format_owner(f: FileInfo):
125211
t_change = RTextList(t_change, ' (', RTextBase.join(', ', hover_lines), ')')
126212

127213
self.reply(RTextBase.format(
128-
'{} {} {}: {}',
214+
'{} {} {}{}: {}',
129215
RText('[*]', RColor.yellow),
130-
pretty_mode(new_file.mode),
216+
_pretty_mode(new_file.mode),
217+
self.__make_uid_gid_columns(new_file),
131218
RText(old_file.path, TextColors.file),
132219
t_change,
133220
))
134-

0 commit comments

Comments
 (0)