qubit-mime

MIME type detection utilities for Rust based on filename glob rules and content magic

Rust CI Coverage Crates.io Rust License

面向 Rust 服务的 MIME 类型检测工具,基于文件名 glob 规则和内容魔数规则。

概述

Qubit MIME 是一个基于仓库的 Rust MIME 类型检测库。它使用 freedesktop shared MIME-info 数据模型:规范 MIME 类型名、别名、本地化说明、文件名 glob、内容魔数规则和父类型关系。

公开 API 分为三层:

设计目标

特性

文件名检测

内容魔数检测

仓库元数据

检测入口

媒体流分类

安装

Cargo.toml 中添加:

[dependencies]
qubit-mime = "0.6"

快速开始

根据文件名和内容检测

use qubit_mime::{
    MimeError,
    MimeDetectionPolicy,
    RepositoryMimeDetector,
};

fn main() -> Result<(), MimeError> {
    let detector = RepositoryMimeDetector::new()?;

    let by_name = detector.detect_by_filename("photo.JPG");
    assert_eq!(Some("image/jpeg".to_owned()), by_name);

    let by_content = detector.detect_by_content(b"%PDF-1.7\n");
    assert_eq!(Some("application/pdf".to_owned()), by_content);

    let combined = detector.detect_bytes(
        b"%PDF-1.7\n",
        Some("report.pdf"),
        MimeDetectionPolicy::VerifyContent,
    );
    assert_eq!(Some("application/pdf".to_owned()), combined);

    Ok(())
}

使用 Rust 风格的 MimeDetector trait

MimeDetectorRegistry 根据 MimeConfig 创建 boxed 或 shared MimeDetector trait object。只需要 MIME 名称的代码可以依赖 trait,而不是依赖 具体检测器类型。

use qubit_mime::{
    MimeConfig,
    MimeDetectionPolicy,
    MimeDetector,
    MimeDetectorRegistry,
    MimeError,
};

fn detect_upload(detector: &dyn MimeDetector, filename: &str, content: &[u8]) -> Option<String> {
    detector.detect(content, Some(filename), MimeDetectionPolicy::VerifyContent)
}

fn main() -> Result<(), MimeError> {
    let detector =
        MimeDetectorRegistry::default_registry()?.create_default_box(&MimeConfig::default())?;

    assert_eq!(
        Some("application/pdf".to_owned()),
        detect_upload(detector.as_ref(), "upload.bin", b"%PDF-1.7\n"),
    );
    Ok(())
}

使用 Config 配置全局默认值

MimeConfig::default() 返回当前全局默认 MIME 配置的快照。使用 MimeConfig::reload_default() 可以从 rs-configConfig 替换全局默认值; 使用 MimeConfig::reload_default_from_env() 可以通过 Config::from_env() 从 进程环境载入。

use qubit_config::Config;
use qubit_mime::{
    CONFIG_MEDIA_STREAM_CLASSIFIER_DEFAULT,
    CONFIG_MIME_AMBIGUOUS_MIME_MAPPING,
    CONFIG_MIME_DETECTOR_DEFAULT,
    CONFIG_MIME_DETECTOR_FALLBACKS,
    CONFIG_MIME_ENABLE_PRECISE_DETECTION,
    CONFIG_MIME_MAX_BUFFER_SIZE,
    CONFIG_MIME_PRECISE_DETECTION_PATTERNS,
    DEFAULT_AMBIGUOUS_MIME_MAPPING,
    DEFAULT_MIME_MAX_BUFFER_SIZE,
    DEFAULT_PRECISE_DETECTION_PATTERNS,
    MimeConfig,
    MimeDetector,
    MimeDetectorRegistry,
    MimeError,
};

