use std::path::PathBuf;
use std::str::FromStr;
use structopt::StructOpt;
#[derive(StructOpt)]
#[structopt(name = "🦕 stegosaurust", about = "Hide text in images, using rust.")]
pub struct Opt {
#[structopt(subcommand)]
pub cmd: Command,
}
#[derive(StructOpt)]
pub enum Command {
#[structopt(
name = "encode",
visible_alias = "enc",
about = "encode files using steganography"
)]
Encode(Encode),
#[structopt(
name = "disguise",
visible_alias = "dsg",
about = "mask all files in a directory using steganography"
)]
Disguise(Disguise),
}
#[derive(StructOpt)]
pub struct Encode {
#[structopt(flatten)]
pub opts: EncodeOpts,
#[structopt(short = "C", long)]
pub check_max_length: bool,
#[structopt(short, long, parse(from_os_str))]
pub output: Option<PathBuf>,
#[structopt(short, long, parse(from_os_str), conflicts_with = "decode")]
pub input: Option<PathBuf>,
#[structopt(parse(from_os_str))]
pub image: PathBuf,
}
#[derive(StructOpt)]
pub struct Disguise {
#[structopt(flatten)]
pub opts: EncodeOpts,
#[structopt(parse(from_os_str))]
pub dir: PathBuf,
}
#[derive(StructOpt, Clone)]
pub struct EncodeOpts {
#[structopt(short, long)]
pub decode: bool,
#[structopt(short, long)]
pub base64: bool,
#[structopt(short, long)]
pub compress: bool,
#[structopt(short, long)]
pub key: Option<String>,
#[structopt(short, long, possible_values=&StegMethod::variants())]
pub method: Option<StegMethod>,
#[structopt(long)]
pub distribution: Option<BitDistribution>,
#[structopt(short, long, required_if("method", "rsb"))]
pub seed: Option<String>,
#[structopt(short = "N", long, required_if("method", "rsb"), possible_values=&["1","2","3","4"])]
pub max_bit: Option<u8>,
}
#[derive(StructOpt, Debug, Clone, Copy, Default)]
pub enum StegMethod {
#[default]
LeastSignificantBit,
RandomSignificantBit,
}
impl FromStr for StegMethod {
type Err = String;
fn from_str(method: &str) -> Result<Self, Self::Err> {
match method {
"lsb" => Ok(Self::LeastSignificantBit),
"rsb" => Ok(Self::RandomSignificantBit),
other => Err(format!("unknown encoding method: {}", other)),
}
}
}
impl StegMethod {
fn variants() -> [&'static str; 2] {
["lsb", "rsb"]
}
}
#[derive(StructOpt, Debug, Clone, Default)]
pub enum BitDistribution {
#[default]
Sequential,
Linear { length: usize },
}
impl FromStr for BitDistribution {
type Err = String;
fn from_str(method: &str) -> Result<Self, Self::Err> {
let parts: Vec<&str> = method.split('-').collect();
let method = if parts.len() <= 1 { method } else { parts[0] };
match method {
"sequential" => Ok(Self::Sequential),
"linear" => {
let length = *(parts.get(1).unwrap_or(&"0"));
let length = length.parse::<usize>().unwrap_or_else(|err| {
eprintln!(
"error parsing message length in linear bit distribution: {}",
err
);
std::process::exit(1);
});
Ok(Self::Linear { length })
}
other => Err(format!("unknown bit distribution {}", other)),
}
}
}