1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
//! A library to parse (and maybe one day write) Sphinx inventory files
//! for referencing other documentation pages that use Sphinx.
//!
//! In contraty to Sphinx itself this library parses the data using a
//! combinator parser, instead of a regex, which has better performance
//! and better error reporting.
//!
//! This library was originally made for use in [ `snakedown` ]( https://crates.io/crates/snakedown )
//! but an effort has been made to make it more generally useful.
//!
//! ### Disclaimer
//! The Sphinx inventory format doesn't have a formal specification.
//! What follows are just the rules that we (and others) have
//! inferred from files we've seen in the wild. We try to be as correct as possible.
//! That said, we can't be guaranteed to be correct. If you find any errors, or have a valid file we
//! can't parse please open an issue!
//!
//! Currently only v2 is supported.
//!
//! ## Usage
//!
//! The main entry points of this create are the the [`InventoryHeader`] and [`SphinxReference`] data
//! structs and the [`SphinxInventoryReader`] and [`SphinxInventoryWriter`]
//! structs to handle with them.
//!
//! The [`SphinxInventoryReader`] and [`SphinxInventoryWriter`] can work with any struct that
//! immplements [`std::io::Read`] and [`std::io::Write`] respectively. These are internally buffered
//! so you do not have to wrap them yourself.
//!
//! When interacting with real `objects.inv` files in the wild you will most likely use the base
//! reader and writer struct, but both also have a `PlainText` variant. The only difference is that
//! the plain text versions don't encode/decode the data in zlib like the files do. This is mostly
//! useful for debugging/testing. In the following examples we will use the plain text versions and
//! the [`std::io::Cursor`] to make it easier to display the results, but the code should work
//! basically unchanged by switching to a [`std::fs::File`] and the base readers and writers.
//!
//! ## Examples
//!
//!
//! ```
//! # use sphinx_inv::*;
//! # use std::fs::File;
//! # use std::io::{Read, Write, Cursor};
//! # use pretty_assertions::assert_eq;
//! #
//! let header = InventoryHeader::new("Sphinx Inv", "0.2.0");
//! let join_reference = SphinxReference::new(
//! "str.join".to_string(),
//! SphinxType::Python(PyRole::Method),
//! None,
//! "library/stdtypes.html#$".to_string(),
//! None);
//! let lower_reference = SphinxReference::new(
//! "str.lower".to_string(),
//! SphinxType::Python(PyRole::Method),
//! None,
//! "library/stdtypes.html#$".to_string(),
//! None);
//!
//! let mut buffer = Vec::new();
//!
//! let mut cursor = Cursor::new(buffer);
//! // the capacity is just to preallocate the internal buffer, it can be anything
//! let mut writer = PlainTextSphinxInventoryWriter::from_header(&header, 2);
//!
//!
//! // add the references to the writer
//! writer.add_reference(&join_reference);
//! writer.add_reference(&lower_reference);
//!
//! // add_reference on it's own only adds it to the internal buffer
//! // nothing actually happens until you call [`SphinxInventoryWriter::finalize`]
//! writer.finalize(&mut cursor).unwrap();
//!
//! let written = String::from_utf8(cursor.into_inner()).unwrap();
//!
//! assert_eq!(&written, "# Sphinx inventory version 2
//! ## Project: Sphinx Inv
//! ## Version: 0.2.0
//! ## The remainder of this file is compressed using zlib.
//! str.join py:method 1 library/stdtypes.html#$ -
//! str.lower py:method 1 library/stdtypes.html#$ -
//! ");
//!
//! let mut cursor = Cursor::new( written);
//!
//! let mut reader = PlainTextSphinxInventoryReader::from_reader(cursor).unwrap();
//!
//! assert_eq!(&header, reader.header());
//!
//! assert_eq!(reader.next().unwrap().unwrap(), join_reference);
//! assert_eq!(reader.next().unwrap().unwrap(), lower_reference);
//!
//! ```
//!
//!
//! ## Format Description
//!
//! As noted by Skinn et al. currently, a inventory file (in the v2 format) has 2 parts:
//! the header and the body.
//!
//! ### Header description
//!
//! The header needs to be of the following format:
//! ```txt
//! # Sphinx inventory version 2
//! # Project: <project name>
//! # Version: <full version number>
//! # The remainder of this file is compressed using zlib.
//! ```
//!
//! #### Caveats:
//! 1. The first line has to match exactly
//! 2. version number should not contain a leading `v`
//! 3. currently zlib is the only compression method that Sphinx supports.
//! 4. Though it is not specified, it is expected that the text mentioned above is in ascii.
//! The project name can contain unicode, but the text in the example must match exactly[^*].
//! 5. While Sphinx itself allows for userdefinable domains and roles, this is not possible for this
//! library due to being complied. However we have made an attempt to include as many domains and
//! roles we found out in the wild. If you are missing any, please submit a feature request or pull
//! request to add it!
//!
//! For more indepth explanation of the format, please see
//! [spobjinv](https://sphobjinv.readthedocs.io/en/stable/syntax.html)
//!
//! [^*]: technically it doesn't as long as the byte offsets are the same since the Sphinx
//! implementation just skips a known amount of bytes, but this is a impl detail so
//! we recommend that the format is still followed
//!
//!
//!### Body format
//!
//! The remaining body of the file after the header must be compressed with zlib.
//! In the decompressed data each line should have the following format:
//!
//! ```txt
//! {name} {domain}:{role} {priority} {uri} {dispname}
//! ```
//!
//! Specifically it must match this regex:
//! `(.+?)\s+(\S+)\s+(-?\d+)\s+?(\S*)\s+(.*)`
//!
//! For example:
//! ```txt
//! str.join py:method 1 library/stdtypes.html#$ -
//! ```
//!
/// The main error type returned by this crate
pub use SphinxInvError;
/// Error type when parsing either the header or a record fails.
pub use SphinxParseError;
/// Error type when there is not enough input from the underlying reader
/// to properly parse the header
pub use MissingHeaderComponent;
/// Struct for handling the metadata of an inventory such as project name and version
pub use InventoryHeader;
/// The main entrypoint to this crate, used to read and parse sphinx reference data
pub use SphinxInventoryReader;
/// plaintext version of [`SphinxInventoryReader`] mainly used for testing and demoing
pub use PlainTextSphinxInventoryReader;
/// The main data struct of this crate with the necessary information to link to external
pub use SphinxReference;
/// The main entrypoint to this crate, used to write and format sphinx reference data
pub use SphinxInventoryWriter;
/// plaintext version of [`SphinxInventoryWriter`] mainly used for testing and demoing
pub use PlainTextSphinxInventoryWriter;
/// type used to parse `{domain}:{roles}` information provided by Sphinx used to disembguate
/// between object types and names between different languages
pub use SphinxType;
pub use ;