fn main() -> Result<(), MimeError> {
    let original = MimeConfig::default();
    let mut config = Config::new();
    config.set(CONFIG_MIME_DETECTOR_DEFAULT, "repository")?;
    config.set(CONFIG_MIME_DETECTOR_FALLBACKS, "")?;
    config.set(CONFIG_MEDIA_STREAM_CLASSIFIER_DEFAULT, "ffprobe")?;
    config.set(CONFIG_MIME_ENABLE_PRECISE_DETECTION, true)?;
    config.set(CONFIG_MIME_PRECISE_DETECTION_PATTERNS, DEFAULT_PRECISE_DETECTION_PATTERNS)?;
    config.set(CONFIG_MIME_AMBIGUOUS_MIME_MAPPING, DEFAULT_AMBIGUOUS_MIME_MAPPING)?;
    config.set(CONFIG_MIME_MAX_BUFFER_SIZE, DEFAULT_MIME_MAX_BUFFER_SIZE)?;

    MimeConfig::reload_default(&config)?;
    let detector =
        MimeDetectorRegistry::default_registry()?.create_default_box(&MimeConfig::default())?;

    assert_eq!(
        Some("application/pdf".to_owned()),
        detector.detect_by_filename("document.pdf"),
    );

    MimeConfig::set_default(original);
    Ok(())
}

使用 registry 和 fallback 选择检测器

MimeDetectorRegistry::create_default_box()MimeDetectorRegistry::create_default_arc() 先尝试配置的默认 detector;如果该 provider 未知、不可用或初始化失败,则按配置的 fallback 链继续尝试。把默认 selector 设置为 auto 时,会从 registry 中按 provider 优先级选择当前可用的实现。

默认 registry 初始包含 MimeDetectorRegistry::builtin() 返回的内置 provider。 扩展 crate 可以在应用启动阶段调用 MimeDetectorRegistry::register_default(provider),把自己的 provider 加入这个 进程级默认 registry。注册成功后,后续任何地方调用 MimeDetectorRegistry::default_registry() 获取到的 registry 快照,都会通过同一个 进程级默认 registry 看到该 provider。

MimeDetectorRegistry::default_registry() 返回当前进程级 registry 的快照克隆。 修改这个快照不会更新全局 registry。需要让 provider 对默认构造器全局可见时, 使用 register_default();需要隔离环境、测试自定义 provider,或限制可选 provider 集合时,使用显式的 MimeDetectorRegistry::builtin()MimeDetectorRegistry::new()

全局注册只在当前进程内生效,通常应在根据配置创建 detector 之前执行一次。provider id 或 alias 重复时会返回 MimeError::DuplicateDetectorName;全局 registry 锁中毒时 会返回 MimeError::DetectorBackend

内置 detector selector:

Selector别名行为
repositoryrepository-mime-detector使用内置 freedesktop MIME 仓库
filefile-command, file-command-mime-detector文件名使用仓库,内容使用 file --mime-type --brief
auto-按优先级和 provider id 选择可用 provider
use qubit_config::Config;
use qubit_mime::{
    CONFIG_MIME_DETECTOR_DEFAULT,
    CONFIG_MIME_DETECTOR_FALLBACKS,
    MimeConfig,
    MimeDetector,
    MimeDetectorRegistry,
    MimeError,
};

fn main() -> Result<(), MimeError> {
    let mut source = Config::new();
    source.set(CONFIG_MIME_DETECTOR_DEFAULT, "file")?;
    source.set(CONFIG_MIME_DETECTOR_FALLBACKS, "repository")?;

    let config = MimeConfig::from_config(&source)?;
    let detector = MimeDetectorRegistry::default_registry()?.create_default_box(&config)?;

    assert_eq!(
        Some("image/png".to_owned()),
        detector.detect_by_filename("image.png"),
    );
    Ok(())
}

需要自定义 provider 时,使用显式 registry:

use qubit_mime::{
    MimeConfig,
    MimeDetector,
    MimeDetectorRegistry,
    MimeError,
};

