Coverage for kryptic_cypher/app.py: 84%
64 statements
« prev ^ index » next coverage.py v7.6.7, created at 2024-11-23 22:56 +0000
« prev ^ index » next coverage.py v7.6.7, created at 2024-11-23 22:56 +0000
1"""
2Module that contains the click entrypoint for our cli interface.
4This is currently only for encoding and decoding data using the encode and decode commands.
5"""
7import base64
8from enum import Enum
9import functools
10from io import BytesIO
11import shutil
12from typing import IO, Callable
13import click
14from .cypher import Cypher, CypherWithKey, registered_cyphers
17@click.group()
18@click.pass_context
19def main(ctx: click.Context):
20 """
21 main group that represents the top-level: ***zombie-nomnom***
23 This will be used to decorate sub-commands for zombie-nomnom.
25 ***Example Usage:***
26 ```python
27 @main.command("sub-command")
28 def sub_command():
29 # do actual meaningful work.
30 pass
31 ```
32 """
35def resolve_cypher(
36 cypher: str,
37 text: str,
38 input: str,
39 key: str,
40) -> Cypher | CypherWithKey:
41 if not text and not input:
42 raise click.ClickException("You must specify either -t or -i")
44 cypher_instance = registered_cyphers[cypher]
45 if isinstance(cypher_instance, CypherWithKey):
46 if not key:
47 raise click.ClickException("You must specify -k")
48 result = cypher_instance.validate_key(key)
49 if not result.success:
50 raise click.ClickException("\n".join(result.messages))
52 return cypher_instance
55def process_output(output: str | None, encoded_text: IO):
56 if output:
57 flags = "wb" if getattr(encoded_text, "mode", None) == "b" else "w"
58 with open(output, flags) as f:
59 shutil.copyfileobj(encoded_text, f)
60 else:
61 if getattr(encoded_text, "mode", None) == "b":
62 full_value = encoded_text.read()
63 binary_string = base64.b64encode(full_value).decode("utf-8")
64 click.echo(binary_string)
65 else:
66 click.echo(encoded_text.read())
69@main.command("encode")
70@click.option(
71 "-c",
72 "--cypher",
73 help="The cypher to use",
74 required=True,
75 type=click.Choice(list(registered_cyphers.keys())),
76)
77@click.option("-t", "--text", help="The text to encode", required=False)
78@click.option("-k", "--key", help="The input file to read text from", required=False)
79@click.option("-i", "--input", help="The input file to read text from", required=False)
80@click.option(
81 "-o",
82 "--output",
83 help="The output file to write text to",
84 required=False,
85 type=click.Path(writable=True, dir_okay=False),
86)
87@click.option(
88 "-b",
89 "--binary",
90 help="The output file to write text to",
91 required=False,
92 is_flag=True,
93)
94def encode(
95 cypher: str,
96 text: str,
97 input: str,
98 binary: bool,
99 key: str,
100 output: str | None,
101):
102 """
103 CLI command to encode text using a cypher in our system that will check to make sure the usage is valid i.e. input is given and key is valid if key is required.
104 """
105 cypher_instance = resolve_cypher(cypher, text, input, key)
107 if input:
108 with open(input, "rb" if binary else "r") as f:
109 text = f.read()
111 if isinstance(cypher_instance, CypherWithKey):
112 encoded_text = cypher_instance.encode(text, key)
113 else:
114 encoded_text = cypher_instance.encode(text)
116 process_output(output, encoded_text)
119@main.command("decode")
120@click.option(
121 "-c",
122 "--cypher",
123 help="The cypher to use",
124 required=True,
125 type=click.Choice(list(registered_cyphers.keys())),
126)
127@click.option("-t", "--text", help="The text to encode", required=False)
128@click.option("-i", "--input", help="The input file to read text from", required=False)
129@click.option("-k", "--key", help="The input file to read text from", required=False)
130@click.option(
131 "-o",
132 "--output",
133 help="The output file to write text to",
134 required=False,
135 type=click.Path(writable=True, dir_okay=False),
136)
137@click.option(
138 "-b",
139 "--binary",
140 help="The output file to write text to",
141 required=False,
142 is_flag=True,
143)
144def decode(
145 cypher: str,
146 text: str,
147 input: str,
148 key: str,
149 output: str,
150 binary: bool,
151):
152 """
153 CLI command to decode text using a cypher in our system that will check to make sure the usage is valid i.e. input is given and key is valid if key is required.
154 """
155 cypher_instance = resolve_cypher(cypher, text, input, key)
156 if input:
157 with open(input, "rb" if binary else "r") as f:
158 text = f.read()
160 if isinstance(cypher_instance, CypherWithKey):
161 encoded_text = cypher_instance.decode(text, key)
162 else:
163 encoded_text = cypher_instance.decode(text)
165 process_output(output, encoded_text)