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
//! # Berry
//!
//! A fast parser for Yarn Berry (v2+) lockfiles.
//!
//! ```
//! let src = concat!(
//! "# This file is generated by running \"yarn install\" inside your project.\n",
//! "# Manual changes might be lost - proceed with caution!\n\n",
//! "__metadata:\n version: 8\n cacheKey: 10\n\n",
//! "\"left-pad@npm:^1.3.0\":\n version: 1.3.0\n resolution: \"left-pad@npm:1.3.0\"\n",
//! " dependencies:\n \"@scope/dep\": \"npm:^2.0.0\"\n languageName: node\n linkType: hard\n",
//! );
//! let lockfile = berry::parse(src)?;
//! assert_eq!(lockfile.metadata().version, "8");
//! assert!(lockfile.remaining().is_empty());
//! for entry in &lockfile {
//! assert_eq!(entry.descriptors().first().unwrap().to_string(), "left-pad@npm:^1.3.0");
//! assert_eq!(entry.version(), Some("1.3.0"));
//! let dep = entry.dependencies().get("@scope/dep").unwrap();
//! assert_eq!(dep.ident().scope(), Some("@scope"));
//! assert_eq!(dep.range(), "npm:^2.0.0");
//! }
//! # Ok::<(), berry::Error>(())
//! ```
//!
//! The parse produces span tables (`u32` offsets into the text) instead of
//! owned strings or maps: a constant number of allocations, independent of the
//! number of entries, and none when [`Buffers`] are reused. Every accessor is
//! a borrowed, `Copy` view.
//!
//! Inputs: [`parse`] (`&str`), [`parse_bytes`] (`&[u8]`, validated as UTF-8)
//! and [`parse_file`] (a path; memory-mapped or read, see its docs for the
//! concurrent-modification contract of mapped files: Yarn rewrites
//! `yarn.lock` in place), each with a `_with`
//! variant that reuses [`Buffers`].
//!
//! Inputs of 2.5 MB and more are parsed in parallel on the current rayon
//! pool; the result is always identical to a sequential parse.
//! `RAYON_NUM_THREADS=1` (or a one-thread `ThreadPool::install`) makes every
//! parse sequential. A parse called from inside a rayon pool whose workers are
//! all busy with long tasks waits for them; latency-sensitive callers should
//! call it from outside the pool.
//!
//! ----
//! This project is not affiliated with Yarn or the Yarn team.
pub use ;
pub use FileError;
pub use ;
pub use ;
pub use ;
use Source;
use Plan;
/// Parse a lockfile.
///
/// Errors only for a missing/invalid header or `__metadata` block, an unknown
/// package property, an invalid `linkType`, or an input of 4 GiB or more;
/// any other text that is not an entry ends the parse (see
/// [`Lockfile::remaining`]).
/// [`parse`] reusing the storage in `buffers` (taken out of it; get it back
/// with [`Lockfile::into_buffers`]). On error the storage stays in `buffers`.
/// [`parse`] for input given as bytes, e.g. a file read with `std::fs::read`.
///
/// The bytes are validated as UTF-8 first (in parallel for large inputs,
/// like the parse), then parsed. Invalid UTF-8 is
/// [`ErrorKind::InvalidUtf8`] at the length of the longest valid prefix.
/// [`parse_bytes`] reusing the storage in `buffers` (see [`parse_with`]).
/// Read (or memory-map) and parse a lockfile file.
///
/// The returned [`Lockfile`] owns the text, so it is `'static`: it can
/// outlive `path` and be sent to another thread. Regular files of 256 KiB
/// or more are memory-mapped read-only and validated as UTF-8 in parallel;
/// smaller files and anything that is not a regular file (pipes, devices) are
/// read with `read(2)`. Errors carry the path and, for parse errors, the
/// line and column (`path:line:column: message`).
///
/// # Concurrent modification
///
/// A mapped file that is rewritten or truncated while the parse runs or while
/// the `Lockfile` is alive changes under the parsed views: they may then see
/// bytes that are not valid UTF-8 (undefined behavior), and reads past a new
/// end of file raise `SIGBUS`. Yarn itself writes `yarn.lock` in place
/// (`fs.writeFile`: `O_TRUNC` + write on the same inode), so this is what a
/// `yarn install` or `yarn add` that changes the lockfile does. Callers must
/// not run Yarn (or anything else that writes the file) on the project from
/// the start of `parse_file` until the `Lockfile` is dropped; drop it before
/// running Yarn. If that cannot be guaranteed, read the file into memory
/// yourself (`std::fs::read`) and parse that private copy with [`parse_bytes`].
/// [`parse_file`] reusing the storage in `buffers`: the span tables and, for
/// files that are read rather than mapped, the read buffer.
/// Test hook (not part of the API): validate `src` as UTF-8 in pieces of
/// about `piece` bytes (0: one piece). Must agree with `std::str::from_utf8`.
/// Test hook (not part of the API): parse with the body cut into chunks of
/// about `chunk_target` bytes, snapped to entry boundaries. The result must
/// be identical to [`parse`].
pub use PARALLEL_MIN as __PARALLEL_MIN;
/// Test hook (not part of the API): parse with chunks starting at exactly
/// these byte offsets (moved forward to a char boundary; offsets inside the
/// preamble, out of order or past the end are ignored). The result must be
/// identical to [`parse`].