fn main() -> Result<(), MimeError> {
    let registry = MimeDetectorRegistry::builtin();
    let detector = registry.create_box("repository-mime-detector", &MimeConfig::default())?;

    assert_eq!(
        Some("text/plain".to_owned()),
        detector.detect_by_filename("notes.txt"),
    );
    Ok(())
}

Registry 选择相关错误:

错误含义
DuplicateDetectorNameprovider id 或 alias 与已有 provider 冲突
UnknownDetector没有已注册 provider 匹配请求的 selector
DetectorUnavailableprovider 存在,但当前进程环境中后端不可用
NoAvailableDetector默认候选和所有 fallback 候选都失败
DetectorBackendprovider 后端在初始化或检测过程中失败

配置键

MimeConfig::from_config() 同时接受逻辑键和环境变量风格键。环境变量使用环境变量 风格名称。列表值既可以是数组,也可以是用 ,; 分隔的字符串;空项会被忽略。 有歧义 MIME 映射按 ; 分隔,每项格式为 extension:video-mime,audio-mime

设置逻辑键环境键默认值
默认 MIME detectormime.detector.defaultQUBIT_MIME_DETECTOR_DEFAULTrepository
MIME detector fallbackmime.detector.fallbacksQUBIT_MIME_DETECTOR_FALLBACKS
媒体流 classifiermime.media.stream.classifier.defaultQUBIT_MEDIA_STREAM_CLASSIFIER_DEFAULTffprobe
启用精确检测mime.enable.precise.detectionQUBIT_MIME_ENABLE_PRECISE_DETECTIONtrue
精确检测扩展名mime.precise.detection.patternsQUBIT_MIME_PRECISE_DETECTION_PATTERNSwebm,ogg
有歧义 MIME 映射mime.ambiguous.mime.mappingQUBIT_MIME_AMBIGUOUS_MIME_MAPPINGwebm:video/webm,audio/webm;ogg:video/ogg,audio/ogg
detector 最大 buffer 大小mime.max.buffer.sizeQUBIT_MIME_MAX_BUFFER_SIZE16777216

检测文件系统路径

use qubit_mime::{
    MimeDetectionPolicy,
    MimeError,
    RepositoryMimeDetector,
};

fn main() -> Result<(), MimeError> {
    let detector = RepositoryMimeDetector::new()?;
    let path = std::env::temp_dir().join("qubit-mime-example.pdf");

    std::fs::write(&path, b"%PDF-1.7\n")?;
    let detected = detector.detect_file(&path, MimeDetectionPolicy::VerifyContent)?;
    std::fs::remove_file(&path).ok();

    assert_eq!(Some("application/pdf".to_owned()), detected);
    Ok(())
}

使用系统 file 命令检测器

FileCommandMimeDetector 使用内置仓库做文件名候选检测,并使用 file --mime-type --brief 做内容检测。

use std::time::Duration;

use qubit_command::CommandRunner;
use qubit_mime::{
    FileCommandMimeDetector,
    MimeDetectionPolicy,
    MimeDetector,
    MimeError,
};

fn main() -> Result<(), MimeError> {
    if !FileCommandMimeDetector::is_available() {
        return Ok(());
    }

    let detector = FileCommandMimeDetector::new();
    let detected = detector.detect(
        b"%PDF-1.7\n",
        Some("report.bin"),
        MimeDetectionPolicy::VerifyContent,
    );

    assert_eq!(Some("application/pdf".to_owned()), detected);

    let runner = CommandRunner::new()
        .timeout(Duration::from_secs(2))
        .disable_logging(true);
    let detector = FileCommandMimeDetector::new().with_command_runner(runner);
    assert!(detector.command_runner().configured_timeout().is_some());

    Ok(())
}

使用 FFprobe 分类媒体流

FfprobeCommandMediaStreamClassifier 可以把媒体文件分类为无媒体流、纯音频、 纯视频或音视频都有。

use std::path::Path;

use qubit_mime::{
    FfprobeCommandMediaStreamClassifier,
    MediaStreamClassifier,
    MediaStreamType,
    MimeError,
};

