Skip to content

Commit 4cc2290

Browse files
committed
feat(cli): add show <command> subcommand with mock data
- Implemented `show <command>` subcommand to display description, usage, example, and notes - Added mock data for ls, grep, cat, mkdir - Improved output formatting with colors and sections - Added tests for valid and invalid commands in test_cli.py
1 parent 9646502 commit 4cc2290

File tree

4 files changed

+152
-29
lines changed

4 files changed

+152
-29
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
## USER ADDED
22
.env.secret
33
.vscode/
4+
.idea/
45

56
# MkDocs (no longer used - using Ibis HTML directly)
67
site/

cli/cli.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import typer
2+
from commands import hello, show
23

3-
from commands import hello
4-
5-
# Create the root CLI app
4+
# Root CLI app
65
app = typer.Typer(help="101 Linux Commands CLI 🚀")
76

87
# Register subcommands
98
app.add_typer(hello.app, name="hello")
109

10+
# register show directly as command
11+
app.command()(show.show)
12+
1113
if __name__ == "__main__":
12-
app()
14+
app()

cli/commands/show.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import typer
2+
from typing import Dict, TypedDict
3+
4+
class CommandInfo(TypedDict):
5+
description: str
6+
usage: str
7+
example: str
8+
notes: str
9+
10+
11+
COMMANDS: Dict[str, CommandInfo] = {
12+
"ls": {
13+
"description": (
14+
"List information about the files in the current directory (the default). "
15+
"Options can be used to modify the output format, sort order, and more."
16+
),
17+
"usage": "ls [OPTION]... [FILE]...",
18+
"example": "ls -la /var/log",
19+
"notes": (
20+
"- `-l` : use a long listing format\n"
21+
"- `-a` : show hidden files (starting with `.`)\n"
22+
"- `-h` : with -l, print sizes in human-readable format (e.g., 1K, 234M)"
23+
),
24+
},
25+
"grep": {
26+
"description": (
27+
"Search input files for lines containing a match to the given PATTERN. "
28+
"Often used for filtering log files or searching through code."
29+
),
30+
"usage": "grep [OPTION]... PATTERN [FILE]...",
31+
"example": 'grep -R "TODO" .',
32+
"notes": (
33+
"- `-i` : ignore case distinctions\n"
34+
"- `-R` : recursively search subdirectories\n"
35+
"- `-n` : show line numbers of matches"
36+
),
37+
},
38+
"cat": {
39+
"description": (
40+
"Concatenate files and print on the standard output. "
41+
"Often used to quickly view file contents."
42+
),
43+
"usage": "cat [OPTION]... [FILE]...",
44+
"example": "cat /etc/passwd",
45+
"notes": (
46+
"- `-n` : number all output lines\n"
47+
"- `-b` : number non-blank lines\n"
48+
"- Use with pipes: `cat file.txt | grep pattern`"
49+
),
50+
},
51+
"mkdir": {
52+
"description": (
53+
"Create directories if they do not already exist. "
54+
"By default, it creates a single directory."
55+
),
56+
"usage": "mkdir [OPTION]... DIRECTORY...",
57+
"example": "mkdir -p projects/python/app",
58+
"notes": (
59+
"- `-p` : create parent directories as needed\n"
60+
"- `-v` : print a message for each created directory"
61+
),
62+
},
63+
}
64+
65+
66+
def show(
67+
command: str = typer.Argument(..., help="Linux command to show details for")
68+
) -> None:
69+
"""
70+
Display description, usage, examples, and notes for a given Linux command.
71+
"""
72+
73+
key = command.strip()
74+
info = COMMANDS.get(key)
75+
76+
if not info:
77+
typer.secho(
78+
f"✖ Unknown command '{command}'. Try one of: {', '.join(sorted(COMMANDS))}",
79+
err=True,
80+
fg=typer.colors.RED,
81+
)
82+
raise typer.Exit(code=1)
83+
84+
typer.secho(f"\nCommand: {key}\n", fg=typer.colors.CYAN, bold=True)
85+
86+
typer.secho("Description:", fg=typer.colors.MAGENTA, bold=True)
87+
typer.echo(f" {info['description']}\n")
88+
89+
typer.secho("Usage:", fg=typer.colors.MAGENTA, bold=True)
90+
typer.echo(f" {info['usage']}\n")
91+
92+
typer.secho("Example:", fg=typer.colors.MAGENTA, bold=True)
93+
typer.echo(f" {info['example']}\n")
94+
95+
typer.secho("Notes:", fg=typer.colors.MAGENTA, bold=True)
96+
typer.echo(f"{info['notes']}\n")

