summaryrefslogtreecommitdiff
path: root/src/xpit_/list_.py
diff options
context:
space:
mode:
authorSteven Van Dorp <steven@vandorp.lu>2026-02-01 15:13:01 +0100
committerSteven Van Dorp <steven@vandorp.lu>2026-02-01 15:13:01 +0100
commitc7987858924608f8013e64260efb806e5ddeab9f (patch)
tree06d1a0e487cabf896339386d2c1ecf02f87a540e /src/xpit_/list_.py
Initial commitHEADmaster
Diffstat (limited to 'src/xpit_/list_.py')
-rw-r--r--src/xpit_/list_.py104
1 files changed, 104 insertions, 0 deletions
diff --git a/src/xpit_/list_.py b/src/xpit_/list_.py
new file mode 100644
index 0000000..fe281c2
--- /dev/null
+++ b/src/xpit_/list_.py
@@ -0,0 +1,104 @@
+import os
+from sys import argv, stdout, stderr
+from . import args_helpers, issue, issue_tracker
+
+DEFAULT_ORDER: list[bytes] = [b"ID", b"POST_COUNT", b"TITLE", b"status", b"labels"]
+
+help_text = f"""\
+Usage: xpit list [options]
+Examples:
+ xpit list -d , -f TITLE,POST_COUNT
+Options:
+ -d, --delimiter <str>
+ Delimiter used to separate the fields.
+ Defaults to '|' (pipe).
+ -f, --fields <FIELD,...>
+ Defaults to '{b','.join(DEFAULT_ORDER).decode("utf-8")}'.
+ Guaranteed fields:
+ ID - ID of the issue
+ TITLE - Title of the issue
+ AUTHOR - Author of the issue
+ POST_COUNT - Total number of posts in the issue, including the initial post
+ PATH_ABS - absolute path to issue.md file
+ PATH_REL - relative path to issue.md file
+ Other fields are optional and parsed in issues' INI headers.
+ The fields are printed in order and the same field can be printed more than once.
+ -h, --help
+ Print this help text.
+"""
+
+class Args:
+ def __init__(self) -> None:
+ self.order = DEFAULT_ORDER
+ self.delimiter = '|'
+
+ def parse(self) -> bool:
+ flag_value_map = {
+ "help|h": 0,
+ "delimiter|d": 1,
+ "fields|f": 1,
+ }
+ res = args_helpers.parse_generic(flag_value_map, argv, 2)
+ if res is None:
+ return False
+ if "help" in res:
+ stdout.write(help_text)
+ return False
+ if "delimiter" in res:
+ self.delimiter = res["delimiter"][0]
+ if "fields" in res:
+ self.order = [field.encode("utf-8") for field in res["fields"][0].split(',')]
+
+ return True
+args = Args()
+
+def main() -> int:
+ if len(argv) < 2:
+ stdout.write(help_text)
+ return 1
+
+ if not args.parse():
+ return 1
+
+ cwd = os.getcwd()
+ issue_tracker_dir = issue_tracker.find_dir(cwd)
+ if issue_tracker_dir is None:
+ return 1
+ for file_name in os.listdir(issue_tracker_dir):
+ if not os.path.isdir(os.path.join(issue_tracker_dir, file_name)):
+ continue
+
+ file_path = os.path.join(issue_tracker_dir, file_name, "issue.md")
+ with open(file_path, "rb") as f:
+ src = f.read()
+ info, err = issue.parse(src)
+ if err is not None:
+ stderr.write(err.msg)
+ return 1
+ i = 0
+ while True:
+ key = args.order[i]
+ if key == b"ID":
+ stdout.write(file_name)
+ elif key == b"TITLE":
+ stdout.write(info.title.decode("utf-8"))
+ elif key == b"POST_COUNT":
+ stdout.write(str(info.post_count()))
+ elif key == b"AUTHOR":
+ stdout.write(info.meta[b"author"].decode("utf-8"))
+ elif key == b"PATH_ABS":
+ stdout.write(file_path)
+ elif key == b"PATH_REL":
+ stdout.write(os.path.relpath(file_path, start=cwd))
+ else:
+ if key in info.meta:
+ stdout.write(info.meta[key].decode("utf-8"))
+
+ i += 1
+ if i >= len(args.order):
+ break
+ stdout.write(args.delimiter)
+ stdout.write('\n')
+
+ return 0
+