fn main() -> Result<(), MimeError> {
    if !FfprobeCommandMediaStreamClassifier::is_available() {
        return Ok(());
    }

    let classifier = FfprobeCommandMediaStreamClassifier::new();
    let stream_type = classifier.classify_file(Path::new("sample.webm"))?;

    assert!(matches!(
        stream_type,
        MediaStreamType::AudioOnly
            | MediaStreamType::VideoOnly
            | MediaStreamType::VideoWithAudio
            | MediaStreamType::None,
    ));
    Ok(())
}

从可 seek 的 reader 检测

detect_reader 会读取仓库中 magic 规则所需的最大字节数,然后恢复 reader 原来的 流位置。

use std::io::{
    Cursor,
    Seek,
};

use qubit_mime::{
    MimeDetectionPolicy,
    MimeError,
    RepositoryMimeDetector,
};

fn main() -> Result<(), MimeError> {
    let detector = RepositoryMimeDetector::new()?;
    let mut reader = Cursor::new(b"%PDF-1.7\npayload".to_vec());

    let detected = detector.detect_reader(
        &mut reader,
        Some("document.bin"),
        MimeDetectionPolicy::VerifyContent,
    )?;
    assert_eq!(Some("application/pdf".to_owned()), detected);
    assert_eq!(0, reader.stream_position()?);

    Ok(())
}

组合检测策略

组合检测同时接收文件名和内容字节。MimeDetectionPolicy 会让每次调用都显式声明 文件名结果和内容结果的取舍策略。

use qubit_mime::{
    MimeDetectionPolicy,
    MimeError,
    RepositoryMimeDetector,
};

fn main() -> Result<(), MimeError> {
    let detector = RepositoryMimeDetector::new()?;
    let pdf_bytes = b"%PDF-1.7\n";

    // 优先使用确定的文件名结果,避免额外检查内容。
    let by_filename = detector.detect_bytes(
        pdf_bytes,
        Some("photo.jpg"),
        MimeDetectionPolicy::PreferFilename,
    );
    assert_eq!(Some("image/jpeg".to_owned()), by_filename);

    // 当内容更可信时,明确检查 magic。
    let by_magic = detector.detect_bytes(
        pdf_bytes,
        Some("photo.jpg"),
        MimeDetectionPolicy::VerifyContent,
    );
    assert_eq!(Some("application/pdf".to_owned()), by_magic);

    Ok(())
}

当文件名来自可信来源且希望减少 I/O 时,使用 MimeDetectionPolicy::PreferFilename。当文件名来自用户上传或可能被伪造时,使用 MimeDetectionPolicy::VerifyContent 更稳妥。

仓库元数据

默认检测器会暴露已解析的仓库。当你不仅需要 MIME 名称,还需要扩展名、别名或说明时, 可以直接使用仓库 API。

use qubit_mime::{
    MimeError,
    RepositoryMimeDetector,
};

fn main() -> Result<(), MimeError> {
    let detector = RepositoryMimeDetector::new()?;
    let repository = detector.repository();

    let png = repository
        .get("image/png")
        .expect("default repository should contain image/png");

    assert_eq!("image/png", png.name());
    assert_eq!(Some("png"), png.preferred_extension());
    assert!(png.description().is_some());
    assert!(png.matches_filename("ICON.PNG"));

    let extensions = png.all_extensions();
    assert!(extensions.contains(&"png"));

    Ok(())
}

MimeRepository::detect_by_filenameMimeRepository::detect_by_content 会返回所有最佳候选项,而不是只返回一个字符串:

use qubit_mime::{
    MimeError,
    RepositoryMimeDetector,
};

fn main() -> Result<(), MimeError> {
    let detector = RepositoryMimeDetector::new()?;
    let repository = detector.repository();

    let candidates = repository.detect_by_filename("archive.tar.gz");
    assert!(!candidates.is_empty());

    for candidate in candidates {
        println!("{} {:?}", candidate.name(), candidate.description());
    }

    Ok(())
}

