use rand::seq::SliceRandom;
use rand::thread_rng;
use std::net::IpAddr;
use crate::bytesbits::BytesBits;
use crate::combinations::Combinations;
use crate::{confusion_level, ConfusionLevel};
pub fn encode(
ip_addr: IpAddr,
required_confusion_level: ConfusionLevel,
) -> Option<String> {
encode_all(ip_addr, required_confusion_level)
.take(10)
.collect::<Vec<String>>()
.choose(&mut thread_rng())
.cloned()
}
pub fn encode_all(
ip_addr: IpAddr,
required_confusion_level: ConfusionLevel,
) -> impl Iterator<Item = String> {
let addr_octets = match ip_addr {
IpAddr::V4(addr_v4) => addr_v4.octets().to_vec(),
IpAddr::V6(addr_v6) => addr_v6.octets().to_vec(),
};
Combinations::new(BytesBits::new(addr_octets))
.filter_map(string_from_u32s)
.filter(move |candidate| {
confusion_level(candidate)
.map(|lev| lev >= required_confusion_level)
.unwrap_or(false)
})
}
fn string_from_u32s(u32s: Vec<u32>) -> Option<String> {
let mut ret = String::with_capacity(u32s.len() * 4);
for n in u32s {
if let Some(ch) = std::char::from_u32(n) {
ret.push(ch);
} else {
return None;
}
}
Some(ret)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encoding_of_something_with_no_valid_encodings() {
assert_eq!(
encode("0.0.0.1".parse().unwrap(), ConfusionLevel::Minimum),
None
);
assert_eq!(enc("0.0.0.1", ConfusionLevel::Minimum), Vec::<&str>::new());
}
#[test]
fn encoding_of_something_with_only_one_encoding() {
assert_eq!(
to_hex_str(
&encode("0.0.1.1".parse().unwrap(), ConfusionLevel::Minimum)
.unwrap()
),
"000000c481"
);
assert_eq!(enc("0.0.1.1", ConfusionLevel::Minimum), vec!["000000c481"]);
assert_eq!(
enc("0.0.1.1", ConfusionLevel::Satisfactory),
vec!["000000c481"]
);
assert_eq!(
enc("0.0.1.1", ConfusionLevel::Delightful),
Vec::<&str>::new()
);
}
#[test]
fn encode_localhost() {
assert_eq!(
enc("127.0.0.1", ConfusionLevel::Delightful),
vec!["3fd0800001", "cfb8000001"]
);
}
#[test]
fn encoding_something_with_only_one_delightful_but_more_satisfactory() {
assert_eq!(
enc("124.32.3.10", ConfusionLevel::Minimum),
vec!["3e0800cc8a", "cfa100060a"]
);
assert_eq!(
enc("124.32.3.10", ConfusionLevel::Satisfactory),
vec!["3e0800cc8a", "cfa100060a"]
);
assert_eq!(
enc("124.32.3.10", ConfusionLevel::Delightful),
vec!["3e0800cc8a"]
);
}
#[test]
fn all_encodings_of_198_51_100_164() {
assert_eq!(
enc("198.51.100.164", ConfusionLevel::Minimum),
vec!["630c6cd2a4", "630cdb8924", "d8b14d4924"]
);
assert_eq!(
enc("198.51.100.164", ConfusionLevel::Satisfactory),
vec!["630c6cd2a4", "630cdb8924", "d8b14d4924"]
);
assert_eq!(
enc("198.51.100.164", ConfusionLevel::Delightful),
vec!["630c6cd2a4", "630cdb8924", "d8b14d4924"]
);
}
#[test]
fn encoding_an_ipv6_address() {
assert_eq!(
enc("::16:164", ConfusionLevel::Minimum),
vec!["000000000000000000000000000000e2b08264"]
);
assert_eq!(
enc("::16:164", ConfusionLevel::Satisfactory),
vec!["000000000000000000000000000000e2b08264"]
);
assert_eq!(
enc("::16:164", ConfusionLevel::Delightful),
vec!["000000000000000000000000000000e2b08264"]
);
}
#[test]
fn encoding_an_ipv6_address_with_lots_of_options() {
let encs = enc("2001:db8::8a2e:370:7334", ConfusionLevel::Delightful);
assert!(encs
.contains(&String::from("1000215b400000000000000851380670e78cb4")));
assert!(encs.contains(&String::from(
"c480041b38000000000000010ac5b00dd88eccb4"
)));
}
fn enc(s: &str, confusion_level: ConfusionLevel) -> Vec<String> {
to_hex(encode_all(s.parse().unwrap(), confusion_level).collect())
}
fn to_hex(enc: Vec<String>) -> Vec<String> {
enc.iter().map(|s| to_hex_str(s)).collect::<Vec<String>>()
}
fn to_hex_str(s: &str) -> String {
s.as_bytes().iter().map(|c| format!("{:02x}", c)).collect()
}
}