lab2
This commit is contained in:
16
Cargo.lock
generated
16
Cargo.lock
generated
@@ -141,6 +141,12 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encode_unicode"
|
name = "encode_unicode"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
@@ -176,6 +182,7 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
"dialoguer",
|
"dialoguer",
|
||||||
|
"itertools",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -184,6 +191,15 @@ version = "1.70.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
|||||||
@@ -7,3 +7,4 @@ edition = "2021"
|
|||||||
anyhow = "1.0.86"
|
anyhow = "1.0.86"
|
||||||
clap = { version = "4.5.17", features = ["derive"] }
|
clap = { version = "4.5.17", features = ["derive"] }
|
||||||
dialoguer = "0.11.0"
|
dialoguer = "0.11.0"
|
||||||
|
itertools = "0.13.0"
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
use crate::util;
|
|
||||||
|
|
||||||
pub struct AtabashChipher {
|
|
||||||
alphabet: Vec<char>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AtabashChipher {
|
|
||||||
pub fn new(alphabet: &str) -> anyhow::Result<Self> {
|
|
||||||
let alphabet = alphabet.chars().collect::<Vec<_>>();
|
|
||||||
|
|
||||||
util::verify_alphabet(&alphabet)?;
|
|
||||||
|
|
||||||
Ok(Self { alphabet })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn encode(&self, input: &str) -> anyhow::Result<String> {
|
|
||||||
let mut output = String::with_capacity(input.len());
|
|
||||||
|
|
||||||
for c in input.chars() {
|
|
||||||
let index = self
|
|
||||||
.alphabet
|
|
||||||
.iter()
|
|
||||||
.position(|&x| x == c)
|
|
||||||
.ok_or(anyhow::anyhow!("cannot encode character {:?}", c))?;
|
|
||||||
output.push(self.alphabet[self.alphabet.len() - index - 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(output)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn decode(&self, input: &str) -> anyhow::Result<String> {
|
|
||||||
self.encode(input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
use crate::util;
|
|
||||||
|
|
||||||
pub struct CaesarCipher {
|
|
||||||
alphabet: Vec<char>,
|
|
||||||
offset: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CaesarCipher {
|
|
||||||
pub fn new(alphabet: &str, offset: i64) -> anyhow::Result<Self> {
|
|
||||||
let alphabet = alphabet.chars().collect::<Vec<_>>();
|
|
||||||
let offset = offset % alphabet.len() as i64;
|
|
||||||
|
|
||||||
util::verify_alphabet(&alphabet)?;
|
|
||||||
|
|
||||||
Ok(Self { alphabet, offset })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn encode(&self, input: &str) -> anyhow::Result<String> {
|
|
||||||
let mut output = String::with_capacity(input.len());
|
|
||||||
|
|
||||||
for c in input.chars() {
|
|
||||||
let index = self.get_index(c, self.offset)?;
|
|
||||||
output.push(self.alphabet[index])
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(output)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn decode(&self, input: &str) -> anyhow::Result<String> {
|
|
||||||
let mut output = String::with_capacity(input.len());
|
|
||||||
|
|
||||||
for c in input.chars() {
|
|
||||||
let index = self.get_index(c, -self.offset)?;
|
|
||||||
output.push(self.alphabet[index])
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(output)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_index(&self, c: char, offset: i64) -> anyhow::Result<usize> {
|
|
||||||
let mut index =
|
|
||||||
self.alphabet
|
|
||||||
.iter()
|
|
||||||
.position(|&x| x == c)
|
|
||||||
.ok_or(anyhow::anyhow!("cannot encode character {:?}", c))? as i64
|
|
||||||
+ offset;
|
|
||||||
|
|
||||||
while index < 0 {
|
|
||||||
index += self.alphabet.len() as i64;
|
|
||||||
}
|
|
||||||
|
|
||||||
index %= self.alphabet.len() as i64;
|
|
||||||
|
|
||||||
Ok(index as usize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
130
src/main.rs
130
src/main.rs
@@ -1,78 +1,68 @@
|
|||||||
use clap::Parser;
|
use itertools::Itertools;
|
||||||
|
|
||||||
mod atbash;
|
|
||||||
#[allow(dead_code)]
|
|
||||||
mod caesar;
|
|
||||||
#[allow(dead_code)]
|
|
||||||
mod polybius_square;
|
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
#[derive(Debug, clap::Parser)]
|
fn encode(group_size: usize, filler_char: char, input: String) -> String {
|
||||||
struct Args {
|
let mut input = util::remove_whitespace(util::remove_punctuation(input));
|
||||||
#[clap(subcommand)]
|
let len = input.chars().count();
|
||||||
command: Command,
|
if len % group_size != 0 {
|
||||||
}
|
for _ in 0..(group_size - len % group_size) {
|
||||||
|
input.push(filler_char);
|
||||||
#[derive(Debug, clap::Subcommand)]
|
|
||||||
enum Command {
|
|
||||||
User,
|
|
||||||
Cli {
|
|
||||||
#[clap(short, long)]
|
|
||||||
mode: Mode,
|
|
||||||
#[clap(short, long)]
|
|
||||||
input: String,
|
|
||||||
#[clap(short, long)]
|
|
||||||
alphabet: String,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, clap::ValueEnum)]
|
|
||||||
enum Mode {
|
|
||||||
Encode,
|
|
||||||
Decode,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
|
||||||
let args = Args::parse();
|
|
||||||
|
|
||||||
match args.command {
|
|
||||||
Command::User => {
|
|
||||||
let alphabet = dialoguer::Input::<String>::new()
|
|
||||||
.with_prompt("Enter alphabet")
|
|
||||||
.with_initial_text("АаБбВвГгДдЕеЁёЖжЗзИиЙйКкЛлМмНнОоПпРрСсТтУуФфХхЦцЧчШшЩщЪъЫыЬьЭэЮюЯя0123456789 ,.:!?_-+/*='^%#&@$")
|
|
||||||
.validate_with(|input: &String| {
|
|
||||||
let chars = input.chars().collect::<Vec<_>>();
|
|
||||||
util::verify_alphabet(&chars)
|
|
||||||
})
|
|
||||||
.interact_text()?;
|
|
||||||
let input = dialoguer::Input::<String>::new()
|
|
||||||
.with_prompt("Enter input")
|
|
||||||
.interact_text()?;
|
|
||||||
|
|
||||||
let atbash = atbash::AtabashChipher::new(&alphabet)?;
|
|
||||||
|
|
||||||
let encoded = atbash.encode(&input)?;
|
|
||||||
println!("Result: {}", encoded);
|
|
||||||
}
|
|
||||||
Command::Cli {
|
|
||||||
mode,
|
|
||||||
input,
|
|
||||||
alphabet,
|
|
||||||
} => {
|
|
||||||
let atbash = atbash::AtabashChipher::new(&alphabet)?;
|
|
||||||
|
|
||||||
match mode {
|
|
||||||
Mode::Encode => {
|
|
||||||
let encoded = atbash.encode(&input)?;
|
|
||||||
println!("{}", encoded);
|
|
||||||
}
|
|
||||||
Mode::Decode => {
|
|
||||||
let decoded = atbash.decode(&input)?;
|
|
||||||
println!("{}", decoded);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut result = String::new();
|
||||||
|
for chunk in &input.chars().rev().chunks(group_size) {
|
||||||
|
for c in chunk {
|
||||||
|
result.push(c);
|
||||||
|
}
|
||||||
|
result.push(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode(_group_size: usize, filler_char: char, input: String) -> String {
|
||||||
|
let result = util::remove_whitespace(util::remove_punctuation(input))
|
||||||
|
.chars()
|
||||||
|
.rev()
|
||||||
|
.collect::<String>();
|
||||||
|
|
||||||
|
result.trim_end_matches(filler_char).to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> anyhow::Result<()> {
|
||||||
|
let group_size = dialoguer::Input::<usize>::new()
|
||||||
|
.with_prompt("Enter group size")
|
||||||
|
.validate_with(|input: &usize| {
|
||||||
|
if input > &1 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err("Must be greater than 1".to_string())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.interact_text()?;
|
||||||
|
let filler_char = dialoguer::Input::<char>::new()
|
||||||
|
.with_prompt("Filler character")
|
||||||
|
.default('A')
|
||||||
|
.interact_text()?;
|
||||||
|
let input = dialoguer::Input::<String>::new()
|
||||||
|
.with_prompt("Enter input")
|
||||||
|
.interact_text()?;
|
||||||
|
let mode = dialoguer::Select::new()
|
||||||
|
.with_prompt("Mode")
|
||||||
|
.item("Encode")
|
||||||
|
.item("Decode")
|
||||||
|
.default(0)
|
||||||
|
.interact()?;
|
||||||
|
|
||||||
|
let result = match mode {
|
||||||
|
0 => encode(group_size, filler_char, input),
|
||||||
|
1 => decode(group_size, filler_char, input),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("Result: {}", result);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
use crate::util;
|
|
||||||
|
|
||||||
pub struct PolybiusSquareCipher {
|
|
||||||
alphabet: Vec<char>,
|
|
||||||
rows: usize,
|
|
||||||
columns: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PolybiusSquareCipher {
|
|
||||||
pub fn new(alphabet: &str, columns: usize, rows: usize) -> anyhow::Result<Self> {
|
|
||||||
let alphabet = alphabet.chars().collect::<Vec<_>>();
|
|
||||||
util::verify_alphabet(&alphabet)?;
|
|
||||||
if rows * columns != alphabet.len() {
|
|
||||||
return Err(anyhow::anyhow!(
|
|
||||||
"rows * columns must equal the length of the alphabet"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
alphabet,
|
|
||||||
rows,
|
|
||||||
columns,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn encode(&self, input: &str) -> anyhow::Result<String> {
|
|
||||||
let mut output = String::with_capacity(input.len());
|
|
||||||
|
|
||||||
for c in input.chars() {
|
|
||||||
let index = self
|
|
||||||
.alphabet
|
|
||||||
.iter()
|
|
||||||
.position(|&x| x == c)
|
|
||||||
.ok_or(anyhow::anyhow!("invalid character"))?;
|
|
||||||
|
|
||||||
let row = index / self.columns;
|
|
||||||
let col = index % self.columns;
|
|
||||||
|
|
||||||
output.push(self.alphabet[((row + 1) % self.rows) * self.columns + col]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(output)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn decode(&self, input: &str) -> anyhow::Result<String> {
|
|
||||||
let mut output = String::with_capacity(input.len());
|
|
||||||
|
|
||||||
for c in input.chars() {
|
|
||||||
let index = self
|
|
||||||
.alphabet
|
|
||||||
.iter()
|
|
||||||
.position(|&x| x == c)
|
|
||||||
.ok_or(anyhow::anyhow!("invalid character"))?;
|
|
||||||
|
|
||||||
let mut row = index / self.columns;
|
|
||||||
let col = index % self.columns;
|
|
||||||
|
|
||||||
if row == 0 {
|
|
||||||
row = self.rows - 1;
|
|
||||||
} else {
|
|
||||||
row -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
output.push(self.alphabet[(row % self.rows) * self.columns + col]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
22
src/util.rs
22
src/util.rs
@@ -1,17 +1,7 @@
|
|||||||
pub fn verify_alphabet(alphabet: &[char]) -> anyhow::Result<()> {
|
pub fn remove_whitespace(input: String) -> String {
|
||||||
if alphabet.len() < 2 {
|
input.chars().filter(|c| !c.is_whitespace()).collect()
|
||||||
return Err(anyhow::anyhow!(
|
}
|
||||||
"Alphabet must contain at least 2 characters"
|
|
||||||
));
|
pub fn remove_punctuation(input: String) -> String {
|
||||||
}
|
input.chars().filter(|c| !c.is_ascii_punctuation()).collect()
|
||||||
|
|
||||||
let mut alphabet_set = std::collections::HashSet::new();
|
|
||||||
for c in alphabet {
|
|
||||||
if alphabet_set.contains(c) {
|
|
||||||
return Err(anyhow::anyhow!("Alphabet contains duplicate characters"));
|
|
||||||
}
|
|
||||||
alphabet_set.insert(*c);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user