自定义仓库

当需要小型测试仓库、产品自定义 MIME 数据库,或使用其他来源生成的数据库时,可使用 MimeRepository::from_xml

use qubit_mime::{
    MimeError,
    MimeRepository,
    RepositoryMimeDetector,
};

fn main() -> Result<(), MimeError> {
    let xml = r#"
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
  <mime-type type="application/x-example">
    <comment>Example bundle</comment>
    <alias type="application/example"/>
    <glob pattern="*.example" weight="80"/>
    <magic priority="90">
      <match type="string" value="EXAMPLE" offset="0"/>
    </magic>
  </mime-type>
</mime-info>
"#;

    let repository = MimeRepository::from_xml(xml)?;
    let detector = RepositoryMimeDetector::with_repository(&repository);

    assert_eq!(
        Some("application/x-example".to_owned()),
        detector.detect_by_filename("demo.example"),
    );
    assert_eq!(
        Some("application/x-example".to_owned()),
        detector.detect_by_content(b"EXAMPLE payload"),
    );

    let mime_type = repository
        .get("application/example")
        .expect("alias should resolve to the canonical MIME type");
    assert_eq!("application/x-example", mime_type.name());
    assert_eq!(Some("example"), mime_type.preferred_extension());

    Ok(())
}

底层规则类型

底层类型适合在测试或集成代码中直接检查、构造或验证 MIME 规则。

use qubit_mime::{
    MagicValueType,
    MimeError,
    MimeGlob,
    MimeMagic,
    MimeMagicMatcher,
    MimeType,
};

fn main() -> Result<(), MimeError> {
    let glob = MimeGlob::new("*.png", MimeGlob::DEFAULT_WEIGHT, false)?;
    assert!(glob.matches("ICON.PNG"));

    let matcher = MimeMagicMatcher::new(
        MagicValueType::String,
        0,
        0,
        b"\x89PNG\r\n\x1a\n".to_vec(),
        None,
        vec![],
    )?;
    let magic = MimeMagic::new(80, vec![matcher]);

    let png = MimeType::builder("image/png")
        .description("en", "PNG image")
        .alias("image/x-png")
        .glob(glob)
        .magic(magic)
        .build();

    assert_eq!("image/png", png.name());
    assert_eq!(Some("png"), png.preferred_extension());
    assert!(png.matches_filename("icon.png"));
    assert!(png.magics()[0].matches(b"\x89PNG\r\n\x1a\npayload"));

    Ok(())
}

API 参考

MimeDetector

方法描述
MimeDetectorRegistry::builtin()创建只包含内置 detector provider 的隔离 registry
MimeDetectorRegistry::default_registry()获取进程级默认 detector registry 的快照
MimeDetectorRegistry::register_default(provider)将外部 detector provider 注册到全局默认 registry
MimeDetectorRegistry::register(provider)将外部 detector provider 注册到显式 registry
MimeDetectorRegistry::create_box(name, config)按 provider id 或 alias 创建 boxed 检测器
MimeDetectorRegistry::create_arc(name, config)按 provider id 或 alias 创建共享检测器
MimeDetectorRegistry::create_default_box(config)将配置的默认值、auto 和 fallback 链解析为 boxed 检测器
MimeDetectorRegistry::create_default_arc(config)将配置的默认值、auto 和 fallback 链解析为共享检测器
MimeDetectorProvider可插拔 detector 实现的工厂 trait
detect_by_filename(filename)根据文件名检测一个 MIME 名称
detect_by_content(bytes)根据内容字节检测一个 MIME 名称
detect(bytes, filename, policy)根据字节和可选文件名检测

RepositoryMimeDetector

