Skip to content

Commit e7ee9ea

Browse files
authoredFeb 5, 2023
docs: Add crate documentation (#21)
This (hopefully 🤞) adds all of the necessary documentation. Closes #2.
2 parents e1980d7 + c9f5209 commit e7ee9ea

14 files changed

+480
-1
lines changed
 

‎src/entries.rs

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
//! Collection of entries from the parsed flatbuffer schema.
2+
//!
3+
//! NOTE: these structs are only useful for viewing the internal structure of the parsed
4+
//! flatbuffer schema. If you just wish to see the containing files and download them, see
5+
//! [File][crate::File].
6+
17
mod bundle_entry;
28
mod chunk_entry;
39
mod directory_entry;

‎src/entries/bundle_entry.rs

+8
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,18 @@ use crate::generated::rman::Bundle;
22

33
use super::chunk_entry::ChunkEntry;
44

5+
/// Single bundle entry object.
6+
///
7+
/// This is identical to the schema in [rman-schema][rman-schema] and exists to provide a
8+
/// persistent structure for the BundleEntry.
9+
///
10+
/// [rman-schema]: https://github.com/ev3nvy/rman-schema
511
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
612
#[derive(Debug, Default, Clone, PartialEq, Eq)]
713
pub struct BundleEntry {
14+
/// Id of the bundle entry.
815
pub id: u64,
16+
/// A vector of chunk entries.
917
pub chunks: Vec<ChunkEntry>,
1018
}
1119

‎src/entries/chunk_entry.rs

+13
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
11
use crate::generated::rman::Chunk;
22

3+
/// Single chunk entry object.
4+
///
5+
/// This is identical to the schema in [rman-schema][rman-schema] and exists to provide a
6+
/// persistent structure for the ChunkEntry.
7+
///
8+
/// [rman-schema]: https://github.com/ev3nvy/rman-schema
39
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
410
#[derive(Debug, Default, Clone, PartialEq, Eq)]
511
pub struct ChunkEntry {
12+
/// Id of the chunk entry.
613
pub id: u64,
14+
/// Chunk size before decompression.
15+
///
16+
/// Mainly used when downloading files.
717
pub compressed_size: u32,
18+
/// Chunk size after decompression.
19+
///
20+
/// Mainly used when downloading files.
821
pub uncompressed_size: u32,
922
}
1023

‎src/entries/directory_entry.rs

+12
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
11
use crate::generated::rman::Directory;
22

3+
/// Single directory entry object.
4+
///
5+
/// This is identical to the schema in [rman-schema][rman-schema] and exists to provide a
6+
/// persistent structure for the DirectoryEntry.
7+
///
8+
/// [rman-schema]: https://github.com/ev3nvy/rman-schema
39
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
410
#[derive(Debug, Default, Clone, PartialEq, Eq)]
511
pub struct DirectoryEntry {
12+
/// Id of the directory entry.
613
pub id: u64,
14+
/// Id of the parent directory entry.
15+
///
16+
/// NOTE: root directory (which is tipically the first DirectoryEntry in the vector) typically
17+
/// has an `id` of 0, yet still has a `parent_id` of 0.
718
pub parent_id: u64,
19+
/// Name of the directory entry.
820
pub name: String,
921
}
1022

‎src/entries/file_entry.rs

+21
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,41 @@
11
use crate::generated::rman::File;
22

3+
/// Single file entry object.
4+
///
5+
/// This is identical to the schema in [rman-schema][rman-schema] and exists to provide a
6+
/// persistent structure for the FileEntry.
7+
///
8+
/// [rman-schema]: https://github.com/ev3nvy/rman-schema
39
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
410
#[derive(Debug, Default, Clone, PartialEq, Eq)]
511
pub struct FileEntry {
12+
/// Id of the file entry.
613
pub id: u64,
14+
/// Id of the directory entry, to which it belongs.
715
pub directory_id: u64,
16+
/// Size of the file entry in bytes.
817
pub size: u32,
18+
/// Name of the file entry.
919
pub name: String,
20+
/// Applicable languages, stored as a bit mask.
1021
pub language_mask: u64,
22+
/// Field with an unknown function and type (it might also be an [`i8`]).
1123
pub unk5: u8,
24+
/// Field with an unknown function and type (it might also be an [`i8`]).
1225
pub unk6: u8,
26+
/// A vector of [chunk ids](crate::entries::ChunkEntry::id) that make up the file.
1327
pub chunk_ids: Vec<u64>,
28+
/// Field with an unknown function and type (it might also be an [`i8`]).
29+
///
30+
/// NOTE: seems to always be 1 when a part of `.app` file on macOS.
1431
pub unk8: u8,
32+
/// Symbolic link of the file entry.
1533
pub symlink: String,
34+
/// Field with an unknown function and type (it might also be an [`i16`]).
1635
pub unk10: u16,
36+
/// Id of the param entry, which provides info about content-defined chunking.
1737
pub param_id: u8,
38+
/// Permissions for the given file entry.
1839
pub permissions: u8,
1940
}
2041

‎src/entries/key_entry.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
use crate::generated::rman::Key;
22

3+
/// Single key entry object.
4+
///
5+
/// This is identical to the schema in [rman-schema][rman-schema] and exists to provide a
6+
/// persistent structure for the KeyEntry.
7+
///
8+
/// [rman-schema]: https://github.com/ev3nvy/rman-schema
39
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
410
#[derive(Debug, Default, Clone, PartialEq, Eq)]
511
pub struct KeyEntry {
12+
/// Field with an unknown function and type (it might also be an [`i16`]).
613
pub unk0: u16,
14+
/// Field with an unknown function and type (it might also be an [`i32`]).
715
pub unk1: u32,
816
}
917

‎src/entries/language_entry.rs

+19
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,28 @@
11
use crate::generated::rman::Language;
22

3+
/// Single language entry object.
4+
///
5+
/// This is identical to the schema in [rman-schema][rman-schema] and exists to provide a
6+
/// persistent structure for the LanguageEntry.
7+
///
8+
/// [rman-schema]: https://github.com/ev3nvy/rman-schema
39
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
410
#[derive(Debug, Default, Clone, PartialEq, Eq)]
511
pub struct LanguageEntry {
12+
/// Id of the language entry.
613
pub id: u8,
14+
/// Name of the language in the language-region variant of the [RFC 5646 standard][rfc-5646],
15+
/// however, underscores are used instead of hyphens.
16+
///
17+
/// There are also non languages present.
18+
/// A non-exhaustive list of values that are not languagues:
19+
/// - `krrating`
20+
/// - `mature`
21+
/// - `twmlogo`
22+
/// - `vnglogo`
23+
/// - `all_loc`
24+
///
25+
/// [rfc-5646]: https://www.rfc-editor.org/rfc/rfc5646.html
726
pub name: String,
827
}
928

‎src/entries/param_entry.rs

+38
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,50 @@
11
use crate::generated::rman::Param;
22

3+
/// Single param entry object.
4+
///
5+
/// This is identical to the schema in [rman-schema][rman-schema] and exists to provide a
6+
/// persistent structure for the ParamEntry.
7+
///
8+
/// [rman-schema]: https://github.com/ev3nvy/rman-schema
39
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
410
#[derive(Debug, Default, Clone, PartialEq, Eq)]
511
pub struct ParamEntry {
12+
/// Field with an unknown function and type (it might also be an [`i16`]).
613
pub unk0: u16,
14+
/// Determines the hash type used when generating chunks.
15+
///
16+
/// - 0 - Invalid/None
17+
/// - 1 - SHA256
18+
/// - 2 - SHA512
19+
/// - 3 - RIOT_HKDF
20+
///
21+
/// These values are copied straight from
22+
/// [moonshadow565's implementation][moonshadow565-rman-rchunk].
23+
/// More about hashing on their [official blog][manifest].
24+
///
25+
/// [moonshadow565-rman-rchunk]: https://github.com/moonshadow565/rman/blob/master/lib/rlib/rchunk.hpp
26+
/// [manifest]: https://technology.riotgames.com/news/supercharging-data-delivery-new-league-patcher
727
pub chunking_version: u8,
28+
/// Minimum chunk size.
29+
///
30+
/// For more info on what this does, see [Riot's blog][manifest] or [FastCDC pdf][fast-cdc].
31+
///
32+
/// [manifest]: https://technology.riotgames.com/news/supercharging-data-delivery-new-league-patcher
33+
/// [fast-cdc]: https://www.usenix.org/system/files/conference/atc16/atc16-paper-xia.pdf
834
pub min_chunk_size: u32,
35+
/// Chunk size.
36+
///
37+
/// For more info on what this does, see [Riot's blog][manifest] or [FastCDC pdf][fast-cdc].
38+
///
39+
/// [manifest]: https://technology.riotgames.com/news/supercharging-data-delivery-new-league-patcher
40+
/// [fast-cdc]: https://www.usenix.org/system/files/conference/atc16/atc16-paper-xia.pdf
941
pub chunk_size: u32,
42+
/// Maximum chunk size.
43+
///
44+
/// For more info on what this does, see [Riot's blog][manifest] or [FastCDC pdf][fast-cdc].
45+
///
46+
/// [manifest]: https://technology.riotgames.com/news/supercharging-data-delivery-new-league-patcher
47+
/// [fast-cdc]: https://www.usenix.org/system/files/conference/atc16/atc16-paper-xia.pdf
1048
pub max_chunk_size: u32,
1149
}
1250

‎src/error.rs

+68
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,99 @@
11
use thiserror::Error;
22

3+
/// Alias for a [`Result`][core::result::Result] with the error type [`ManifestError`].
34
pub type Result<T> = core::result::Result<T, ManifestError>;
45

6+
/// This enum represents all possible errors that may occur when parsing a file.
57
#[derive(Error, Debug)]
68
pub enum ManifestError {
9+
/// The error was caused by a failure to seek to a desired offset.
10+
///
11+
/// This error occurs when [`Seek`][std::io::Seek] fails.
712
#[error("Could not seek to desired position. Error: {0}")]
813
SeekError(std::io::Error),
14+
/// The error was caused by invalid magic bytes.
15+
///
16+
/// This error occurs when the first four bytes (magic bytes) do not equal `0x52`, `0x4D`,
17+
/// `0x41` and `0x4E` (or `R`, `M`, `A`, `N` in ascii) respectively.
18+
///
19+
/// Usually caused by providing a file that is not a Riot Manifest file.
920
#[error("Invalid magic bytes (expected: \"0x4E414D52\", was: \"{0:#010x}\").")]
1021
InvalidMagicBytes(u32),
22+
/// The error was caused by invalid major version.
23+
///
24+
/// This error occurs when the major version in the file header doesn't equal 2.
25+
///
26+
/// Should only occur if the manifest format gets a major change or an update, Parser
27+
/// may no longer function if this happens.
28+
///
29+
/// NOTE: The feature `version_error` must be enabled for this error to occur.
1130
#[error("Unsupported major version (expected: \"2\", was: \"{0}\").")]
1231
#[cfg(feature = "version_error")]
1332
InvalidMajor(u8),
33+
/// The error was caused by invalid minor version.
34+
///
35+
/// This error occurs when the minor version in the file header doesn't equal 0.
36+
///
37+
/// Should only occur if the manifest format gets a minor change or an update. Parser
38+
/// should still be functional if this happens,
39+
///
40+
/// NOTE: The feature `version_error` must be enabled for this error to occur.
1441
#[error("Unsupported minor version (expected: \"0\", was: \"{0}\").")]
1542
#[cfg(feature = "version_error")]
1643
InvalidMinor(u8),
44+
/// The error was caused by an invalid offset.
45+
///
46+
/// This error occurs when the offset is smaller or larger than the file itself.
47+
///
48+
/// Should never happen for official, Riot-made manifests.
1749
#[error("Offset ({0}) is larger than the total file size.")]
1850
InvalidOffset(u32),
51+
/// The error was caused by compressed size being too large.
52+
///
53+
/// This error occurs when the compressed size is larger than the file itself.
54+
///
55+
/// Should never happen for official, Riot-made manifests.
1956
#[error("Compressed size ({0}) is larger than the total file size.")]
2057
CompressedSizeTooLarge(u32),
58+
/// The error was caused by a failure to read or write bytes on an IO stream.
59+
///
60+
/// This error occurs when [`read_exact`][std::io::Read::read_exact], any `read_` method in
61+
/// [`byteorder::ReadBytesExt`], or [`File::open`][std::fs::File::open] fails.
62+
///
63+
/// Usually caused by invalid or inaccessible path or unexpected eof when parsing a header.
2164
#[error("{0}")]
2265
IoError(#[from] std::io::Error),
66+
/// The error was caused by a failure to convert from one number type to another.
67+
///
68+
/// This error occurs when the conversion from or into [`usize`] fails.
69+
///
70+
/// Should never fail on 32-bit (or higher) targets.
2371
#[error("Conversion failed. Error: {0}")]
2472
ConversionFailure(#[from] std::num::TryFromIntError),
73+
/// The error was caused by a failure to decompress zstd data.
74+
///
75+
/// This error occurs when [`decompress`][zstd::bulk::decompress] fails.
76+
///
77+
/// Should never happen for official, Riot-made manifests.
2578
#[error("{0}")]
2679
ZstdDecompressError(std::io::Error),
80+
/// The error was caused by a failure to parse [`FileEntry`][crate::entries::FileEntry] into
81+
/// [`File`][crate::File].
82+
///
83+
/// This error occurs when either a [`directory_id`](crate::entries::FileEntry::directory_id)
84+
/// or [`parent_id`](crate::entries::DirectoryEntry::parent_id) points to an invalid
85+
/// [`DirectoryEntry`][crate::entries::DirectoryEntry] or when
86+
/// [`chunk_id`](crate::entries::FileEntry::chunk_ids) refers to an invalid
87+
/// [`ChunkEntry`][crate::entries::ChunkEntry].
88+
///
89+
/// Should never happen for official, Riot-made manifests.
2790
#[error("{0}")]
2891
FileParseError(String),
92+
/// The error was caused by an invalid flatbuffer.
93+
///
94+
/// This error occurs when a flatbuffer fails to verify.
95+
///
96+
/// Should never happen for official, Riot-made manifests.
2997
#[error("{0}")]
3098
FlatbufferError(#[from] flatbuffers::InvalidFlatbuffer),
3199
}

‎src/file.rs

+47
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,65 @@ use std::collections::HashMap;
33
use crate::entries::FileEntry;
44
use crate::{ManifestError, Result};
55

6+
/// Single file object.
7+
///
8+
/// Represents a file and it's properties that can be downloaded and written to a file system.
69
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
710
#[derive(Debug, Default, Clone, PartialEq, Eq)]
811
pub struct File {
12+
/// Id of the file.
913
pub id: u64,
14+
/// File name.
1015
pub name: String,
16+
/// Permissions for the given file.
1117
pub permissions: u8,
18+
/// Size of the file entry in bytes.
1219
pub size: u32,
20+
/// Absolute path to the file, where root is one of the
21+
/// [directory entries][crate::entries::DirectoryEntry].
1322
pub path: String,
23+
/// Symbolic link of the file.
1424
pub symlink: String,
25+
/// A vector of applicable languages.
1526
pub languages: Vec<String>,
1627
#[allow(dead_code)]
1728
chunks: Vec<(u64, u64, u32, u32)>,
1829
}
1930

2031
impl File {
32+
/// Parses [`FileEntry`] into a [`File`] object.
33+
///
34+
/// First parameter is a [`FileEntry`] that is parsed into a [`File`], the other three are
35+
/// [`HashMap`]s used for fast lookups for the required data.
36+
///
37+
/// Here is how they are structured:
38+
/// - Parameter `language_entries` is a [`HashMap`] where the key is a
39+
/// [language id](crate::entries::LanguageEntry::id) and the value is a
40+
/// [language name](crate::entries::LanguageEntry::name).
41+
///
42+
/// - Parameter `directories` is a [`HashMap`] where the key is a
43+
/// [directory id](crate::entries::DirectoryEntry::id) and the value is a tuple of:
44+
/// - [directory name](crate::entries::DirectoryEntry::name)
45+
/// - and [parent directory id](crate::entries::DirectoryEntry::parent_id).
46+
///
47+
/// - Parameter `chunk_entries` is a [`HashMap`] where the key is a
48+
/// [chunk id](crate::entries::ChunkEntry::id) and the value is a tuple of:
49+
/// - [bundle id](crate::entries::BundleEntry::id),
50+
/// - offset in bundle (to this specific chunk),
51+
/// - [uncompressed size](crate::entries::ChunkEntry::uncompressed_size)
52+
/// - and [compressed size](crate::entries::ChunkEntry::compressed_size).
53+
///
54+
/// [`File`]: crate::File
55+
/// [`FileEntry`]: crate::entries::FileEntry
56+
///
57+
/// # Errors
58+
///
59+
/// If a directory with [provided id](crate::entries::FileEntry::directory_id) or
60+
/// [parent id](crate::entries::DirectoryEntry::parent_id) does not exist within the
61+
/// `directories` [`HashMap`], or if a chunk with
62+
/// [chunk id](crate::entries::FileEntry::chunk_ids) does not exist within the `chunk_entries`
63+
/// [`HashMap`], the error [`FileParseError`][crate::ManifestError::FileParseError] is
64+
/// returned.
2165
pub fn parse(
2266
file: &FileEntry,
2367
language_entries: &HashMap<u8, String>,
@@ -83,6 +127,9 @@ impl File {
83127
}
84128

85129
impl File {
130+
/// Function to download the associated file contents.
131+
///
132+
/// Currently unimplemented.
86133
pub fn download(&self) {
87134
unimplemented!("downloading not yet implemented");
88135
}

‎src/lib.rs

+99-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,105 @@
1-
// #![deny(missing_docs)]
1+
#![deny(missing_docs)]
22
#![deny(missing_debug_implementations)]
33
#![deny(clippy::all, clippy::unwrap_used)]
44

5+
//! # rman
6+
//!
7+
//! The `rman` crate provides a convenient api for parsing and downloading
8+
//! [manifest files][manifest]. This format is specific to games made by [Riot Games][riot-games].
9+
//!
10+
//! Crates name is derived from the 4 magic bytes at the beginning of the `.manifest` file,
11+
//! which correspond to `R`, `M`, `A`, `N` ascii characters. `RMAN` probably stands for
12+
//! Riot Manifest (or a variation of the two words).
13+
//!
14+
//! # Usage
15+
//!
16+
//! This crate is on [crates.io][rman-crates-io]. To use it, just add the dependency `rman` to the
17+
//! `Cargo.toml` file.
18+
//!
19+
//! ```toml
20+
//! [dependencies]
21+
//! rman = "0.1"
22+
//! ```
23+
//!
24+
//! # Example: parsing a manifest file from path
25+
//!
26+
//! Easiest way to parse a manifest file is to just provide a path to it's location.
27+
//!
28+
//! [`from_path`][crate::RiotManifest::from_path] calls [`File::open`][std::fs::File::open]
29+
//! internally, so everything from [`&str`][str] to [`PathBuf`][std::path::PathBuf] is a valid
30+
//! argument.
31+
//!
32+
//! ```
33+
//! use rman::RiotManifest;
34+
//!
35+
//! let path = "file.manifest";
36+
//! # let path = concat!(env!("OUT_DIR"), "/valid.manifest");
37+
//!
38+
//! let manifest = RiotManifest::from_path(path).unwrap();
39+
//!
40+
//! assert_eq!(manifest.data.files.len(), 1);
41+
//! ```
42+
//!
43+
//! # Example: parsing a manifest file from reader
44+
//!
45+
//! If you read the file from another source, or you already have the file in a reader,
46+
//! you can instead call [`from_reader`](crate::RiotManifest::from_reader) to parse the file.
47+
//!
48+
//! Internally, this is what [`from_path`](crate::RiotManifest::from_path) calls, so the response
49+
//! of the two functions should be identical.
50+
//!
51+
//! ```
52+
//! use std::fs;
53+
//! use std::io::BufReader;
54+
//!
55+
//! use rman::RiotManifest;
56+
//!
57+
//! let path = "file.manifest";
58+
//! # let path = concat!(env!("OUT_DIR"), "/valid.manifest");
59+
//! let file = fs::File::open(path).unwrap();
60+
//! let mut reader = BufReader::new(file);
61+
//!
62+
//! let manifest = RiotManifest::from_reader(&mut reader).unwrap();
63+
//!
64+
//! assert_eq!(manifest.data.files.len(), 1);
65+
//! ```
66+
//!
67+
//! # Scope
68+
//!
69+
//! This crate:
70+
//! - reads the `.manifest` file, and verifies it's a valid `rman` file,
71+
//! - decompresses the containing data which was compressed using [zstd][zstd],
72+
//! - parses the decompressed [FlatBuffer][flatbuffers] data,
73+
//! - stores all of the parsed data on [`ManifestData`][crate::ManifestData],
74+
//! - combines the data into a vector of downloadable [`File`][crate::File]s,
75+
//! - provides a function to [`download`][crate::File::download] specific files.
76+
//!
77+
//! This crate doesn't:
78+
//! - generate a `.manifest` file,
79+
//! - create or parse chunks.
80+
//!
81+
//! # Feature: `default`
82+
//!
83+
//! By default, all of the features are disabled.
84+
//!
85+
//! # Feature: `version_error`
86+
//!
87+
//! If enabled, throws errors on unknown manifest versions, instead of continuing and assuming it
88+
//! works.
89+
//!
90+
//! # Feature: `serde`
91+
//!
92+
//! If enabled, all structs in [`entries`][crate::entries], as well as [`File`][crate::File]
93+
//! will implement [`Serialize`][serde-serialize] and [`Deserialize`][serde-deserialize].
94+
//!
95+
//! [flatbuffers]: https://github.com/google/flatbuffers
96+
//! [manifest]: https://technology.riotgames.com/news/supercharging-data-delivery-new-league-patcher
97+
//! [riot-games]: https://www.riotgames.com
98+
//! [rman-crates-io]: https://crates.io/crates/rman
99+
//! [serde-serialize]: https://docs.rs/serde/latest/serde/trait.Serialize.html
100+
//! [serde-deserialize]: https://docs.rs/serde/latest/serde/trait.Deserialize.html
101+
//! [zstd]: https://github.com/facebook/zstd
102+
5103
pub mod entries;
6104
mod error;
7105
mod file;

‎src/parser.rs

+62
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,43 @@ use log::debug;
1212

1313
use crate::{ManifestError, Result};
1414

15+
/// Main parser object.
16+
///
17+
/// Depending on the function you call, it either parses a manifest
18+
/// [from reader][crate::RiotManifest::from_reader] or [a file][crate::RiotManifest::from_path].
1519
#[derive(Debug, Default, Clone, PartialEq, Eq)]
1620
pub struct RiotManifest {
21+
/// Parsed file header data.
22+
///
23+
/// Stores information like [magic bytes](crate::Header::magic),
24+
/// [version](crate::Header::major), [flags](crate::Header::flags),
25+
/// [size](crate::Header::compressed_size), [offset](crate::Header::offset), etc.
1726
pub header: Header,
27+
/// Parsed flatbuffer data.
28+
///
29+
/// Stores all of the [flatbuffer entries][crate::entries], as well as the [parsed files][crate::File].
1830
pub data: ManifestData,
1931
}
2032

2133
impl RiotManifest {
34+
/// Loads data from a file and parses it.
35+
///
36+
/// This is just a convenience method that [opens a file][std::fs::File::open],
37+
/// [buffers it][std::io::BufReader] and calls [`RiotManifest::from_reader`].
38+
///
39+
/// # Errors
40+
///
41+
/// If reading a file fails, the error [`IoError`][crate::ManifestError::IoError] is
42+
/// returned.
43+
///
44+
/// If parsing fails, it propagates an error from [`RiotManifest::from_reader`].
45+
///
46+
/// # Examples
47+
///
48+
/// See
49+
/// [parsing a manifest file from path](index.html#example-parsing-a-manifest-file-from-path).
50+
///
51+
/// [`RiotManifest::from_reader`]: crate::RiotManifest::from_reader
2252
pub fn from_path<P>(path: P) -> Result<Self>
2353
where
2454
P: AsRef<Path>,
@@ -28,6 +58,38 @@ impl RiotManifest {
2858
Self::from_reader(&mut reader)
2959
}
3060

61+
/// Main parser method.
62+
///
63+
/// Brief overview on how parsing the manifest is done:
64+
/// - attempts to [parse the header][crate::Header::from_reader]
65+
/// - [seeks][std::io::Seek] to the [offset](crate::Header::offset)
66+
/// - reads [x amount](crate::Header::compressed_size) of bytes to buffer
67+
/// - [decompresses][zstd::bulk::decompress] read bytes
68+
/// - decompressed data is a [flatbuffer binary], that is then
69+
/// [parsed][crate::ManifestData::parse].
70+
///
71+
/// # Errors
72+
///
73+
/// If parsing the header fails, it propagates an error from
74+
/// [`Header::from_reader`][crate::Header::from_reader].
75+
///
76+
/// If seeking to offset fails, the error [`SeekError`][crate::ManifestError::SeekError] is
77+
/// returned.
78+
///
79+
/// If converting [`compressed_size`](crate::Header::compressed_size) or
80+
/// [`uncompressed_size`](crate::Header::uncompressed_size) to [`usize`] fails, the error
81+
/// [`ConversionFailure`][crate::ManifestError::ConversionFailure] is returned.
82+
///
83+
/// If reading compressed flatbuffer data fails, the error
84+
/// [`IoError`][crate::ManifestError::IoError] is returned.
85+
///
86+
/// If zstd decompression fails, the error
87+
/// [`ZstdDecompressError`][crate::ManifestError::ZstdDecompressError] is returned.
88+
///
89+
/// If parsing flatbuffer binary fails, it propagates an error from
90+
/// [`ManifestData::parse`][crate::ManifestData::parse].
91+
///
92+
/// [flatbuffer binary]: https://github.com/ev3nvy/rman-schema
3193
pub fn from_reader<R>(reader: &mut R) -> Result<Self>
3294
where
3395
R: Read + Seek,

‎src/parser/header.rs

+55
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,74 @@ use log::{debug, info, warn};
55

66
use crate::{ManifestError, Result};
77

8+
/// File header.
89
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
910
pub struct Header {
11+
/// Magic bytes of the file.
12+
///
13+
/// They are stored as [`u32`] and not as a string, which also means that it reads `RMAN` in
14+
/// reverse because of endianness.
1015
pub magic: u32,
16+
/// Major version of the manifest format.
17+
///
18+
/// So far, it's only ever been 2.
19+
///
20+
/// NOTE: The library logs if the version differs from 2 using the [log crate][log].
1121
pub major: u8,
22+
/// Minor version of the manifest format.
23+
///
24+
/// So far, it's only ever been 0.
25+
///
26+
/// NOTE: The library logs if the version differs from 0 using the [log crate][log].
1227
pub minor: u8,
28+
/// Manifest flags (no idea what any of them mean or do).
1329
pub flags: u16,
30+
/// Offset to the compressed flatbuffer data.
1431
pub offset: u32,
32+
/// Size of the parsed flatbuffer schema before decompression.
1533
pub compressed_size: u32,
34+
/// Manifest id.
1635
pub manifest_id: u64,
36+
/// Size of the parsed flatbuffer schema after decompression.
1737
pub uncompressed_size: u32,
1838
}
1939

2040
impl Header {
41+
/// Main header parser method.
42+
///
43+
/// This method tries to parse the file header, and does some basic checks that it is indeed a
44+
/// Riot Manifest file.
45+
///
46+
/// It checks the [magic bytes](Header::magic), [version](Header::major),
47+
/// [offset](Header::offset) and [compressed size](Header::compressed_size).
48+
///
49+
/// # Errors
50+
///
51+
/// If converting file size from [`usize`] fails, the error
52+
/// [`ConversionFailure`][crate::ManifestError::ConversionFailure] is returned.
53+
///
54+
/// If seeking to start (rewinding) fails, the error
55+
/// [`SeekError`][crate::ManifestError::SeekError] is returned.
56+
///
57+
/// If reading from io stream fails, the error [`IoError`][crate::ManifestError::IoError] is
58+
/// returned.
59+
///
60+
/// If magic bytes do not equal to `R`, `M`, `A` and `N`, the error
61+
/// [`InvalidMagicBytes`][crate::ManifestError::InvalidMagicBytes] is returned.
62+
///
63+
/// If major version does not equal 2, and the feature
64+
/// [`version_error`](index.html#feature-version_error) is enabled, the error
65+
/// [`InvalidMajor`][crate::ManifestError::InvalidMajor] is returned.
66+
///
67+
/// If minor version does not equal 0, and the feature
68+
/// [`version_error`](index.html#feature-version_error) is enabled, the error
69+
/// [`InvalidMinor`][crate::ManifestError::InvalidMinor] is returned.
70+
///
71+
/// If offset is smaller or larger than the file, the error
72+
/// [`InvalidOffset`][crate::ManifestError::InvalidOffset] is returned.
73+
///
74+
/// If compressed_size is smaller or larger than the file, the error
75+
/// [`CompressedSizeTooLarge`][crate::ManifestError::CompressedSizeTooLarge] is returned.
2176
pub fn from_reader<R>(reader: &mut R) -> Result<Self>
2277
where
2378
R: Read + Seek,

‎src/parser/manifest.rs

+24
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,22 @@ use crate::generated::rman::root_as_manifest;
55
use crate::File;
66
use crate::Result;
77

8+
/// Stores all of the flatbuffer data, as well as the parsed files.
89
#[derive(Debug, Default, Clone, PartialEq, Eq)]
910
pub struct ManifestData {
11+
/// Vector of [bundle entries][crate::entries::BundleEntry].
1012
pub bundle_entries: Vec<BundleEntry>,
13+
/// Vector of [directory entries][crate::entries::DirectoryEntry].
1114
pub directory_entries: Vec<DirectoryEntry>,
15+
/// Vector of [file entries][crate::entries::FileEntry].
1216
pub file_entries: Vec<FileEntry>,
17+
/// Vector of [key entries][crate::entries::KeyEntry].
1318
pub key_entries: Vec<KeyEntry>,
19+
/// Vector of [language entries][crate::entries::LanguageEntry].
1420
pub language_entries: Vec<LanguageEntry>,
21+
/// Vector of [param entries][crate::entries::ParamEntry].
1522
pub param_entries: Vec<ParamEntry>,
23+
/// Vector of [files][crate::File].
1624
pub files: Vec<File>,
1725
}
1826

@@ -28,6 +36,22 @@ macro_rules! map_vector {
2836
}
2937

3038
impl ManifestData {
39+
/// Main flatbuffer parser method.
40+
///
41+
/// This method tries to parse the entire flatbuffer binary.
42+
///
43+
/// Yes, I know that a huge advantage of flatbuffers is the fact that they don't need to be
44+
/// parsed. However parsing the data in our case allows us to more easly explore it's
45+
/// contents and provides permanent objects so that the buffer can be discarded. Not to
46+
/// mention the fact that it's still plenty fast. :^)
47+
///
48+
/// # Errors
49+
///
50+
/// If verifying the flatbuffer fails, the error
51+
/// [`FlatbufferError`][crate::ManifestError::FlatbufferError] is returned.
52+
///
53+
/// If parsing the [`File`][crate::File] fails, it propagates an error from
54+
/// [`File::parse`][crate::File::parse].
3155
pub fn parse(bytes: &[u8]) -> Result<Self> {
3256
let manifest = root_as_manifest(bytes)?;
3357

0 commit comments

Comments
 (0)
Please sign in to comment.