cli/test_cli.py

Lines changed: 49 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,58 +5,82 @@
55
import subprocess
66
import sys
77

8+
CLI = [sys.executable, "cli.py"]
9+
CWD = os.path.dirname(__file__)
810

9-
def test_cli_help():
10-
"""Test that the CLI shows help."""
11-
result = subprocess.run(
12-
[sys.executable, "cli.py", "--help"],
11+
12+
def run_cli(args):
13+
"""Helper to run CLI with subprocess."""
14+
return subprocess.run(
15+
CLI + args,
1316
capture_output=True,
1417
text=True,
15-
cwd=os.path.dirname(__file__),
16-
)
18+
cwd=CWD,
19+
)
20+
21+
22+
def test_cli_help():
23+
"""Test that the CLI shows help."""
24+
result = run_cli(["--help"])
1725
assert result.returncode == 0
1826
assert "101 Linux Commands CLI" in result.stdout
1927

2028

2129
def test_hello_command():
2230
"""Test the hello command."""
23-
result = subprocess.run(
24-
[sys.executable, "cli.py", "hello", "greet"],
25-
capture_output=True,
26-
text=True,
27-
cwd=os.path.dirname(__file__),
28-
)
31+
result = run_cli(["hello", "greet"])
2932
assert result.returncode == 0
3033
assert "Hello, World!" in result.stdout
3134

3235

3336
def test_hello_command_with_name():
3437
"""Test the hello command with a custom name."""
35-
result = subprocess.run(
36-
[sys.executable, "cli.py", "hello", "greet", "--name", "Linux"],
37-
capture_output=True,
38-
text=True,
39-
cwd=os.path.dirname(__file__),
40-
)
38+
result = run_cli(["hello", "greet", "--name", "Linux"])
4139
assert result.returncode == 0
4240
assert "Hello, Linux!" in result.stdout
4341

4442

4543
def test_hello_help():
4644
"""Test the hello command help."""
47-
result = subprocess.run(
48-
[sys.executable, "cli.py", "hello", "--help"],
49-
capture_output=True,
50-
text=True,
51-
cwd=os.path.dirname(__file__),
52-
)
45+
result = run_cli(["hello", "--help"])
5346
assert result.returncode == 0
5447
assert "Hello command group" in result.stdout
5548

5649

50+
# ----------------------------
51+
# Tests for `show` subcommand
52+
# ----------------------------
53+
54+
def test_show_ls():
55+
"""Test the show command with ls."""
56+
result = run_cli(["show", "ls"])
57+
assert result.returncode == 0
58+
assert "# ls" in result.stdout or "Command: ls" in result.stdout
59+
assert "List" in result.stdout
60+
61+
62+
def test_show_grep():
63+
"""Test the show command with grep."""
64+
result = run_cli(["show", "grep"])
65+
assert result.returncode == 0
66+
assert "grep" in result.stdout
67+
assert "Search" in result.stdout or "Print" in result.stdout
68+
69+
70+
def test_show_invalid():
71+
"""Test the show command with an invalid command."""
72+
result = run_cli(["show", "foobar"])
73+
assert result.returncode == 1
74+
combined_output = result.stdout + result.stderr
75+
assert "Unknown command" in combined_output
76+
77+
5778
if __name__ == "__main__":
5879
test_cli_help()
5980
test_hello_command()
6081
test_hello_command_with_name()
6182
test_hello_help()
62-
print("✅ All tests passed!")
83+
test_show_ls()
84+
test_show_grep()
85+
test_show_invalid()
86+
print("✅ All tests passed!")

0 commit comments

Comments
 (0)