方法描述
new()创建使用内置 freedesktop 仓库的检测器
with_repository(repository)创建借用显式仓库的检测器
with_repository_and_config(repository, config)创建借用显式仓库和 MIME 配置的检测器
repository()借用底层仓库
detect_by_filename(filename)返回文件名匹配到的第一个 MIME 名称
detect_by_content(bytes)返回内容 magic 匹配到的第一个 MIME 名称
detect_bytes(bytes, filename, policy)根据字节和可选文件名检测
detect_reader(reader, filename, policy)Read + Seek reader 检测并恢复位置
detect_file(file, policy)打开并检测本地文件路径

FileCommandMimeDetector

方法描述
new()创建使用内置仓库和系统 file 命令的检测器
with_repository(repository)创建借用显式仓库的检测器
with_repository_and_runner(repository, runner)使用显式 qubit_command::CommandRunner 创建检测器
with_repository_runner_and_config(repository, runner, config)使用显式仓库、runner 和 MIME 配置创建检测器
command_runner()借用用于命令执行的 runner
set_command_runner(runner)替换用于命令执行的 runner
is_available()检查 file 命令是否可执行
detect_file_by_content(file)只根据命令输出检测本地文件
detect_file(file, policy)根据文件名和命令支持的内容检测来检测本地文件
detect_reader(reader, filename, policy)通过 file-backed 路径检测可 seek reader

MediaStreamClassifier

方法描述
MediaStreamClassifierRegistry::builtin()创建只包含内置 classifier provider 的隔离 registry
MediaStreamClassifierRegistry::default_registry()获取进程级默认 classifier registry 的快照
MediaStreamClassifierRegistry::register_default(provider)将外部 classifier provider 注册到全局默认 registry
MediaStreamClassifierRegistry::register(provider)将外部 classifier provider 注册到显式 registry
MediaStreamClassifierRegistry::create_box(name, config)按 provider id 或 alias 创建 boxed classifier
MediaStreamClassifierRegistry::create_arc(name, config)按 provider id 或 alias 创建共享 classifier
MediaStreamClassifierRegistry::create_default_box(config)将配置的默认值或 auto 解析为 boxed classifier
MediaStreamClassifierRegistry::create_default_arc(config)将配置的默认值或 auto 解析为共享 classifier
MediaStreamClassifierProvider可插拔 classifier 实现的工厂 trait
classify_file(file)分类本地媒体文件
classify_reader(reader)从 reader 分类媒体内容
classify_content(bytes)分类内存中的媒体内容

FfprobeCommandMediaStreamClassifier

方法描述
new()创建基于 FFprobe 的 classifier
is_available()检查 ffprobe 是否可执行
classify_stream_listing(output)分类 FFprobe codec_type 输出
set_working_directory(directory)设置命令工作目录

MimeRepository

方法描述
from_xml(xml)解析 freedesktop shared MIME-info XML 文档
empty()创建空仓库
all()按数据库顺序返回所有 MIME 类型
get(name)解析规范 MIME 名称或别名
max_test_bytes()返回 magic 规则需要的最大内容前缀长度
detect_by_filename(filename)返回最佳文件名候选项
detect_by_content(bytes)返回最佳内容候选项
detect(filename, bytes, policy)合并文件名和内容检测

元数据与规则类型

类型用途
MimeType单个 MIME 类型的元数据和规则
MimeTypeBuilder构造独立 MimeType 值的 builder
MimeGlob带权重和大小写敏感设置的文件名 glob 规则
MimeMagic带优先级的一组 magic matcher
MimeMagicMatcher带 offset、value、mask 和子 matcher 的单个 magic 匹配器
MagicValueTypefreedesktop magic value type 枚举
MediaStreamType音视频流分类结果
MimeConfig精确检测和有歧义媒体映射配置
MimeErrorXML 解析、规则校验和 I/O 错误类型

MimeConfig

