162 lines
4.9 KiB
Python
162 lines
4.9 KiB
Python
__add__ = [ "diff" ]
|
|
|
|
from collections.abc import Set
|
|
from os import listdir, readlink
|
|
from os.path import abspath
|
|
from typing import Callable, Final
|
|
from collections.abc import Iterable
|
|
|
|
from .mytypes import *
|
|
from .Config import *
|
|
from .utils import *
|
|
from .whatsthispath import *
|
|
from .utils import *
|
|
|
|
|
|
class DiffResult:
|
|
def __init__(self, path:str) -> None:
|
|
self.path:Final[str] = path
|
|
self.differs:set[PathAndType] = set()
|
|
self.same:set[PathAndType] = set()
|
|
self.differentTypes:set[tuple[str,PathType,PathType]] = set()
|
|
self.presentOnlyInFirst:set[PathAndType] = set()
|
|
self.presentOnlyInSecond:set[PathAndType] = set()
|
|
self.weird:set[str] = set()
|
|
self.subresults:dict[str,DiffResult] = {}
|
|
|
|
def simplify(self, *, deep:bool) -> None:
|
|
for l in [
|
|
self.differs, self.same, self.presentOnlyInFirst,
|
|
self.presentOnlyInSecond
|
|
]:
|
|
mapSet(l, ( lambda v: ( v[0], simplifyPath(v[1]) ) ))
|
|
mapSet(self.differentTypes, (
|
|
lambda v: ( simplifyPath(v[0]), *v[1:] )
|
|
))
|
|
mapSet(self.weird, simplifyPath)
|
|
if deep:
|
|
for v in self.subresults.values():
|
|
v.simplify(deep=True)
|
|
|
|
def print(self, source:str|None = None, target:str|None = None) -> None:
|
|
reprSource = repr(source) if source is not None else "the source directory"
|
|
reprTarget = repr(target) if target is not None else "the target directory"
|
|
|
|
def reprPathAndType(v:PathAndType) -> str:
|
|
return f"{lineForPathAndType(v)}"
|
|
def printWith[T](label:str, what:set[T], f:Callable[[T],str]):
|
|
if len(what) == 0:
|
|
return
|
|
print(label + ":")
|
|
for v in what:
|
|
print(f"-\t{f(v)}")
|
|
title(f"for path {repr(self.path)}")
|
|
|
|
printWith("same", self.same, reprPathAndType)
|
|
|
|
printWith("different", self.differs, reprPathAndType)
|
|
|
|
printWith("different types", self.differentTypes,
|
|
lambda v: f"{reprPathType(v[1])} / {reprPathType(v[2])}: {v[0]}"
|
|
)
|
|
|
|
printWith(f"exist only in {reprSource}",
|
|
self.presentOnlyInFirst, reprPathAndType
|
|
)
|
|
|
|
printWith(f"exist only in {reprTarget}",
|
|
self.presentOnlyInSecond, reprPathAndType
|
|
)
|
|
|
|
printWith("dunno what that is lol", self.weird,
|
|
lambda v: v
|
|
)
|
|
|
|
|
|
def diff(pathA:str, pathB:str, *, ignored:Set[PathAndType]) -> DiffResult:
|
|
r = _diffDirs(abspath(pathA), abspath(pathB), ignored, "/", trust=False)
|
|
return r
|
|
|
|
|
|
def _diffDirs(
|
|
absPathA:str, absPathB:str, absIgnoredPaths:Set[PathAndType],
|
|
relCurrentPath:str, *, trust:bool
|
|
) -> DiffResult:
|
|
|
|
absCurrentPathA = absPathA + "/" + relCurrentPath
|
|
absCurrentPathB = absPathB + "/" + relCurrentPath
|
|
absCurrentPathA_what = whatsthispath(absCurrentPathA)
|
|
absCurrentPathB_what = whatsthispath(absCurrentPathB)
|
|
|
|
if not trust:
|
|
assert absPathA[0] == "/", "'pathA' should be an absolute path."
|
|
assert absPathB[0] == "/", "'pathB' should be an absolute path."
|
|
assert relCurrentPath[0] == "/", (
|
|
"'relCurrentPath' should start with '/'."
|
|
)
|
|
assert all([ v[1][0] == "/" for v in absIgnoredPaths ]), (
|
|
"'ignoredPaths' must contain only absolute paths."
|
|
)
|
|
assert absCurrentPathA_what == 'd', "'pathA' should be a directory."
|
|
assert absCurrentPathB_what == 'd', "'pathB' should be a directory."
|
|
|
|
r = DiffResult(relCurrentPath)
|
|
|
|
for name in set(listdir(absCurrentPathA)) | set(listdir(absCurrentPathB)):
|
|
relNewPath = relCurrentPath + "/" + name
|
|
absNewPathA = absCurrentPathA + "/" + name
|
|
absNewPathB = absCurrentPathB + "/" + name
|
|
absNewPathA_what = whatsthispath(absNewPathA)
|
|
absNewPathB_what = whatsthispath(absNewPathB)
|
|
if absNewPathA_what is None:
|
|
assert absNewPathB_what is not None
|
|
r.presentOnlyInSecond.add((absNewPathB_what, relNewPath))
|
|
elif absNewPathB_what is None:
|
|
r.presentOnlyInFirst.add((absNewPathA_what, relNewPath))
|
|
elif absNewPathA_what != absNewPathB_what:
|
|
r.differentTypes.add(
|
|
(relNewPath, absNewPathA_what, absNewPathB_what)
|
|
)
|
|
else:
|
|
_diffOne(r,
|
|
absPathA, absPathB, absIgnoredPaths, relNewPath, name,
|
|
absNewPathA_what
|
|
)
|
|
|
|
r.simplify(deep=False)
|
|
return r
|
|
|
|
|
|
def _diffOne(r:DiffResult, absPathA:str, absPathB:str, absIgnoredPaths:Set[PathAndType],
|
|
relCurrentPath:str, filename:str, relCurrentPath_what:PathType
|
|
) -> None:
|
|
match relCurrentPath_what:
|
|
case "d":
|
|
rr = _diffDirs(absPathA, absPathB, absIgnoredPaths, relCurrentPath, trust=False) # TODO(debug) add trust=True
|
|
r.subresults[filename] = rr
|
|
if (len(rr.differentTypes) > 0
|
|
or len(rr.presentOnlyInFirst) > 0
|
|
or len(rr.presentOnlyInSecond) > 0
|
|
or len(rr.differs) > 0
|
|
):
|
|
r.differs.add( (relCurrentPath_what, filename) )
|
|
elif len(rr.weird) > 0:
|
|
r.weird.add( filename )
|
|
else:
|
|
r.same.add( ( relCurrentPath_what, filename ) )
|
|
|
|
case "f"|"l":
|
|
getData:Callable[[str],str|bytes] = (
|
|
(lambda path: open(path, "rb").read())
|
|
if relCurrentPath_what == "f" else
|
|
(lambda path: readlink(path))
|
|
)
|
|
(
|
|
r.same if (
|
|
getData(absPathA + "/" + relCurrentPath)
|
|
== getData(absPathB + "/" + relCurrentPath)
|
|
) else r.differs
|
|
).add( (relCurrentPath_what, filename) )
|
|
|
|
case "w":
|
|
r.weird.add( filename )
|