#lazy-evaluation #emoji #data #serialization #stream

bin+lib emojfuscate

Tools for turning any data into emoji and back again

2 unstable releases

new 0.2.0 Apr 18, 2025
0.1.0 Apr 5, 2025

#312 in Encoding

Download history 114/week @ 2025-04-02 16/week @ 2025-04-09

130 downloads per month

Custom license

255KB
3K SLoC

Emojfuscate: The ultimate human readable serialization format

Emojfuscate is a library that turns data into emoji. Other popular serialization formats, like JSON and XML, serialize data using scary symbols like {, [ and <. This is friendly to robots, not humans. Emojfuscate will encode your data using emoji, i.e. pure human emotion.

As a library Emojfuscate promises to bring concrete business value to your project by virtue of it being written in Rust and being smolderingly quick.

Let's see an example.

use emojfuscate::{ConstructFromEmoji, Demojfuscate, Emojfuscate};

#[derive(Emojfuscate, ConstructFromEmoji, Clone, Debug, PartialEq)]
struct Person {
    age: u8,
    name: String,
    is_cool: bool,
}

let original_person = Person {
    age: 33,
    name: "Axel".to_string(),
    is_cool: true,
};

let emojified = original_person.clone().emojfuscate();

// emojified: πŸ’ŸπŸ“°πŸ€©πŸ’΄πŸŽ…πŸ§ΆπŸƒπŸ·πŸ˜€πŸ’΅
println!("emojified: {}", emojified.clone());

let deserialized_person = emojified.demojfuscate();

assert_eq!(deserialized_person, Ok(original_person));

Laziness

To further embrace human properties, Emojfuscate is as lazy as possible. Any iterator, whose elements can be emojfuscated, can be turned into a lazy iterator of emoji.

Any lazy stream of emoji can be converted into a lazy stream of whatever type you want as long as it can be demojfuscated. Emojfuscate can cover all your emojfuscating needs in constant memory.

use emojfuscate::{ConstructFromEmoji, Demojfuscate, Emojfuscate, ConstructFromEmojiStream};

let source = std::iter::repeat("hello"); // infinite stream of String : Iterator<Item = String>

let demojfuscated: Result<Vec<String>, emojfuscate::FromEmojiError> = source
    .emojfuscate_stream() // infinite stream of emoji: Iterator<Item = char>
    .demojfuscate_stream() // infinite stream of hopefully String: Iterator<Item = Result<String, FromEmojiError>>
    .take(3) // finite stream of hopefully String: Iterator<Item = Result<String, FromEmojiError>>
    .collect(); // Result<Vec<String>, FromEmojiError>

assert_eq!(
    demojfuscated,
    Ok(vec![
        "hello".to_string(),
        "hello".to_string(),
        "hello".to_string()
    ])
);

How it works

Let's say we have a tuple of u8

let tuple = (1u8, 2u8, 3u8, 4u8, 5u8);

The raw bits will look like this:

    1        2        3        4        5
/---^--\ /---^--\ /---^--\ /---^--\ /---^--\
00000001 00000010 00000011 00000100 00000101

Every 10 bits will be mapped to one emoji

    4=πŸ˜†      32=🫣     193=πŸ‘       5=πŸ˜…
/----^----\/----^----\/----^----\/----^----\
00000001 00000010 00000011 00000100 00000101

But what if the number bits is not divisible by 10?

If we have a tuple with only two u8

let tuple = (1u8, 2u8);

Then the bits will be padded with zeros until we get an even multiple of 10

    4=πŸ˜†      32=🫣
/----^----\/----^----\
00000001 00000010 0000
                  \--/
                  four padded zeros

The emoji with the padded zeros will be prefixed with πŸ“° to signify four padded zeros. πŸ“ƒ, πŸ“œ, πŸ“„, πŸ“°, πŸ—ž, πŸ“‘, πŸ”–, 🏷 and πŸ’° signify that the next emoji has 1, 2, 3, 4, 5, 6, 7, 8 or 9 padded zeros respectively (though in practice it's impossible to be offset by an odd number since we're only dealing with an integer number of bytes).

So (1u8, 2u8) will be emojfuscated into πŸ˜†πŸ“°πŸ«£.

Limitations

The derive macro for Emojfuscate and ConstructFromEmoji for enums currently rely on representing the fields as tuples. At the moment Emojfuscate and ConstructFromEmoji are only implemented for tuples up to 24 elements. So you can't derive instances for enums where any of the constructors has more than 24 fields.

Luckily we have nifty macros for making tuple instances for both Emojfuscate and ConstructFromEmoji, so if you need more fields you can easily make a PR and just call the respective macros a few extra times until your needs are satisfied.

Dependencies

~1.4–2MB
~37K SLoC