方法描述
from_config(config)qubit_config::Config 解析 MIME 配置
from_env()Config::from_env() 解析 MIME 配置
default()克隆当前全局默认 MIME 配置
set_default(config)替换未来默认实例使用的全局默认配置
reload_default(config)Config 解析并替换全局默认配置
reload_default_from_env()从进程环境解析并替换全局默认配置
mime_detector_default()读取配置的 detector selector
mime_detector_fallbacks()读取配置的 detector fallback 链
media_stream_classifier_default()读取配置的媒体 classifier selector

模块结构

源码结构按 detector、classifier 和 repository 职责组织:

src/
  mime_detector.rs              # 顶层 MimeDetector trait
  mime_config.rs                # 精确检测配置
  detector/                     # detector 实现
  classifier/                   # 媒体流 classifier 接口与实现
  repository/                   # MIME 数据库、glob、magic 和元数据类型

普通业务代码优先使用 crate root 的 re-export。只有在需要查看或扩展具体 detector、 classifier 或 repository 组件时,才需要直接使用嵌套模块。

检测规则

文件名规则

文件名检测只使用路径的最后一个组件。匹配结果按以下顺序排序:

  1. glob 权重更高者优先。
  2. 权重相同时,glob 模式更长者优先。
  3. 仓库 API 在多个候选项完全并列时保留数据库顺序。

内容规则

内容检测会用传入的字节前缀检查每个 MIME 类型的直接 magic 规则。结果按更高 magic 优先级排序。可使用 repository.max_test_bytes() 获取当前仓库最值得读取的 最大前缀长度。

组合规则

组合检测会先执行文件名 glob 检测。当文件名只有一个匹配项且未强制检查 magic 时, 直接使用该文件名结果。否则继续执行内容 magic 检测,并与文件名候选项合并。

与 Java common-mime 对比

方面Java common-mimeQubit MIME
数据库模型Freedesktop shared MIME-info相同模型
文件名检测Glob 规则Glob 规则
内容检测Magic 规则Magic 规则
别名查找支持支持
检测器接口MimeDetectorMimeDetector trait
媒体流分类接口MediaStreamClassifierMediaStreamClassifier trait
仓库检测器RepositoryMimeDetectorRepositoryMimeDetector
file 命令检测器FileCommandMimeDetectorFileCommandMimeDetector
FFprobe classifierFfprobeCommandMediaStreamClassifierFfprobeCommandMediaStreamClassifier
仓库加载XML 资源内置 XML 或显式 XML
返回风格Java 对象与集合Rust Option、slice 和 vector
错误处理Java 异常具体 MimeError

测试与代码覆盖率

本项目测试统一放在 tests/ 目录下,覆盖仓库解析、文件名匹配、内容 magic 匹配、 reader/path 检测和覆盖率阈值。

运行测试

# 运行所有测试
cargo test

# 生成覆盖率报告
./coverage.sh

# 生成文本格式覆盖率报告
./coverage.sh text

# 运行 CI 检查(格式化、clippy、测试、文档、覆盖率、audit)
./ci-check.sh

依赖项

运行时依赖保持很少:

许可证

Copyright (c) 2026. Haixing Hu, Qubit Co. Ltd. All rights reserved.

根据 Apache 许可证 2.0 版("许可证")授权; 除非遵守许可证,否则您不得使用此文件。 您可以在以下位置获取许可证副本:

<http://www.apache.org/licenses/LICENSE-2.0>

除非适用法律要求或书面同意,否则根据许可证分发的软件 按"原样"分发,不附带任何明示或暗示的担保或条件。 有关许可证下的特定语言管理权限和限制,请参阅许可证。

完整的许可证文本请参阅 LICENSE

贡献

欢迎贡献。请保持改动与现有 Rust 项目结构一致,并在提交 Pull Request 前运行 ./ci-check.sh

作者

胡海星 - Qubit Co. Ltd.

相关项目

Qubit 旗下的更多 Rust 库发布在 GitHub 组织 qubit-ltd

---

仓库地址:https://github.com/qubit-ltd/rs-mime