1 个不稳定版本

0.1.7 2023年4月14日
0.1.6 2023年4月14日

#757 in 编码

ISC许可证

100KB
2K SLoC

Rust对象格式 (.rof)

Rust对象格式允许将Rust对象序列化到一个与原始对象非常相似的文本格式文件中。它还允许轻松地在不同的编程语言之间传输对象,因为对象可以在各自的编程语言中创建,序列化为字符串,发送到另一种编程语言,然后反序列化并再次使用。

简而言之,这是一个可以将Rust对象序列化和反序列化为字符串的库,这允许在分离的编程语言之间简单保存和传输Rust对象。

Crates.io Docs.rs Github ISC licensed

cargo add rof-rs

高级API

#[derive(RofCompat)]
enum SongGenre {
    ROCK,
    POP,
    HIPHOP,
    RAP,
    JAZZ,
    COUNTRY,
    HEAVYMETAL,
    EDM,
    CLASSICAL,
}

#[derive(RofCompat)]
struct Song {
    song_title: String,
    song_author: String,
    timestamp: usize,
    song_genre: SongGenre
}

fn main() {
    let mut song =
            Song::load_from_file("C:\\songs\\song_32.rof");

    song.timestamp += 1; // Increment the timestamp by 1

    song.save_to_file(
            "C:\\songs\\song_32.rof",
            true /* pretty print option, adds tabs, spaces and newlines to make the file more human-readable, but will not change the data itself in any way */,
        )
        .expect("Could not save song to a file");
}

高级API可以通过使用RofCompat宏实现RofCompat特质。这个RofCompat特质允许您将对象反序列化为低级别的DataValue结构,然后可以将其保存到文件中。RofCompat特质还可以将低级别数据值结构反序列化回其原始形式。此外,RofCompat特质还提供了其他实用函数,例如:

  • serialize_to_string(&self, pretty_print: bool) -> String {}
  • deserialize_from_string(serialized_rof: &str) -> Self {}
  • as_rof(&self) -> Rof {}
  • load_from_file(file_path: &str) -> Self {}
  • save_to_file(&self, file_path: &str, pretty_print: bool) -> Result<(), ()> {}

几乎任何结构或枚举都可以通过使用其 derive 宏在这些要求下实现RofCompat特质:

  • 所有 RofCompat 对象都必须实现Default属性
  • 由于可能在未来解决的某些技术原因,无法序列化类似“对象”的结构和枚举值,例如数组(不是vecs)、切片、元组和其它。这些不可序列化的属性将简单地由 derive 宏忽略,如果您想实现它们,不幸的是您将不得不手动实现RofCompat特质。

如上所述,您可以手动实现RofCompat,如下面的示例所示,建议在尝试之前阅读低级API。这允许您更精确地控制您的结构/枚举在低级形式中的表示方式。

#[derive(Default)]
struct Color {
    r: u8,
    g: u8,
    b: u8,
}

impl RofCompat for Color {
    fn serialize(&self) -> Box<dyn DataValue> {
        Box::new(DataValueInteger::U32(
            65536 * (self.r as u32) + 256 * (self.g as u32) + (self.b as u32),
        ))
    }

    fn deserialize(rof_object: Box<dyn DataValue>) -> Self {
        let color_int: u32 = rof_object.as_u32();

        Self {
            r: (color_int % 16_777_216 / 65_536) as u8,
            g: (color_int % 65_536 / 256) as u8,
            b: (color_int % 256) as u8,
        }
    }
}

let mut color = Color::load_from_file("C:\\example_objects\\color.rof");

color.r = (color.r + 1) % 255;

color
    .save_to_file(
        "C:\\example_objects\\color.rof",
        true,
    )
    .expect("Could not save color to file");

实用函数是基于这两个函数为您实现的。正如您所看到的,现在存储的数据要简洁得多,这是一个很好的例子,即使不是总是必要的,但有时手动实现RofCompat也是一个好主意。

与低级API不同,使用RofCompat trait不需要对内部系统的工作方式有太多了解,并且可以快速设置。

低级API

Rof对象负责在Rof中对所有文件的序列化和反序列化,并支持高级RofCompat trait。您可以如下定义一个新的Rof

// Load computer file as a rof

let computer_rof =
    Rof::load_from_file("C:\\example_objects\\computer.rof");

// Convert the rof to a struct structure

let mut computer_structure = computer_rof.get_object().as_struct_structure();

let computer_name: String = match computer_structure.get("computer_name") {
    Some(loaded_computer_name) => loaded_computer_name.as_string(),
    None => String::default(), // ""
};

let mut computer_ram: usize = match computer_structure.get("computer_ram") {
    Some(loaded_computer_ram) => loaded_computer_ram.as_usize(),
    None => usize::default(), // 0
};

let computer_type: ComputerType = match computer_structure.get("computer_type") {
    Some(loaded_computer_type) => match loaded_computer_type.as_enum_structure().0.as_ref()
    {
        "windows" => ComputerType::WINDOWS,
        "linux" => ComputerType::LINUX,
        "macos" => ComputerType::MACOS,
        "reactos" => ComputerType::REACTOS,
        _ => ComputerType::REACTOS,
    },
    None => ComputerType::default(),
};

// Print out the parsed computer

println!(
    "Loaded {:?} Computer, named {} with {}gb of ram",
    computer_type, computer_name, computer_ram
);

// Modify the computer

computer_ram += 1;

// Convert the computer back into a struct structure

let computer_struct_properties: Vec<Property> = vec![
    Property::new(
        String::from("computer_name"),
        Box::new(DataValueString::new(computer_name)), // OR computer_name.serialize()
    ),
    Property::new(
        String::from("computer_ram"),
        Box::new(DataValueInteger::USIZE(computer_ram)), // OR computer_ram.serialize()
    ),
    Property::new(
        String::from("computer_type"),
        Box::new(DataValueEnum::new(
            match computer_type {
                ComputerType::WINDOWS => "windows",
                ComputerType::LINUX => "linux",
                ComputerType::MACOS => "macos",
                ComputerType::REACTOS => "reactos",
            }
            .to_string(),
            Vec::new(),
        )),
    ),
];

let computer_struct_structure = DataValueStruct::new(computer_struct_properties);

// Save rof to computer file

// Must create a new rof object, becausue the .get_object() function returns an immutable reference

Rof::new(Box::new(computer_struct_structure))
    .save_to_file(
        "C:\\example_objects\\computer.rof",
        true,
    )
    .expect("Could not save computer to file");

低级API对任何想要使用它的人来说都是可用的,尽管推荐使用高级RofCompat API,因为它更易读,需要更少的样板代码,并且更适合初学者。

依赖项

~255–700KB
~17K SLoC