写一个 Rust 小应用 pngme - 3.实现PNG

实现了 ChunkTypeChunk 结构体之后,接下来实现一个 Png 结构体,用于表示和操作 PNG 文件。它包含了 PNG 文件的标准头和一系列数据块(Chunk),并提供了从字节数据创建 Png 实例、添加和移除数据块、查找特定类型数据块以及将 Png 实例转换为字节数据等功能。

rust 复制代码
use std::convert::TryFrom;
use std::fmt;
use anyhow::{Result, Error};
use std::str::FromStr;
use crate::chunk::Chunk;
use crate::chunk_type::ChunkType;

导入必要的模块和类型。std::convert::TryFrom 用于实现从字节切片转换为 Png 实例;std::fmt 用于实现 Display trait,以便格式化输出 Png 实例;anyhow 提供了统一的错误处理机制;std::str::FromStr 用于将字符串转换为特定类型;crate::chunk::Chunkcrate::chunk_type::ChunkType 分别表示 PNG 文件的数据块和数据块类型。

Png 结构体定义
rust 复制代码
pub struct Png {
    header: [u8; 8],
    chunks: Vec<Chunk>,
}

Png 结构体包含两个字段:header 是一个长度为 8 的字节数组,用于存储 PNG 文件的标准头;chunks 是一个 Chunk 类型的向量,用于存储 PNG 文件的所有数据块。

常量定义与构造函数
rust 复制代码
impl Png {
    // 定义标准的 PNG 文件头常量
    pub const STANDARD_HEADER: [u8; 8] = [137, 80, 78, 71, 13, 10, 26, 10];

    // 从数据块向量创建 Png 实例
    pub fn from_chunks(chunks: Vec<Chunk>) -> Png {
        Png {
            header: Self::STANDARD_HEADER,
            chunks,
        }
    }

    // 向 Png 实例中追加一个数据块
    pub fn append_chunk(&mut self, chunk: Chunk) {
        self.chunks.push(chunk);
    }

    // 移除第一个指定类型的数据块
    pub fn remove_first_chunk(&mut self, chunk_type: &str) -> Result<Chunk> {
        let chunk_type = ChunkType::from_str(chunk_type)?;
        if let Some(index) = self.chunks.iter().position(|c| *c.chunk_type() == chunk_type) {
            Ok(self.chunks.remove(index))
        } else {
            Err(Error::msg(format!("Chunk of type {} not found", chunk_type)))
        }
    }

    // 返回 PNG 文件头的引用
    pub fn header(&self) -> &[u8; 8] {
        &self.header
    }

    // 返回所有数据块的切片
    pub fn chunks(&self) -> &[Chunk] {
        &self.chunks
    }

    // 根据数据块类型查找数据块
    pub fn chunk_by_type(&self, chunk_type: &str) -> Option<&Chunk> {
        let chunk_type = match ChunkType::from_str(chunk_type) {
            Ok(ct) => ct,
            Err(_) => return None,
        };
        self.chunks.iter().find(|c| *c.chunk_type() == chunk_type)
    }

    // 将整个 PNG 文件转换为字节向量
    pub fn as_bytes(&self) -> Vec<u8> {
        let mut bytes = Vec::from(self.header);
        for chunk in &self.chunks {
            bytes.extend_from_slice(&chunk.as_bytes());
        }
        bytes
    }
}
从字节切片转换为 Png 实例
rust 复制代码
// 实现从字节切片转换为 Png 实例
impl TryFrom<&[u8]> for Png {
    type Error = Error;

    fn try_from(value: &[u8]) -> Result<Self> {
        if value.len() < 8 || &value[0..8] != &Self::STANDARD_HEADER {
            return Err(Error::msg("Invalid PNG header"));
        }
        let mut chunks = Vec::new();
        let mut index = 8;
        while index < value.len() {
            let chunk = Chunk::try_from(&value[index..])?;
            chunks.push(chunk.clone());
            index += 4 + 4 + chunk.length() as usize + 4;
        }
        Ok(Png {
            header: Self::STANDARD_HEADER,
            chunks,
        })
    }
}
格式化输出
rust 复制代码
impl fmt::Display for Png {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "PNG File:")?;
        writeln!(f, "  Header: {:?}", self.header)?;
        writeln!(f, "  Chunks:")?;
        for chunk in &self.chunks {
            writeln!(f, "    {}", chunk)?;
        }
        Ok(())
    }
}

以上就是通过定义 Png 结构体和相关方法,提供了一个完整的 PNG 文件解析和操作方案。

相关推荐
本地化文档12 分钟前
rustdoc-book-l10n
rust·github·gitcode
Tony Bai3 小时前
Rust 看了流泪,AI 看了沉默:扒开 Go 泛型最让你抓狂的“残疾”类型推断
开发语言·人工智能·后端·golang·rust
jump_jump4 小时前
RTK:给 AI 编码助手瘦身的 Rust 代理
性能优化·rust·claude
小杍随笔9 小时前
【Rust Exercism 练习详解:Anagram + Space Age + Sublist(附完整代码与深度解读)】
开发语言·rust·c#
Rust研习社11 小时前
Rust 字符串与切片实战
rust
朝阳58111 小时前
局域网聊天工具
javascript·rust
朝阳58111 小时前
我做了一个局域网传文件的小工具,记录一下
javascript·rust
Rust语言中文社区1 天前
【Rust日报】用 Rust 重写的 Turso 是一个更好的 SQLite 吗?
开发语言·数据库·后端·rust·sqlite
小杍随笔1 天前
【Rust 半小时速成(2024 Edition 更新版)】
开发语言·后端·rust
Source.Liu1 天前
【office2pdf】office2pdf 纯 Rust 实现的 Office 转 PDF 库
rust·pdf·office2pdf