diff --git a/pyproject.toml b/pyproject.toml index 77103ad..921cfb0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,6 +33,9 @@ dependencies = [ "markdown-it-py>=3.0.0" ] +[project.scripts] +slackify-markdown = "slackify_markdown.cli:main" + [project.urls] "Homepage" = "https://github.com/thesmallstar/slackify-markdown-python" "Bug Tracker" = "https://github.com/thesmallstar/slackify-markdown-python/issues" diff --git a/readme.md b/readme.md index 0bcab0a..d4fe01b 100644 --- a/readme.md +++ b/readme.md @@ -10,10 +10,16 @@ This library converts regular Markdown to Slack-specific markdown, making it eas ## Installation +**With pip** ```bash pip install slackify-markdown ``` +**With uv** +```bash +uv add slackify-markdown +``` + ## Usage ```python @@ -54,6 +60,16 @@ This is *bold text* and this is _italic text_. """ ``` +## Command-line usage + +After installation, use the `slackify-markdown` command to convert a file or stdin: + +```bash +slackify-markdown README.md +slackify-markdown README.md --output README.slack.md +cat README.md | slackify-markdown +``` + ## Features - Converts headers to Slack-compatible bold text @@ -89,4 +105,4 @@ This project is licensed under the MIT License - see the LICENSE file for detail ## Acknowledgements -Shoutout to [jsarafaj](https://github.com/jsarafajr), his JS library has inspired the code and tests for this package. \ No newline at end of file +Shoutout to [jsarafaj](https://github.com/jsarafajr), his JS library has inspired the code and tests for this package. diff --git a/src/slackify_markdown/cli.py b/src/slackify_markdown/cli.py new file mode 100644 index 0000000..78982fd --- /dev/null +++ b/src/slackify_markdown/cli.py @@ -0,0 +1,78 @@ +from __future__ import annotations + +import argparse +import sys +from pathlib import Path +from typing import Sequence, TextIO + +from .service import slackify_markdown + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + prog="slackify-markdown", + description="Convert Markdown to Slack mrkdwn.", + ) + parser.add_argument( + "input", + nargs="?", + type=Path, + help="Markdown file to convert. Reads stdin when omitted or set to '-'.", + ) + parser.add_argument( + "-o", + "--output", + type=Path, + help="Write output to this file instead of stdout.", + ) + return parser + + +def read_markdown(input_path: Path | None, stdin: TextIO) -> str: + if input_path is None or str(input_path) == "-": + return stdin.read() + + try: + return input_path.read_text(encoding="utf-8") + except OSError as exc: + raise RuntimeError(f"cannot read {input_path}: {exc}") from exc + + +def write_output(slack_text: str, output_path: Path | None, stdout: TextIO) -> None: + if output_path is None: + stdout.write(slack_text) + if slack_text and not slack_text.endswith("\n"): + stdout.write("\n") + return + + try: + output_path.write_text(slack_text, encoding="utf-8") + except OSError as exc: + raise RuntimeError(f"cannot write {output_path}: {exc}") from exc + + +def run( + argv: Sequence[str] | None = None, + *, + stdin: TextIO = sys.stdin, + stdout: TextIO = sys.stdout, + stderr: TextIO = sys.stderr, +) -> int: + args = build_parser().parse_args(argv) + + try: + markdown = read_markdown(args.input, stdin) + write_output(slackify_markdown(markdown), args.output, stdout) + except RuntimeError as exc: + print(f"slackify-markdown: {exc}", file=stderr) + return 1 + + return 0 + + +def main() -> None: + raise SystemExit(run()) + + +if __name__ == "__main__": + main() diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 0000000..26c58e1 --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,35 @@ +from io import StringIO +from pathlib import Path + +import pytest + +from slackify_markdown.cli import run + + +def test_cli_reads_stdin() -> None: + stdout = StringIO() + + exit_code = run([], stdin=StringIO("[Link](https://example.com)"), stdout=stdout) + + assert exit_code == 0 + assert stdout.getvalue() == "\n" + + +def test_cli_reads_and_writes_files(tmp_path: Path) -> None: + input_path = tmp_path / "input.md" + output_path = tmp_path / "output.txt" + input_path.write_text("*italic*", encoding="utf-8") + + exit_code = run([str(input_path), "--output", str(output_path)]) + + assert exit_code == 0 + assert output_path.read_text(encoding="utf-8") == "_italic_\n" + + +def test_cli_reports_missing_input() -> None: + stderr = StringIO() + + exit_code = run(["missing.md"], stderr=stderr) + + assert exit_code == 1 + assert "cannot read missing.md" in stderr.getvalue()