Rust入门实战 编写Minecraft启动器#3解析资源配置

首发于Enaium的个人博客


在上一篇文章中,我们已经建立了资源模型,接下来我们需要解析游戏的配置文件。

首先我们添加serde_json依赖和model依赖。

toml 复制代码
model = { path = "../model" }
serde_json = "1.0"

之后我们在lib.rs中添加解析的trait

rust 复制代码
pub trait Parse<T>: Sized {
    type Error;
    fn parse(value: T) -> Result<Self, Self::Error>;
}

之后将所有的model都实现这个trait,并测试它们。这里其实只用将需要手动解析的实现这个trait,其他的会在我们用reqwest下载的时候自动解析。

asset.rs

rust 复制代码
use model::asset::*;

use crate::Parse;

impl Parse<&str> for AssetIndex {
    type Error = serde_json::Error;

    fn parse(value: &str) -> Result<Self, Self::Error> {
        serde_json::from_str::<AssetIndex>(value)
    }
}

impl Parse<&str> for Index {
    type Error = serde_json::Error;

    fn parse(value: &str) -> Result<Self, Self::Error> {
        serde_json::from_str::<Index>(value)
    }
}

impl Parse<&str> for Object {
    type Error = serde_json::Error;

    fn parse(value: &str) -> Result<Self, Self::Error> {
        serde_json::from_str::<Object>(value)
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn test_asset_index() {
        let asset_index = AssetIndex::parse(
        r#"{"id": "17", "sha1": "fab15439bdef669e389e25e815eee8f1b2aa915e", "size": 447033, "totalSize": 799252591, "url": "https://piston-meta.mojang.com/v1/packages/fab15439bdef669e389e25e815eee8f1b2aa915e/17.json"}"#).unwrap_or_else(|err| panic!("{:?}",err));
        assert_eq!("17", asset_index.id);
        assert_eq!("fab15439bdef669e389e25e815eee8f1b2aa915e", asset_index.sha1);
        assert_eq!(447033, asset_index.size);
        assert_eq!(799252591, asset_index.total_size);
        assert_eq!("https://piston-meta.mojang.com/v1/packages/fab15439bdef669e389e25e815eee8f1b2aa915e/17.json", asset_index.url);
    }

    #[test]
    fn test_index() {
        let index = Index::parse(r#"{"objects": {"icons/icon_128x128.png": {"hash": "b62ca8ec10d07e6bf5ac8dae0c8c1d2e6a1e3356", "size": 9101}}}"#)
            .unwrap_or_else(|err| panic!("{:?}",err));

        assert_eq!(1, index.objects.len());
        assert_eq!(
            "b62ca8ec10d07e6bf5ac8dae0c8c1d2e6a1e3356",
            index.objects.get("icons/icon_128x128.png").unwrap().hash
        );
        assert_eq!(
            9101,
            index.objects.get("icons/icon_128x128.png").unwrap().size
        );
    }
}

library.rs

rust 复制代码
use model::library::*;

use crate::Parse;

impl Parse<&str> for Library {
    type Error = serde_json::Error;

    fn parse(value: &str) -> Result<Self, Self::Error> {
        serde_json::from_str::<Library>(value)
    }
}

impl Parse<&str> for Rule {
    type Error = serde_json::Error;

    fn parse(value: &str) -> Result<Self, Self::Error> {
        serde_json::from_str::<Rule>(value)
    }
}

impl Parse<&str> for Os {
    type Error = serde_json::Error;

    fn parse(value: &str) -> Result<Self, Self::Error> {
        serde_json::from_str::<Os>(value)
    }
}

impl Parse<&str> for Download {
    type Error = serde_json::Error;

    fn parse(value: &str) -> Result<Self, Self::Error> {
        serde_json::from_str::<Download>(value)
    }
}

impl Parse<&str> for Artifact {
    type Error = serde_json::Error;

    fn parse(value: &str) -> Result<Self, Self::Error> {
        serde_json::from_str::<Artifact>(value)
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn test_library() {
        let library = Library::parse(
                r#"{"downloads": {"artifact": {"path": "ca/weblite/java-objc-bridge/1.1/java-objc-bridge-1.1.jar", "sha1": "1227f9e0666314f9de41477e3ec277e542ed7f7b", "size": 1330045, "url": "https://libraries.minecraft.net/ca/weblite/java-objc-bridge/1.1/java-objc-bridge-1.1.jar"}}, "name": "ca.weblite:java-objc-bridge:1.1", "rules": [{"action": "allow", "os": {"name": "osx"}}]}"#,
            ).unwrap_or_else(|err| panic!("{:?}",err));

        assert_eq!("ca.weblite:java-objc-bridge:1.1", library.name);
        assert_eq!(
            "ca/weblite/java-objc-bridge/1.1/java-objc-bridge-1.1.jar",
            library.downloads.artifact.path
        );
        assert_eq!(
            "1227f9e0666314f9de41477e3ec277e542ed7f7b",
            library.downloads.artifact.sha1
        );
        assert_eq!(1330045, library.downloads.artifact.size);
        assert_eq!(
            "https://libraries.minecraft.net/ca/weblite/java-objc-bridge/1.1/java-objc-bridge-1.1.jar",
            library.downloads.artifact.url
        );
        let rules = &library.rules.unwrap();
        assert_eq!("allow", rules[0].action);
        assert_eq!("osx", rules[0].os.name);
    }
}

version_manifest.rs

rust 复制代码
use model::version_manifest::*;

use crate::Parse;

impl Parse<&str> for VersionManifest {
    type Error = serde_json::Error;

    fn parse(value: &str) -> Result<Self, Self::Error> {
        serde_json::from_str::<VersionManifest>(value)
    }
}

impl Parse<&str> for Latest {
    type Error = serde_json::Error;

    fn parse(value: &str) -> Result<Self, Self::Error> {
        serde_json::from_str::<Latest>(value)
    }
}

impl Parse<&str> for Version {
    type Error = serde_json::Error;

    fn parse(value: &str) -> Result<Self, Self::Error> {
        serde_json::from_str::<Version>(value)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_version() {
        let version = Version::parse(
            r#"{"id": "1.21", "type": "release", "url": "https://piston-meta.mojang.com/v1/packages/177e49d3233cb6eac42f0495c0a48e719870c2ae/1.21.json", "time": "2024-06-13T08:32:38+00:00", "releaseTime": "2024-06-13T08:24:03+00:00"}"#,
        ).unwrap_or_else(|err| panic!("{:?}",err));

        assert_eq!("1.21", version.id);
        assert_eq!("release", version.type_);
        assert_eq!("https://piston-meta.mojang.com/v1/packages/177e49d3233cb6eac42f0495c0a48e719870c2ae/1.21.json", version.url);
        assert_eq!("2024-06-13T08:32:38+00:00", version.time);
        assert_eq!("2024-06-13T08:24:03+00:00", version.release_time);
    }

    #[test]
    fn test_latest() {
        let latest = Latest::parse(r#"{"release": "1.21", "snapshot": "1.21"}"#)
            .unwrap_or_else(|err| panic!("{:?}", err));
        assert_eq!("1.21", latest.release);
        assert_eq!("1.21", latest.snapshot);
    }

    #[test]
    fn test_version_manifest() {
        let version_manifest =
            VersionManifest::parse(r#"{"latest": {"release": "1.21", "snapshot": "1.21"}, "versions": [{"id": "1.21", "type": "release", "url": "https://piston-meta.mojang.com/v1/packages/177e49d3233cb6eac42f0495c0a48e719870c2ae/1.21.json", "time": "2024-06-13T08:32:38+00:00", "releaseTime": "2024-06-13T08:24:03+00:00"}]}"#).unwrap_or_else(|err| panic!("{:?}", err));
        assert_eq!("1.21", version_manifest.latest.release);
        assert_eq!("1.21", version_manifest.latest.snapshot);
        assert_eq!("1.21", version_manifest.versions[0].id);
        assert_eq!("release", version_manifest.versions[0].type_);
        assert_eq!("https://piston-meta.mojang.com/v1/packages/177e49d3233cb6eac42f0495c0a48e719870c2ae/1.21.json", version_manifest.versions[0].url);
        assert_eq!(
            "2024-06-13T08:32:38+00:00",
            version_manifest.versions[0].time
        );
        assert_eq!(
            "2024-06-13T08:24:03+00:00",
            version_manifest.versions[0].release_time
        );
    }
}

version.rs

rust 复制代码
use crate::Parse;
use model::version::*;

impl Parse<&str> for Version {
    type Error = serde_json::Error;

    fn parse(value: &str) -> Result<Self, Self::Error> {
        serde_json::from_str::<Version>(value)
    }
}

impl Parse<&str> for Download {
    type Error = serde_json::Error;

    fn parse(value: &str) -> Result<Self, Self::Error> {
        serde_json::from_str::<Download>(value)
    }
}

impl Parse<&str> for Client {
    type Error = serde_json::Error;

    fn parse(value: &str) -> Result<Self, Self::Error> {
        serde_json::from_str::<Client>(value)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_version() {
        let game = Version::parse(
            r#"{"downloads": {"client": {"sha1": "0e9a07b9bb3390602f977073aa12884a4ce12431", "size": 26836080, "url": "https://piston-data.mojang.com/v1/objects/0e9a07b9bb3390602f977073aa12884a4ce12431/client.jar"}}, "id": "1.21", "libraries": [], "mainClass": "net.minecraft.client.main.Main", "releaseTime": "2024-06-13T08:24:03+00:00", "time": "2024-06-13T08:32:38+00:00", "type": "release"}"#,
        ).unwrap_or_else(|err| panic!("{:?}",err));

        let client = &game.downloads.client;
        assert_eq!("0e9a07b9bb3390602f977073aa12884a4ce12431", client.sha1);
        assert_eq!(26836080, client.size);
        assert_eq!("https://piston-data.mojang.com/v1/objects/0e9a07b9bb3390602f977073aa12884a4ce12431/client.jar", client.url);

        assert_eq!("1.21", game.id);
        assert_eq!("net.minecraft.client.main.Main", game.main_class);
        assert_eq!("2024-06-13T08:24:03+00:00", game.release_time);
        assert_eq!("2024-06-13T08:32:38+00:00", game.time);
        assert_eq!("release", game.type_);
    }
}

最后我们将这些tait导出。

rust 复制代码
pub mod asset;
pub mod library;
pub mod version;
pub mod version_manifest;

项目地址

相关推荐
Swift社区1 小时前
在 Swift 中实现字符串分割问题:以字典中的单词构造句子
开发语言·ios·swift
没头脑的ht1 小时前
Swift内存访问冲突
开发语言·ios·swift
没头脑的ht1 小时前
Swift闭包的本质
开发语言·ios·swift
wjs20241 小时前
Swift 数组
开发语言
吾日三省吾码2 小时前
JVM 性能调优
java
stm 学习ing2 小时前
FPGA 第十讲 避免latch的产生
c语言·开发语言·单片机·嵌入式硬件·fpga开发·fpga
湫ccc3 小时前
《Python基础》之字符串格式化输出
开发语言·python
弗拉唐3 小时前
springBoot,mp,ssm整合案例
java·spring boot·mybatis
oi773 小时前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器