Init: Basic project structure and functionality.
This commit is contained in:
commit
8715c69e89
13 changed files with 503 additions and 0 deletions
0
fck/__init__.py
Normal file
0
fck/__init__.py
Normal file
32
fck/checker.py
Normal file
32
fck/checker.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
from typing import Generator, List, Tuple, Optional
|
||||
from .file import FILE
|
||||
import re
|
||||
|
||||
|
||||
def checkmark(value: Optional[bool] = None) -> str:
|
||||
"""
|
||||
Takes optional bool and returns colored string.
|
||||
"""
|
||||
return {
|
||||
True: '\033[92m✓\033[0m',
|
||||
False: '\033[91m❌\033[0m',
|
||||
None: '\033[33m?\033[0m'
|
||||
}[value]
|
||||
|
||||
|
||||
def check(f: FILE, largefile: bool = False) -> bool:
|
||||
"""
|
||||
Check given file and return checked file.
|
||||
"""
|
||||
f.csum.reset()
|
||||
try:
|
||||
with open(f.fpath, 'rb') as file:
|
||||
if not largefile:
|
||||
f.csum.gensum(file.read())
|
||||
else:
|
||||
for line in file:
|
||||
f.csum.gensum(line)
|
||||
except FileNotFoundError:
|
||||
print(f"[WARN]: No such file or directory: {f.fpath}")
|
||||
print(f"{checkmark(f.verify())} \t {f.csum} \t {f.esum} \t {f.fname}")
|
||||
return f.verify()
|
18
fck/cktype/__init__.py
Normal file
18
fck/cktype/__init__.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
from re import search
|
||||
from typing import Optional
|
||||
from .crc32 import CRC32
|
||||
|
||||
CKTYPES = [
|
||||
(CRC32)
|
||||
]
|
||||
|
||||
def resolve(fname: str, esum: Optional[str] = None, cstype: Optional[str] = None):
|
||||
"""
|
||||
Checks fname input for checksum pattern and returns first match.
|
||||
Can be overridden with cstype.
|
||||
If neither is applicable the first possible checksum type will be returned.
|
||||
"""
|
||||
if esum is None and (match := CKTYPES[0].REGEX.search(fname)):
|
||||
esum = match.group(0)
|
||||
|
||||
return CKTYPES[0](), esum
|
29
fck/cktype/crc32.py
Normal file
29
fck/cktype/crc32.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
from typing import Generator, List, Tuple, Optional
|
||||
from re import compile, Pattern
|
||||
from zlib import crc32
|
||||
|
||||
|
||||
class CRC32(object):
|
||||
NAME: str = "CRC32"
|
||||
EXT: List[str] = [".sfv"]
|
||||
SYNTAX: List[Pattern] = [
|
||||
compile(r'^;*$'),
|
||||
compile(r'^.* [0-9a-fA-F]{8}$')
|
||||
]
|
||||
REGEX: Pattern = compile(r'[0-9a-fA-F]{8}')
|
||||
|
||||
def __init__(self):
|
||||
self.cksum: int = 0
|
||||
|
||||
def gensum(self, data: bytes):
|
||||
self.cksum = crc32(data, self.cksum)
|
||||
|
||||
def reset(self):
|
||||
self.__init__()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
cstring = str(hex(self.cksum))[2:].upper()
|
||||
return ''.join([((8 - len(cstring)) * "0"), cstring])
|
||||
|
||||
def __eq__(self, other) -> bool:
|
||||
return self.__repr__() == other.upper()
|
18
fck/file.py
Normal file
18
fck/file.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
from typing import Optional
|
||||
from os import path
|
||||
from . import cktype
|
||||
|
||||
|
||||
class FILE:
|
||||
def __init__(self, fpath: str, esum: Optional[str] = None):
|
||||
self.fpath = fpath
|
||||
self.fname = path.basename(fpath)
|
||||
self.csum, self.esum = cktype.resolve(self.fname, esum)
|
||||
|
||||
def __repr__(self):
|
||||
return self.fname
|
||||
|
||||
def verify(self) -> Optional[bool]:
|
||||
if self.esum is None:
|
||||
return None
|
||||
return self.csum.__repr__() == self.esum
|
65
fck/fileutils.py
Normal file
65
fck/fileutils.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
from typing import Generator, List, Tuple
|
||||
from os import listdir, path
|
||||
from .file import FILE
|
||||
|
||||
|
||||
def search(pathlist: List[str]) -> Generator[FILE, None, None]:
|
||||
"""
|
||||
Generate file paths from given list of Paths.
|
||||
|
||||
+------------+
|
||||
| Parameters |
|
||||
+------------+
|
||||
| pathlist: List[str]
|
||||
| List of files and directories.
|
||||
|
||||
+--------+
|
||||
| Yields |
|
||||
---------+
|
||||
| file: Tuple[(None, str)]
|
||||
| file is a Tuple containing:
|
||||
| - a files path string
|
||||
| - a crc32 sum (None if unknown)
|
||||
"""
|
||||
for fpath in pathlist:
|
||||
if path.isfile(fpath):
|
||||
if len(fpath) > 4 and fpath[-4:] == ".sfv":
|
||||
yield from sfv_read(fpath)
|
||||
else:
|
||||
yield FILE(path.realpath(fpath))
|
||||
continue
|
||||
if path.isdir(fpath):
|
||||
yield from search([path.join(fpath, x) for x in listdir(fpath)])
|
||||
continue
|
||||
print(f"[WARN]: No such file or directory: {fpath}")
|
||||
|
||||
|
||||
def sfv_read(filename: str) -> Generator[FILE, None, None]:
|
||||
"""
|
||||
Read sfv file.
|
||||
"""
|
||||
try:
|
||||
with open(filename, 'r') as file:
|
||||
yield from (FILE(' '.join(x.split()[:-1]), x.split()[-1])
|
||||
for x in file.read().split('\n')
|
||||
if len(x) != 0 and x[0] != ';')
|
||||
except UnicodeDecodeError:
|
||||
print(f"[ERR]: {filename} is not a text file.")
|
||||
except FileNotFoundError:
|
||||
print(f"[WARN]: No such file or directory: {filename}")
|
||||
|
||||
|
||||
def sfv_write(checked_files: List[Tuple[str, str, bool]], filename: str) -> None:
|
||||
"""
|
||||
Write sfv file.
|
||||
"""
|
||||
try:
|
||||
with open(filename, 'w') as file:
|
||||
checked_files.sort()
|
||||
if any([not x[2] for x in checked_files]):
|
||||
print(f"[WARN]: {filename} will contain unverified checksums.")
|
||||
# [file.write(f"{str(x[0])}\t{str(x[1])}") for x in checked_files]
|
||||
|
||||
file.write('\n'.join([x for x in ["hello" "world"]]))
|
||||
except FileExistsError:
|
||||
pass
|
Loading…
Add table
Add a link
Reference in a new issue