mirror of
https://github.com/RoboSats/robo-identities.git
synced 2026-01-23 06:34:14 +00:00
Re-write robohash generator for light weight w/ embeded parts
This commit is contained in:
1
robohash/.gitignore
vendored
1
robohash/.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
/target
|
||||
/Cargo.lock
|
||||
/robohash.txt
|
||||
@ -1,29 +1,25 @@
|
||||
use robohash::*;
|
||||
use std::error::Error;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
|
||||
fn build_robohash(
|
||||
initial_string: &str,
|
||||
set: &str,
|
||||
color: &str,
|
||||
background_set: &str,
|
||||
use_background: &bool,
|
||||
size: u32,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
// build
|
||||
let robo_hash: RoboHash = RoboHashBuilder::new(initial_string)
|
||||
.with_set(set)
|
||||
.with_color(&color)
|
||||
.with_background_set(background_set)
|
||||
.with_background(use_background)
|
||||
.with_size(size, size)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let base64_robohash = robo_hash.assemble_base64()?;
|
||||
let _base64_robohash = robo_hash.assemble_base64()?;
|
||||
|
||||
// Save output
|
||||
// use std::fs::File;
|
||||
// use std::io::Write;
|
||||
// let mut output = File::create("robohash.txt")?;
|
||||
// write!(output, "{}", base64_robohash)?;
|
||||
|
||||
@ -32,28 +28,26 @@ fn build_robohash(
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
let initial_string = black_box("test");
|
||||
let set = black_box("set1");
|
||||
let color = black_box(String::from("red"));
|
||||
let background_set = black_box("bg1");
|
||||
let use_background = black_box(&true);
|
||||
let size = black_box(512);
|
||||
|
||||
c.bench_function("Build Robohash", |b| {
|
||||
b.iter(|| build_robohash(initial_string, set, &color, background_set, size))
|
||||
b.iter(|| build_robohash(initial_string, use_background, size))
|
||||
});
|
||||
|
||||
let size = black_box(256);
|
||||
c.bench_function("Build medium size Robohash", |b| {
|
||||
b.iter(|| build_robohash(initial_string, set, &color, background_set, size))
|
||||
b.iter(|| build_robohash(initial_string, use_background, size))
|
||||
});
|
||||
|
||||
let size = black_box(64);
|
||||
c.bench_function("Build small size Robohash", |b| {
|
||||
b.iter(|| build_robohash(initial_string, set, &color, background_set, size))
|
||||
b.iter(|| build_robohash(initial_string, use_background, size))
|
||||
});
|
||||
|
||||
let size = black_box(8);
|
||||
c.bench_function("Build tiny size Robohash", |b| {
|
||||
b.iter(|| build_robohash(initial_string, set, &color, background_set, size))
|
||||
b.iter(|| build_robohash(initial_string, use_background, size))
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -13,37 +13,81 @@ def convert_to_webp_base64(file_path: str) -> str:
|
||||
encoded_string = base64.b64encode(buffer.getvalue())
|
||||
return encoded_string.decode("utf-8")
|
||||
|
||||
def sort_image_arrays_by_stacking_order(tuples_list:list)->list:
|
||||
try:
|
||||
sorted_tuples = sorted(tuples_list, key=lambda tup: int(''.join([i for i in tup[0].split('#')[1] if i.isnumeric()])))
|
||||
except:
|
||||
# backgrounds do not have sorting number
|
||||
sorted_tuples = tuples_list
|
||||
return sorted_tuples
|
||||
|
||||
def create_image_arrays(directory):
|
||||
def create_image_arrays(directory:str)->list((str,str,int)):
|
||||
image_arrays = []
|
||||
max_length = 0
|
||||
for root, _, files in os.walk(directory):
|
||||
png_files = [f for f in files if f.endswith(".png")]
|
||||
if png_files:
|
||||
max_length = max(max_length, len(png_files))
|
||||
|
||||
for root, _, files in os.walk(directory):
|
||||
png_files = [f for f in files if f.endswith(".png")]
|
||||
if png_files:
|
||||
array = "[\n"
|
||||
for png_file in png_files:
|
||||
for i, png_file in enumerate(png_files):
|
||||
png_path = os.path.join(root, png_file)
|
||||
base64_string = convert_to_webp_base64(png_path)
|
||||
array += f' "{base64_string}",\n'
|
||||
if i < max_length:
|
||||
array += ' PADDING,\n'*(max_length-i-1)
|
||||
array += "]"
|
||||
image_arrays.append((root, array))
|
||||
return image_arrays
|
||||
image_arrays.append((root, array, len(png_files)))
|
||||
image_arrays = sort_image_arrays_by_stacking_order(image_arrays)
|
||||
return image_arrays, max_length
|
||||
|
||||
def get_alphabetic_substring(string:str)-> str:
|
||||
for i in range(len(string)-1, -1, -1):
|
||||
if string[i].isdigit():
|
||||
result = string[i+1:]
|
||||
if result == "":
|
||||
return "BACKGROUND"
|
||||
return result
|
||||
return ""
|
||||
|
||||
def write_image_arrays(image_arrays, output_file):
|
||||
def part_name(root:str) -> str:
|
||||
name = root.replace('/', '_').replace('#', '_').upper()
|
||||
name = get_alphabetic_substring(name)
|
||||
return name
|
||||
|
||||
def create_vectors(image_arrays: list, length) -> str:
|
||||
content = f'pub static PARTS: &[[&str;{length}]] = &['
|
||||
for root, _, _ in image_arrays:
|
||||
content += f' {part_name(root)},'
|
||||
content += '];\n\n'
|
||||
|
||||
content += f'pub static PARTS_LENGTH: [u8; {len(image_arrays)}] = ['
|
||||
for root, _, l in image_arrays:
|
||||
content += f'{l},'
|
||||
content += '];\n\n'
|
||||
|
||||
return content
|
||||
|
||||
def write_image_arrays(image_arrays:list, length, output_file:str):
|
||||
content = create_vectors(image_arrays, length)
|
||||
content += 'const PADDING: &str = "";\n'
|
||||
for root, array, _ in image_arrays:
|
||||
content += "\n"
|
||||
content += f"const {part_name(root)}: [&str;{length}] =\n{array};\n"
|
||||
with open(output_file, "w") as f:
|
||||
f.write("use std::borrow::Cow;\n")
|
||||
for root, array in image_arrays:
|
||||
array_name = root.replace('/', '_').replace('#', '_')
|
||||
f.write("\n")
|
||||
f.write(f"pub static {array_name}: &[&str] = {array};\n")
|
||||
f.write(content)
|
||||
|
||||
if __name__ == "__main__":
|
||||
directory = "sets/set1/green"
|
||||
output_file = "src/robot_parts.rs"
|
||||
image_arrays = create_image_arrays(directory)
|
||||
write_image_arrays(image_arrays, output_file)
|
||||
image_arrays, length = create_image_arrays(directory)
|
||||
write_image_arrays(image_arrays, length, output_file)
|
||||
|
||||
directory = "backgrounds"
|
||||
output_file = "src/backgrounds.rs"
|
||||
image_arrays = create_image_arrays(directory)
|
||||
write_image_arrays(image_arrays, output_file)
|
||||
### Regenerating backgrounds.rs requires a bit of manual editing
|
||||
# directory = "backgrounds"
|
||||
# output_file = "src/backgrounds.rs"
|
||||
# image_arrays, length = create_image_arrays(directory)
|
||||
# write_image_arrays(image_arrays, length, output_file)
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -11,15 +11,22 @@ pub(crate) fn build_robo_hash_image(
|
||||
background: &Option<String>,
|
||||
width: u32,
|
||||
height: u32,
|
||||
hue_rotation: &Option<i32>,
|
||||
) -> Result<RgbaImage, Error> {
|
||||
let mut base_image = image::ImageBuffer::new(width, height);
|
||||
if let Some(background) = background {
|
||||
append_to_image(&mut base_image, background, width, height)?;
|
||||
append_to_image(&mut base_image, background, width, height, &0)?;
|
||||
}
|
||||
|
||||
let hue = match hue_rotation {
|
||||
Some(hue) => hue,
|
||||
None => &0,
|
||||
};
|
||||
|
||||
robo_parts
|
||||
.iter()
|
||||
.try_for_each(|image_path| -> Result<(), Error> {
|
||||
append_to_image(&mut base_image, image_path, width, height)?;
|
||||
append_to_image(&mut base_image, image_path, width, height, hue)?;
|
||||
Ok(())
|
||||
})?;
|
||||
Ok(base_image)
|
||||
@ -27,23 +34,19 @@ pub(crate) fn build_robo_hash_image(
|
||||
|
||||
fn append_to_image(
|
||||
base_image: &mut ImageBuffer<Rgba<u8>, Vec<u8>>,
|
||||
image_path: &String,
|
||||
image_path: &str,
|
||||
width: u32,
|
||||
height: u32,
|
||||
hue_rotation: &i32,
|
||||
) -> Result<(), Error> {
|
||||
let image = try_open_image(image_path)?;
|
||||
let image = imageops::resize(&image, width, height, imageops::FilterType::Lanczos3);
|
||||
// let image = try_open_image(image_path)?;
|
||||
let image = from_base64(image_path)?;
|
||||
let mut image = imageops::resize(&image, width, height, imageops::FilterType::Lanczos3);
|
||||
imageops::colorops::huerotate_in_place(&mut image, *hue_rotation);
|
||||
imageops::overlay(base_image, &image, 0, 0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_open_image(image_path: &String) -> Result<DynamicImage, Error> {
|
||||
match image::open(image_path) {
|
||||
Ok(image) => Ok(image),
|
||||
Err(e) => Err(Error::ImageOpenFailed(format!("{e:#?}"))),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_base_64(image: &RgbaImage) -> Result<String, Error> {
|
||||
let mut bytes: Vec<u8> = Vec::new();
|
||||
image.write_to(&mut Cursor::new(&mut bytes), image::ImageFormat::Png)?;
|
||||
@ -64,20 +67,24 @@ pub(crate) mod tests {
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
use crate::backgrounds::BACKGROUNDS;
|
||||
use crate::robot_parts::PARTS;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn build_robo_hash_image_returns_built_image_of_parts() {
|
||||
// arrange
|
||||
let robo_parts = vec![
|
||||
String::from("./sets/set1/blue/003#01Body/000#blue_body-10.png"),
|
||||
String::from("./sets/set1/blue/004#02Face/000#blue_face-07.png"),
|
||||
String::from("./sets/set1/blue/000#03Mouth/000#blue_mouth-10.png"),
|
||||
String::from("./sets/set1/blue/001#04Eyes/000#blue_eyes-07.png"),
|
||||
String::from("./sets/set1/blue/002#05Accessory/000#blue_accessory-02.png"),
|
||||
String::from(PARTS[0][0]),
|
||||
String::from(PARTS[1][0]),
|
||||
String::from(PARTS[2][0]),
|
||||
String::from(PARTS[3][0]),
|
||||
String::from(PARTS[4][0]),
|
||||
];
|
||||
let hue_rotation = None;
|
||||
// act
|
||||
let robo_hash = build_robo_hash_image(&robo_parts, &None, 512, 512);
|
||||
let robo_hash = build_robo_hash_image(&robo_parts, &None, 512, 512, &hue_rotation);
|
||||
// assert
|
||||
assert!(robo_hash.is_ok())
|
||||
}
|
||||
@ -86,25 +93,27 @@ pub(crate) mod tests {
|
||||
fn to_base64_converts_image_to_base64_string() {
|
||||
// arrange
|
||||
let robo_parts = vec![
|
||||
String::from("./sets/set1/blue/003#01Body/000#blue_body-10.png"),
|
||||
String::from("./sets/set1/blue/004#02Face/000#blue_face-07.png"),
|
||||
String::from("./sets/set1/blue/000#03Mouth/000#blue_mouth-10.png"),
|
||||
String::from("./sets/set1/blue/001#04Eyes/000#blue_eyes-07.png"),
|
||||
String::from("./sets/set1/blue/002#05Accessory/000#blue_accessory-02.png"),
|
||||
String::from(PARTS[0][0]),
|
||||
String::from(PARTS[1][0]),
|
||||
String::from(PARTS[2][0]),
|
||||
String::from(PARTS[3][0]),
|
||||
String::from(PARTS[4][0]),
|
||||
];
|
||||
let background = Some(String::from("./backgrounds/bg1/000#robotBG-11.png"));
|
||||
let hue_rotation = Some(90);
|
||||
let background = Some(String::from(BACKGROUNDS[0]));
|
||||
let expected_base64 = load_base64_string_image_resources("image");
|
||||
let robo_hash = build_robo_hash_image(&robo_parts, &background, 512, 512)
|
||||
let robo_hash = build_robo_hash_image(&robo_parts, &background, 512, 512, &hue_rotation)
|
||||
.expect("Should return an actual ImageBuffer");
|
||||
// act
|
||||
let base64_string = to_base_64(&robo_hash);
|
||||
// assert
|
||||
assert!(base64_string.is_ok());
|
||||
|
||||
// // Save output
|
||||
// // Overwrite test image
|
||||
// use std::io::Write;
|
||||
// let mut output = File::create("./robohash.txt").unwrap();
|
||||
// let mut output = File::create("./test_resources/image.txt").unwrap();
|
||||
// write!(output, "{}", base64_string.unwrap());
|
||||
|
||||
assert_eq!(base64_string.unwrap(), expected_base64)
|
||||
}
|
||||
|
||||
|
||||
@ -1,63 +1,30 @@
|
||||
use crate::error::Error;
|
||||
|
||||
mod backgrounds;
|
||||
pub mod error;
|
||||
mod hash;
|
||||
mod image;
|
||||
mod materials;
|
||||
|
||||
const SET_DEFAULT: &str = "set1";
|
||||
mod robot_parts;
|
||||
|
||||
pub struct RoboHashBuilder<'a> {
|
||||
text: &'a str,
|
||||
color: Option<String>,
|
||||
image_size: ImageSize,
|
||||
set: String,
|
||||
set_root: String,
|
||||
background_set: Option<String>,
|
||||
background_root: String,
|
||||
use_background: &'a bool,
|
||||
}
|
||||
|
||||
impl<'a> RoboHashBuilder<'a> {
|
||||
pub fn new(text: &'a str) -> Self {
|
||||
let color = None;
|
||||
let image_size = ImageSize::default();
|
||||
let set = String::from(SET_DEFAULT);
|
||||
let set_root = String::from("./sets");
|
||||
let background_set = None;
|
||||
let background_root = String::from("./backgrounds");
|
||||
let use_background = &true;
|
||||
Self {
|
||||
text,
|
||||
color,
|
||||
image_size,
|
||||
set,
|
||||
set_root,
|
||||
background_set,
|
||||
background_root,
|
||||
use_background,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_set(mut self, set: &str) -> RoboHashBuilder<'a> {
|
||||
self.set = String::from(set);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_set_location(mut self, set_location: &str) -> RoboHashBuilder<'a> {
|
||||
self.set_root = String::from(set_location);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_background_set(mut self, background_set: &str) -> RoboHashBuilder<'a> {
|
||||
self.background_set = Some(String::from(background_set));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_background_location(mut self, background_location: &str) -> RoboHashBuilder<'a> {
|
||||
self.background_root = String::from(background_location);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_color(mut self, color: &str) -> RoboHashBuilder<'a> {
|
||||
self.color = Some(String::from(color));
|
||||
pub fn with_background(mut self, use_background: &'a bool) -> RoboHashBuilder<'a> {
|
||||
self.use_background = use_background;
|
||||
self
|
||||
}
|
||||
|
||||
@ -70,41 +37,21 @@ impl<'a> RoboHashBuilder<'a> {
|
||||
let hash_array_chunks = 11;
|
||||
let hash = hash::sha512_digest(self.text)?;
|
||||
let hash_array = hash::split_hash(&hash, hash_array_chunks)?;
|
||||
let color = color_selection(&hash_array, &self.color, &self.set, &self.set_root)?;
|
||||
let set = self.set_with_color(color);
|
||||
let sets_root = self.set_root.to_owned();
|
||||
let background_set = self.background_set.to_owned();
|
||||
let background_root = self.background_root.to_owned();
|
||||
let use_background = self.use_background.to_owned();
|
||||
|
||||
Ok(RoboHash {
|
||||
image_size: self.image_size,
|
||||
hash_array,
|
||||
set,
|
||||
sets_root,
|
||||
background_set,
|
||||
background_root,
|
||||
use_background,
|
||||
})
|
||||
}
|
||||
|
||||
fn set_with_color(&self, color: Option<String>) -> String {
|
||||
match self.set.as_str() {
|
||||
SET_DEFAULT => match color {
|
||||
Some(color) => format!("{}/{}", &self.set.as_str(), color.as_str()),
|
||||
None => String::from(self.set.as_str()),
|
||||
},
|
||||
_ => String::from(self.set.as_str()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RoboHash {
|
||||
image_size: ImageSize,
|
||||
hash_array: Vec<i64>,
|
||||
set: String,
|
||||
sets_root: String,
|
||||
background_set: Option<String>,
|
||||
background_root: String,
|
||||
use_background: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@ -116,8 +63,8 @@ struct ImageSize {
|
||||
impl ImageSize {
|
||||
pub(crate) fn default() -> Self {
|
||||
Self {
|
||||
width: 1024,
|
||||
height: 1024,
|
||||
width: 256,
|
||||
height: 256,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -128,94 +75,55 @@ impl RoboHash {
|
||||
return Err(Error::RoboHashMissingRequiredData);
|
||||
}
|
||||
|
||||
let set = files_in_set(&self.hash_array, &self.sets_root, &self.set)?;
|
||||
let background = match &self.background_set {
|
||||
Some(set) => background(&self.hash_array, &self.background_root, set)?,
|
||||
None => None,
|
||||
let set = select_robot_parts(&self.hash_array);
|
||||
|
||||
let background = match &self.use_background {
|
||||
true => select_background(&self.hash_array),
|
||||
false => None,
|
||||
};
|
||||
|
||||
let hue_rotation = select_hue_rotation(&self.hash_array);
|
||||
|
||||
let image = image::build_robo_hash_image(
|
||||
&set,
|
||||
&background,
|
||||
self.image_size.width,
|
||||
self.image_size.height,
|
||||
&hue_rotation,
|
||||
)?;
|
||||
|
||||
let base64 = image::to_base_64(&image)?;
|
||||
Ok(base64)
|
||||
}
|
||||
|
||||
fn is_missing_required_data(&self) -> bool {
|
||||
self.hash_array.is_empty() || self.set.is_empty() || self.sets_root.is_empty()
|
||||
self.hash_array.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
fn files_in_set(hash_array: &Vec<i64>, sets_root: &str, set: &str) -> Result<Vec<String>, Error> {
|
||||
let categories_in_set = materials::categories_in_set(sets_root, set)?;
|
||||
let mut index = 4;
|
||||
let mut files = categories_in_set
|
||||
.iter()
|
||||
.flat_map(
|
||||
|category| match materials::files_in_category(sets_root, set, category) {
|
||||
Ok(file) => {
|
||||
let set_index = (hash_array[index] % file.len() as i64) as usize;
|
||||
if let Some(selected_file) = file.get(set_index) {
|
||||
index = index + 1;
|
||||
Some(String::from(selected_file))
|
||||
} else {
|
||||
println!("failed to fetch index {set_index:#?} from {file:#?}");
|
||||
None
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("{e:#?}");
|
||||
None
|
||||
}
|
||||
},
|
||||
)
|
||||
.collect::<Vec<String>>();
|
||||
files.sort_by(|a, b| {
|
||||
a.split("#").collect::<Vec<_>>()[1].cmp(b.split("#").collect::<Vec<_>>()[1])
|
||||
});
|
||||
Ok(files)
|
||||
}
|
||||
fn select_robot_parts(hash_array: &[i64]) -> Vec<String> {
|
||||
use robot_parts::{PARTS, PARTS_LENGTH};
|
||||
let mut selected_strings = Vec::new();
|
||||
|
||||
fn background(
|
||||
hash_array: &Vec<i64>,
|
||||
background_root: &str,
|
||||
set: &str,
|
||||
) -> Result<Option<String>, Error> {
|
||||
let index = 3;
|
||||
let backgrounds = materials::categories_in_set(background_root, set)?;
|
||||
let set_index = (hash_array[index] % backgrounds.len() as i64) as usize;
|
||||
Ok(match backgrounds.get(set_index) {
|
||||
Some(background) => {
|
||||
let background_path = [background_root, "/", set, "/", background].concat();
|
||||
Some(background_path)
|
||||
}
|
||||
None => {
|
||||
println!("failed to fetch index {set_index:#?} from {backgrounds:#?}");
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn color_selection(
|
||||
hash_array: &Vec<i64>,
|
||||
color: &Option<String>,
|
||||
set: &str,
|
||||
set_root: &str,
|
||||
) -> Result<Option<String>, Error> {
|
||||
if set == SET_DEFAULT && color.is_none() {
|
||||
Ok(Some(random_color(hash_array, set_root)?))
|
||||
} else {
|
||||
Ok(color.clone())
|
||||
for i in 0..PARTS.len() {
|
||||
let index = (hash_array[i] % PARTS_LENGTH[i] as i64) as usize;
|
||||
selected_strings.push(PARTS[i][index].to_string())
|
||||
}
|
||||
|
||||
selected_strings
|
||||
}
|
||||
|
||||
fn random_color(hash_array: &Vec<i64>, set_root: &str) -> Result<String, Error> {
|
||||
let available_colors = materials::categories_in_set(set_root, "set1")?;
|
||||
let selected_index = (hash_array[0] % available_colors.len() as i64) as usize;
|
||||
Ok(available_colors[selected_index].clone())
|
||||
fn select_background(hash_array: &[i64]) -> Option<String> {
|
||||
use backgrounds::BACKGROUNDS;
|
||||
let index = 6;
|
||||
let i = (hash_array[index] % BACKGROUNDS.len() as i64) as usize;
|
||||
Some(BACKGROUNDS[i].to_string())
|
||||
}
|
||||
|
||||
fn select_hue_rotation(hash_array: &[i64]) -> Option<i32> {
|
||||
let index = 7;
|
||||
let hue = (hash_array[index] % 360) as i32;
|
||||
Some(hue)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -237,64 +145,6 @@ mod tests {
|
||||
assert_eq!(robo_hash_builder.text, text)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_that_robo_hash_builder_returns_a_builder_with_default_set() {
|
||||
// arrange
|
||||
let text = "text";
|
||||
let expected_set = SET_DEFAULT;
|
||||
// act
|
||||
let robo_hash_builder = RoboHashBuilder::new(text);
|
||||
// assert
|
||||
assert_eq!(robo_hash_builder.set, expected_set)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_that_robo_hash_builder_returns_a_builder_with_color_set_to_any() {
|
||||
// arrange
|
||||
let text = "text";
|
||||
let expected_color = None;
|
||||
// act
|
||||
let robo_hash_builder = RoboHashBuilder::new(text);
|
||||
// assert
|
||||
assert_eq!(robo_hash_builder.color, expected_color)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_that_robo_hash_builder_with_set_changes_the_set() {
|
||||
// arrange
|
||||
let text = "text";
|
||||
let set = "set1";
|
||||
let expected_set = "set1";
|
||||
// act
|
||||
let robo_hash_builder = RoboHashBuilder::new(text).with_set(set);
|
||||
// assert
|
||||
assert_eq!(robo_hash_builder.set, expected_set)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_that_robo_hash_builder_with_color_changes_sets_color() {
|
||||
// arrange
|
||||
let text = "text";
|
||||
let color = "blue";
|
||||
let expected_color = Some(String::from("blue"));
|
||||
// act
|
||||
let robo_hash_builder = RoboHashBuilder::new(text).with_color(color);
|
||||
// assert
|
||||
assert_eq!(robo_hash_builder.color, expected_color)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_that_robo_hash_builder_with_set_root_changes_sets_new_set_root() {
|
||||
// arrange
|
||||
let text = "text";
|
||||
let set_root = "new_set_root";
|
||||
let expected_set_root = "new_set_root";
|
||||
// act
|
||||
let robo_hash_builder = RoboHashBuilder::new(text).with_set_location(set_root);
|
||||
// assert
|
||||
assert_eq!(robo_hash_builder.set_root, expected_set_root)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_that_robo_hash_builder_build_returns_a_robo_hash_struct() {
|
||||
// arrange
|
||||
@ -335,68 +185,13 @@ mod tests {
|
||||
) {
|
||||
// arrange
|
||||
let image_size = ImageSize {
|
||||
width: 1024,
|
||||
height: 1024,
|
||||
width: 512,
|
||||
height: 512,
|
||||
};
|
||||
let robo_hash = RoboHash {
|
||||
image_size,
|
||||
hash_array: vec![],
|
||||
set: String::from("set1"),
|
||||
sets_root: String::from("set_root"),
|
||||
background_set: None,
|
||||
background_root: String::from("background_root"),
|
||||
};
|
||||
// act
|
||||
let image = robo_hash.assemble_base64();
|
||||
// assert
|
||||
assert!(image.is_err());
|
||||
assert_eq!(
|
||||
image.err().unwrap().to_string(),
|
||||
Error::RoboHashMissingRequiredData.to_string()
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_robo_hash_assemble_base64_returns_missing_data_error_when_set_does_not_contain_any_data(
|
||||
) {
|
||||
// arrange
|
||||
let image_size = ImageSize {
|
||||
width: 1024,
|
||||
height: 1024,
|
||||
};
|
||||
let robo_hash = RoboHash {
|
||||
image_size,
|
||||
hash_array: vec![1, 2],
|
||||
set: String::from(""),
|
||||
sets_root: String::from("set_root"),
|
||||
background_set: None,
|
||||
background_root: String::from("background_root"),
|
||||
};
|
||||
// act
|
||||
let image = robo_hash.assemble_base64();
|
||||
// assert
|
||||
assert!(image.is_err());
|
||||
assert_eq!(
|
||||
image.err().unwrap().to_string(),
|
||||
Error::RoboHashMissingRequiredData.to_string()
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_robo_hash_assemble_base64_returns_missing_data_error_when_sets_root_does_not_contain_any_data(
|
||||
) {
|
||||
// arrange
|
||||
let image_size = ImageSize {
|
||||
width: 1024,
|
||||
height: 1024,
|
||||
};
|
||||
let robo_hash = RoboHash {
|
||||
image_size,
|
||||
hash_array: vec![1, 2],
|
||||
set: String::from("set1"),
|
||||
sets_root: String::from(""),
|
||||
background_set: None,
|
||||
background_root: String::from("background_root"),
|
||||
use_background: false,
|
||||
};
|
||||
// act
|
||||
let image = robo_hash.assemble_base64();
|
||||
@ -412,21 +207,16 @@ mod tests {
|
||||
fn test_that_robo_hash_image_is_generated() {
|
||||
// arrange
|
||||
let initial_string = "test";
|
||||
let set = SET_DEFAULT;
|
||||
let color: Option<String> = None;
|
||||
let background_set = "bg1";
|
||||
|
||||
let test_resource = format!("{initial_string}_{set}_{color:#?}_{background_set}");
|
||||
let expected_robo_hash = load_base64_string_image_resources(&test_resource);
|
||||
let test_resource = initial_string;
|
||||
let expected_robo_hash = load_base64_string_image_resources(test_resource);
|
||||
|
||||
// act
|
||||
let robo_hash = RoboHashBuilder::new(initial_string)
|
||||
.with_set(set)
|
||||
.with_background_set(background_set)
|
||||
.build()
|
||||
.unwrap();
|
||||
let robo_hash = RoboHashBuilder::new(initial_string).build().unwrap();
|
||||
let constructed_robo_hash = robo_hash.assemble_base64().unwrap();
|
||||
|
||||
// _write_to_test_resources(&test_resource, &constructed_robo_hash);
|
||||
|
||||
assert_eq!(constructed_robo_hash, expected_robo_hash);
|
||||
}
|
||||
|
||||
|
||||
@ -4,17 +4,12 @@ use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let initial_string = "test";
|
||||
let set = "set1";
|
||||
let color = String::from("red");
|
||||
let background_set = "bg1";
|
||||
let initial_string = "reckless";
|
||||
let size = 256;
|
||||
|
||||
// build
|
||||
let robo_hash: RoboHash = RoboHashBuilder::new(initial_string)
|
||||
.with_set(set)
|
||||
.with_color(&color)
|
||||
.with_background_set(background_set)
|
||||
.with_background(&true)
|
||||
.with_size(size, size)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
@ -1,52 +0,0 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
pub(crate) fn categories_in_set(root: &str, set: &str) -> Result<Vec<String>, Error> {
|
||||
let sets_dir = Path::new(root).join(set);
|
||||
let sets = directories_in_path(&sets_dir)?;
|
||||
Ok(sets)
|
||||
}
|
||||
|
||||
pub(crate) fn files_in_category(
|
||||
root: &str,
|
||||
set: &str,
|
||||
category: &str,
|
||||
) -> Result<Vec<String>, Error> {
|
||||
let directory = path_builder(root, set, category);
|
||||
let files = directories_in_path(&directory)?
|
||||
.iter()
|
||||
.flat_map(|dir| {
|
||||
if let Some(path) = directory.join(dir).as_path().to_str() {
|
||||
Some(String::from(path))
|
||||
} else {
|
||||
println!("cannot create directory {directory:#?}/{dir:#?}");
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<String>>();
|
||||
Ok(files)
|
||||
}
|
||||
|
||||
fn path_builder(sets_root: &str, set: &str, category: &str) -> PathBuf {
|
||||
Path::new(sets_root).join(set).join(category)
|
||||
}
|
||||
|
||||
fn directories_in_path(path: &PathBuf) -> Result<Vec<String>, Error> {
|
||||
let mut directories = path
|
||||
.read_dir()?
|
||||
.into_iter()
|
||||
.filter_map(|path| match path {
|
||||
Ok(path) => match path.file_name().into_string() {
|
||||
Ok(set) => Some(set),
|
||||
Err(e) => {
|
||||
println!("{e:#?}");
|
||||
None
|
||||
}
|
||||
},
|
||||
Err(_) => None,
|
||||
})
|
||||
.collect::<Vec<String>>();
|
||||
directories.sort();
|
||||
Ok(directories)
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
robohash/test_resources/test.txt
Normal file
1
robohash/test_resources/test.txt
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -40,6 +40,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_nickname_generator() {
|
||||
assert_eq!(
|
||||
generate_nickname("23d022aa5dc633f2f115e48fc1f393f051ebdec3dfae41cfcd01bdac3577017f"),
|
||||
|
||||
Reference in New Issue
Block a user