lab2
This commit is contained in:
16
Cargo.lock
generated
16
Cargo.lock
generated
@@ -141,6 +141,12 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "0.3.6"
|
||||
@@ -176,6 +182,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"dialoguer",
|
||||
"itertools",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -184,6 +191,15 @@ version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
|
||||
@@ -7,3 +7,4 @@ edition = "2021"
|
||||
anyhow = "1.0.86"
|
||||
clap = { version = "4.5.17", features = ["derive"] }
|
||||
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)
|
||||
}
|
||||
}
|
||||
108
src/main.rs
108
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;
|
||||
|
||||
#[derive(Debug, clap::Parser)]
|
||||
struct Args {
|
||||
#[clap(subcommand)]
|
||||
command: Command,
|
||||
fn encode(group_size: usize, filler_char: char, input: String) -> String {
|
||||
let mut input = util::remove_whitespace(util::remove_punctuation(input));
|
||||
let len = input.chars().count();
|
||||
if len % group_size != 0 {
|
||||
for _ in 0..(group_size - len % group_size) {
|
||||
input.push(filler_char);
|
||||
}
|
||||
}
|
||||
|
||||
let mut result = String::new();
|
||||
for chunk in &input.chars().rev().chunks(group_size) {
|
||||
for c in chunk {
|
||||
result.push(c);
|
||||
}
|
||||
result.push(' ');
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[derive(Debug, clap::Subcommand)]
|
||||
enum Command {
|
||||
User,
|
||||
Cli {
|
||||
#[clap(short, long)]
|
||||
mode: Mode,
|
||||
#[clap(short, long)]
|
||||
input: String,
|
||||
#[clap(short, long)]
|
||||
alphabet: String,
|
||||
},
|
||||
}
|
||||
fn decode(_group_size: usize, filler_char: char, input: String) -> String {
|
||||
let result = util::remove_whitespace(util::remove_punctuation(input))
|
||||
.chars()
|
||||
.rev()
|
||||
.collect::<String>();
|
||||
|
||||
#[derive(Debug, Clone, clap::ValueEnum)]
|
||||
enum Mode {
|
||||
Encode,
|
||||
Decode,
|
||||
result.trim_end_matches(filler_char).to_string()
|
||||
}
|
||||
|
||||
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)
|
||||
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 atbash = atbash::AtabashChipher::new(&alphabet)?;
|
||||
let result = match mode {
|
||||
0 => encode(group_size, filler_char, input),
|
||||
1 => decode(group_size, filler_char, input),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("Result: {}", result);
|
||||
|
||||
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<()> {
|
||||
if alphabet.len() < 2 {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Alphabet must contain at least 2 characters"
|
||||
));
|
||||
}
|
||||
|
||||
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(())
|
||||
pub fn remove_whitespace(input: String) -> String {
|
||||
input.chars().filter(|c| !c.is_whitespace()).collect()
|
||||
}
|
||||
|
||||
pub fn remove_punctuation(input: String) -> String {
|
||||
input.chars().filter(|c| !c.is_ascii_punctuation()).collect()
|
||||
}
|
||||
Reference in New Issue
Block a user