You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
catcli/catcli/catcli.py

401 lines
12 KiB
Python

7 years ago
#!/usr/bin/env python3
# author: deadc0de6
"""
author: deadc0de6 (https://github.com/deadc0de6)
Copyright (c) 2017, deadc0de6
Catcli command line interface
"""
import sys
import os
import datetime
1 year ago
from typing import Dict, Any, List
7 years ago
from docopt import docopt
# local imports
1 year ago
from catcli import nodes
from catcli.version import __version__ as VERSION
1 year ago
from catcli.nodes import NodeTop, NodeAny
from catcli.logger import Logger
from catcli.colors import Colors
from catcli.catalog import Catalog
from catcli.walker import Walker
from catcli.noder import Noder
from catcli.utils import ask, edit
from catcli.fuser import Fuser
from catcli.exceptions import BadFormatException, CatcliException
7 years ago
NAME = 'catcli'
CUR = os.path.dirname(os.path.abspath(__file__))
CATALOGPATH = f'{NAME}.catalog'
GRAPHPATH = f'/tmp/{NAME}.dot'
7 years ago
SEPARATOR = '/'
WILD = '*'
FORMATS = ['native', 'csv', 'csv-with-header', 'fzf-native', 'fzf-csv']
7 years ago
BANNER = f""" +-+-+-+-+-+-+
7 years ago
|c|a|t|c|l|i|
+-+-+-+-+-+-+ v{VERSION}"""
7 years ago
USAGE = f"""
{BANNER}
7 years ago
Usage:
{NAME} ls [--catalog=<path>] [--format=<fmt>] [-aBCrVSs] [<path>]
1 year ago
{NAME} find [--catalog=<path>] [--format=<fmt>]
[-aBCbdVsP] [--path=<path>] [<term>]
{NAME} index [--catalog=<path>] [--meta=<meta>...]
[-aBCcfnV] <name> <path>
{NAME} update [--catalog=<path>] [-aBCcfnV] [--lpath=<path>] <name> <path>
{NAME} mount [--catalog=<path>] [-V] <mountpoint>
{NAME} rm [--catalog=<path>] [-BCfV] <storage>
{NAME} rename [--catalog=<path>] [-BCfV] <storage> <name>
{NAME} edit [--catalog=<path>] [-BCfV] <storage>
{NAME} graph [--catalog=<path>] [-BCV] [<path>]
{NAME} print_supported_formats
{NAME} help
{NAME} --help
{NAME} --version
7 years ago
Options:
--catalog=<path> Path to the catalog [default: {CATALOGPATH}].
--meta=<meta> Additional attribute to store [default: ].
-a --archive Handle archive file [default: False].
-B --no-banner Do not display the banner [default: False].
-b --script Output script to manage found file(s) [default: False].
-C --no-color Do not output colors [default: False].
-c --hash Calculate md5 hash [default: False].
-d --directory Only directory [default: False].
1 year ago
-F --format=<fmt> see \"print_supported_formats\" [default: native].
-f --force Do not ask when updating the catalog [default: False].
-l --lpath=<path> Path where changes are logged [default: ]
-n --no-subsize Do not store size of directories [default: False].
-P --parent Ignore stored relpath [default: True].
-p --path=<path> Start path.
-r --recursive Recursive [default: False].
1 year ago
-s --raw-size Print raw size [default: False].
-S --sortsize Sort by size, largest first [default: False].
-V --verbose Be verbose [default: False].
-v --version Show version.
-h --help Show this screen.
""" # nopep8
7 years ago
1 year ago
def cmd_mount(args: Dict[str, Any],
top: NodeTop,
1 year ago
noder: Noder) -> None:
"""mount action"""
mountpoint = args['<mountpoint>']
1 year ago
debug = args['--verbose']
Fuser(mountpoint, top, noder,
debug=debug)
1 year ago
def cmd_index(args: Dict[str, Any],
noder: Noder,
catalog: Catalog,
top: NodeTop) -> None:
2 years ago
"""index action"""
7 years ago
path = args['<path>']
name = args['<name>']
2 years ago
usehash = args['--hash']
4 years ago
debug = args['--verbose']
subsize = not args['--no-subsize']
7 years ago
if not os.path.exists(path):
Logger.err(f'\"{path}\" does not exist')
return
7 years ago
if name in noder.get_storage_names(top):
try:
if not ask(f'Overwrite storage \"{name}\"'):
Logger.err(f'storage named \"{name}\" already exist')
return
except KeyboardInterrupt:
Logger.err('aborted')
return
node = noder.get_storage_node(top, name)
1 year ago
Logger.debug(f'top node: {top}')
Logger.debug(f'storage node: {node}')
node.parent = None
3 years ago
7 years ago
start = datetime.datetime.now()
2 years ago
walker = Walker(noder, usehash=usehash, debug=debug)
attr = args['--meta']
root = noder.new_storage_node(name, path, top, attr)
_, cnt = walker.index(path, root, name)
7 years ago
if subsize:
2 years ago
noder.rec_size(root, store=True)
7 years ago
stop = datetime.datetime.now()
diff = stop - start
Logger.info(f'Indexed {cnt} file(s) in {diff}')
if cnt > 0:
catalog.save(top)
7 years ago
1 year ago
def cmd_update(args: Dict[str, Any],
noder: Noder,
catalog: Catalog,
top: NodeTop) -> None:
2 years ago
"""update action"""
path = args['<path>']
name = args['<name>']
2 years ago
usehash = args['--hash']
logpath = args['--lpath']
4 years ago
debug = args['--verbose']
subsize = not args['--no-subsize']
if not os.path.exists(path):
Logger.err(f'\"{path}\" does not exist')
return
root = noder.get_storage_node(top, name, newpath=path)
if not root:
Logger.err(f'storage named \"{name}\" does not exist')
return
start = datetime.datetime.now()
2 years ago
walker = Walker(noder, usehash=usehash, debug=debug,
logpath=logpath)
cnt = walker.reindex(path, root, top)
if subsize:
2 years ago
noder.rec_size(root, store=True)
stop = datetime.datetime.now()
diff = stop - start
Logger.info(f'updated {cnt} file(s) in {diff}')
if cnt > 0:
catalog.save(top)
1 year ago
def cmd_ls(args: Dict[str, Any],
noder: Noder,
top: NodeTop) -> List[NodeAny]:
2 years ago
"""ls action"""
7 years ago
path = args['<path>']
if not path:
path = SEPARATOR
if not path.startswith(SEPARATOR):
path = SEPARATOR + path
# prepend with top node path
1 year ago
pre = f'{SEPARATOR}{nodes.NAME_TOP}'
7 years ago
if not path.startswith(pre):
path = pre + path
# ensure ends with a separator
7 years ago
if not path.endswith(SEPARATOR):
path += SEPARATOR
# add wild card
7 years ago
if not path.endswith(WILD):
path += WILD
2 years ago
fmt = args['--format']
if fmt.startswith('fzf'):
2 years ago
raise BadFormatException('fzf is not supported in ls, use find')
1 year ago
found = noder.list(top,
path,
rec=args['--recursive'],
2 years ago
fmt=fmt,
raw=args['--raw-size'])
if not found:
path = args['<path>']
Logger.err(f'\"{path}\": nothing found')
return found
7 years ago
1 year ago
def cmd_rm(args: Dict[str, Any],
noder: Noder,
catalog: Catalog,
top: NodeTop) -> NodeTop:
2 years ago
"""rm action"""
name = args['<storage>']
node = noder.get_storage_node(top, name)
if node:
7 years ago
node.parent = None
if catalog.save(top):
Logger.info(f'Storage \"{name}\" removed')
7 years ago
else:
Logger.err(f'Storage named \"{name}\" does not exist')
7 years ago
return top
1 year ago
def cmd_find(args: Dict[str, Any],
noder: Noder,
top: NodeTop) -> List[NodeAny]:
2 years ago
"""find action"""
fromtree = args['--parent']
directory = args['--directory']
startpath = args['--path']
fmt = args['--format']
raw = args['--raw-size']
script = args['--script']
search_for = args['<term>']
1 year ago
found = noder.find_name(top, search_for,
script=script,
startnode=startpath,
only_dir=directory,
parentfromtree=fromtree,
fmt=fmt, raw=raw)
return found
7 years ago
1 year ago
def cmd_graph(args: Dict[str, Any],
noder: Noder,
top: NodeTop) -> None:
2 years ago
"""graph action"""
7 years ago
path = args['<path>']
if not path:
path = GRAPHPATH
cmd = noder.to_dot(top, path)
Logger.info(f'create graph with \"{cmd}\" (you need graphviz)')
7 years ago
1 year ago
def cmd_rename(args: Dict[str, Any],
catalog: Catalog,
top: NodeTop) -> None:
2 years ago
"""rename action"""
storage = args['<storage>']
new = args['<name>']
storages = list(x.name for x in top.children)
if storage in storages:
node = next(filter(lambda x: x.name == storage, top.children))
node.name = new
if catalog.save(top):
2 years ago
msg = f'Storage \"{storage}\" renamed to \"{new}\"'
Logger.info(msg)
else:
Logger.err(f'Storage named \"{storage}\" does not exist')
1 year ago
def cmd_edit(args: Dict[str, Any],
noder: Noder,
catalog: Catalog,
top: NodeTop) -> None:
2 years ago
"""edit action"""
storage = args['<storage>']
storages = list(x.name for x in top.children)
if storage in storages:
node = next(filter(lambda x: x.name == storage, top.children))
attr = node.attr
if not attr:
attr = ''
new = edit(attr)
node.attr = noder.attrs_to_string(new)
if catalog.save(top):
Logger.info(f'Storage \"{storage}\" edited')
else:
Logger.err(f'Storage named \"{storage}\" does not exist')
1 year ago
def banner() -> None:
2 years ago
"""print banner"""
2 years ago
Logger.stderr_nocolor(BANNER)
Logger.stderr_nocolor("")
7 years ago
1 year ago
def print_supported_formats() -> None:
2 years ago
"""print all supported formats to stdout"""
print('"native" : native format')
print('"csv" : CSV format')
print(f' {Noder.CSV_HEADER}')
2 years ago
print('"fzf-native" : fzf to native (only for find)')
print('"fzf-csv" : fzf to csv (only for find)')
1 year ago
def main() -> bool:
2 years ago
"""entry point"""
7 years ago
args = docopt(USAGE, version=VERSION)
3 years ago
if args['help'] or args['--help']:
print(USAGE)
return True
if args['print_supported_formats']:
print_supported_formats()
return True
# check format
fmt = args['--format']
if fmt not in FORMATS:
Logger.err(f'bad format: {fmt}')
print_supported_formats()
return False
if args['--verbose']:
print(args)
7 years ago
# print banner
if not args['--no-banner']:
banner()
7 years ago
# set colors
if args['--no-color']:
2 years ago
Colors.no_color()
7 years ago
# init noder
noder = Noder(debug=args['--verbose'], sortsize=args['--sortsize'],
arc=args['--archive'])
7 years ago
# init catalog
2 years ago
catalog_path = args['--catalog']
catalog = Catalog(catalog_path, debug=args['--verbose'],
7 years ago
force=args['--force'])
# init top node
top = catalog.restore()
if not top:
top = noder.new_top_node()
# handle the meta node
4 years ago
meta = noder.update_metanode(top)
catalog.set_metanode(meta)
7 years ago
# parse command
2 years ago
try:
if args['index']:
cmd_index(args, noder, catalog, top)
if args['update']:
2 years ago
if not catalog.exists():
Logger.err(f'no such catalog: {catalog_path}')
return False
2 years ago
cmd_update(args, noder, catalog, top)
elif args['find']:
2 years ago
if not catalog.exists():
Logger.err(f'no such catalog: {catalog_path}')
return False
2 years ago
cmd_find(args, noder, top)
elif args['ls']:
2 years ago
if not catalog.exists():
Logger.err(f'no such catalog: {catalog_path}')
return False
2 years ago
cmd_ls(args, noder, top)
elif args['mount']:
if not catalog.exists():
Logger.err(f'no such catalog: {catalog_path}')
return False
cmd_mount(args, top, noder)
2 years ago
elif args['rm']:
2 years ago
if not catalog.exists():
Logger.err(f'no such catalog: {catalog_path}')
return False
2 years ago
cmd_rm(args, noder, catalog, top)
elif args['graph']:
2 years ago
if not catalog.exists():
Logger.err(f'no such catalog: {catalog_path}')
return False
2 years ago
cmd_graph(args, noder, top)
elif args['rename']:
2 years ago
if not catalog.exists():
Logger.err(f'no such catalog: {catalog_path}')
return False
2 years ago
cmd_rename(args, catalog, top)
elif args['edit']:
2 years ago
if not catalog.exists():
Logger.err(f'no such catalog: {catalog_path}')
return False
2 years ago
cmd_edit(args, noder, catalog, top)
2 years ago
except CatcliException as exc:
2 years ago
Logger.stderr_nocolor('ERROR ' + str(exc))
2 years ago
return False
7 years ago
return True
if __name__ == '__main__':
if main():
sys.exit(0)
sys.exit(1)