1#[macro_use]
2extern crate log;
3#[macro_use]
4extern crate serde_derive;
5#[macro_use]
6extern crate bitflags;
7#[macro_use]
8extern crate pijul_macros;
9#[macro_use]
10extern crate thiserror;
11#[cfg(test)]
12#[macro_use]
13extern crate quickcheck;
14
15pub mod alive;
16pub mod apply;
17pub mod change;
18pub mod changestore;
19mod diff;
20pub mod fs;
21pub mod missing_context;
22pub mod output;
23pub mod path;
24pub mod pin;
25pub mod pristine;
26pub mod record;
27pub mod small_string;
28mod text_encoding;
29pub mod unrecord;
30mod vector2;
31pub mod vertex_buffer;
32pub mod working_copy;
33
34pub mod key;
35
36#[cfg(test)]
37mod tests;
38
39use std::sync::{LazyLock, Mutex};
40
41pub const DOT_DIR: &str = ".pijul";
42pub const DEFAULT_CHANNEL: &str = "main";
43
44#[derive(Debug, Error)]
45#[error("Parse error: {:?}", s)]
46pub struct ParseError {
47 s: String,
48}
49
50#[derive(Debug, Error, Serialize, Deserialize)]
51pub enum RemoteError {
52 #[error("Repository not found: {}", url)]
53 RepositoryNotFound { url: String },
54 #[error("Channel {} not found for repository {}", channel, url)]
55 ChannelNotFound { channel: String, url: String },
56 #[error("Ambiguous path: {}", path)]
57 AmbiguousPath { path: String },
58 #[error("Path not found: {}", path)]
59 PathNotFound { path: String },
60 #[error("Change not found: {}", change)]
61 ChangeNotFound { change: String },
62}
63
64pub use crate::apply::Workspace as ApplyWorkspace;
65pub use crate::apply::{ApplyError, LocalApplyError, apply_change_arc};
66pub use crate::diff::DEFAULT_SEPARATOR;
67pub use crate::fs::{FsError, WorkingCopyIterator};
68pub use crate::output::{Archive, Conflict};
69pub use crate::pristine::{
70 ArcTxn, Base32, ChangeId, ChannelMutTxnT, ChannelRef, ChannelTxnT, DepsTxnT, EdgeFlags,
71 GraphTxnT, Hash, Inode, Merkle, MutTxnT, OwnedPathId, RemoteRef, ResultOptionalExt, TreeTxnT,
72 TxnT, Vertex,
73};
74pub use crate::record::Builder as RecordBuilder;
75pub use crate::record::{Algorithm, InodeUpdate};
76pub use crate::text_encoding::Encoding;
77pub use crate::unrecord::UnrecordError;
78
79pub type Hasher = std::collections::hash_map::RandomState;
80
81pub type HashMap<K, V> = std::collections::HashMap<K, V, Hasher>;
82pub type HashSet<K> = std::collections::HashSet<K, Hasher>;
83
84impl<
85 T: ::sanakirja::LoadPage<Error = ::sanakirja::Error>
86 + ::sanakirja::RootPageMut
87 + ::sanakirja::Commit
88 + ::sanakirja::AllocPage<Error = ::sanakirja::Error>,
89> MutTxnTExt for pristine::sanakirja::GenericTxn<T>
90{
91}
92impl<T: ::sanakirja::LoadPage<Error = ::sanakirja::Error> + ::sanakirja::RootPage> TxnTExt
93 for pristine::sanakirja::GenericTxn<T>
94{
95}
96
97pub fn commit<T: pristine::MutTxnT>(
98 txn: std::sync::Arc<std::sync::RwLock<T>>,
99) -> Result<(), T::GraphError> {
100 let txn = match std::sync::Arc::try_unwrap(txn) {
101 Ok(txn) => txn.into_inner().unwrap(),
102 _ => {
103 unreachable!()
104 }
105 };
106 txn.commit()
107}
108
109pub trait MutTxnTExt: pristine::MutTxnT {
110 #[allow(clippy::type_complexity)]
111 fn apply_root_change_if_needed<C: changestore::ChangeStore, R: rand::Rng>(
112 &mut self,
113 changes: &C,
114 channel: &ChannelRef<Self>,
115 rng: R,
116 ) -> Result<
117 Option<(pristine::Hash, u64, pristine::Merkle)>,
118 crate::apply::ApplyError<C::Error, Self>,
119 > {
120 crate::apply::apply_root_change(self, channel, changes, rng)
121 }
122
123 fn apply_change_ws<C: changestore::ChangeStore>(
124 &mut self,
125 changes: &C,
126 channel: &mut Self::Channel,
127 hash: &crate::pristine::Hash,
128 workspace: &mut ApplyWorkspace,
129 ) -> Result<(u64, pristine::Merkle), crate::apply::ApplyError<C::Error, Self>> {
130 crate::apply::apply_change_ws(changes, self, channel, hash, workspace)
131 }
132
133 fn apply_change_rec_ws<C: changestore::ChangeStore>(
134 &mut self,
135 changes: &C,
136 channel: &mut Self::Channel,
137 hash: &crate::pristine::Hash,
138 workspace: &mut ApplyWorkspace,
139 ) -> Result<(), crate::apply::ApplyError<C::Error, Self>> {
140 crate::apply::apply_change_rec_ws(changes, self, channel, hash, workspace, false)
141 }
142
143 fn apply_change<C: changestore::ChangeStore>(
144 &mut self,
145 changes: &C,
146 channel: &mut Self::Channel,
147 hash: &pristine::Hash,
148 ) -> Result<(u64, pristine::Merkle), crate::apply::ApplyError<C::Error, Self>> {
149 crate::apply::apply_change(changes, self, channel, hash)
150 }
151
152 fn apply_change_rec<C: changestore::ChangeStore>(
153 &mut self,
154 changes: &C,
155 channel: &mut Self::Channel,
156 hash: &pristine::Hash,
157 ) -> Result<(), crate::apply::ApplyError<C::Error, Self>> {
158 crate::apply::apply_change_rec(changes, self, channel, hash, false)
159 }
160
161 fn apply_deps_rec<C: changestore::ChangeStore>(
162 &mut self,
163 changes: &C,
164 channel: &mut Self::Channel,
165 hash: &pristine::Hash,
166 ) -> Result<(), crate::apply::ApplyError<C::Error, Self>> {
167 crate::apply::apply_change_rec(changes, self, channel, hash, true)
168 }
169
170 fn apply_local_change_ws(
171 &mut self,
172 channel: &pristine::ChannelRef<Self>,
173 change: &change::Change,
174 hash: &pristine::Hash,
175 inode_updates: &HashMap<usize, InodeUpdate>,
176 workspace: &mut ApplyWorkspace,
177 ) -> Result<(u64, pristine::Merkle), crate::apply::LocalApplyError<Self>> {
178 crate::apply::apply_local_change_ws(self, channel, change, hash, inode_updates, workspace)
179 }
180
181 fn apply_local_change(
182 &mut self,
183 channel: &crate::pristine::ChannelRef<Self>,
184 change: &crate::change::Change,
185 hash: &pristine::Hash,
186 inode_updates: &HashMap<usize, InodeUpdate>,
187 ) -> Result<(u64, pristine::Merkle), crate::apply::LocalApplyError<Self>> {
188 crate::apply::apply_local_change(self, channel, change, hash, inode_updates)
189 }
190
191 fn apply_recorded<C: changestore::ChangeStore>(
192 &mut self,
193 channel: &mut pristine::ChannelRef<Self>,
194 recorded: record::Recorded,
195 changestore: &C,
196 ) -> Result<pristine::Hash, crate::apply::ApplyError<C::Error, Self>> {
197 let contents_hash = {
198 let mut hasher = pristine::Hasher::default();
199 hasher.update(&recorded.contents.lock()[..]);
200 hasher.finish()
201 };
202 let mut change = change::LocalChange {
203 offsets: change::Offsets::default(),
204 hashed: change::Hashed {
205 version: change::VERSION,
206 contents_hash,
207 changes: recorded
208 .actions
209 .into_iter()
210 .map(|rec| rec.globalize(self).unwrap())
211 .collect(),
212 metadata: Vec::new(),
213 dependencies: Vec::new(),
214 extra_known: Vec::new(),
215 header: change::ChangeHeader::default(),
216 },
217 unhashed: None,
218 contents: std::sync::Arc::try_unwrap(recorded.contents)
219 .unwrap()
220 .into_inner(),
221 };
222 let hash = changestore
223 .save_change(&mut change, |_, _| Ok(()))
224 .map_err(apply::ApplyError::Changestore)?;
225 apply::apply_local_change(self, channel, &change, &hash, &recorded.updatables)
226 .map_err(ApplyError::LocalChange)?;
227 Ok(hash)
228 }
229
230 fn unrecord<C: changestore::ChangeStore>(
231 &mut self,
232 changes: &C,
233 channel: &pristine::ChannelRef<Self>,
234 hash: &pristine::Hash,
235 salt: u64,
236 touched: &mut unrecord::TouchedInodes,
237 ) -> Result<bool, unrecord::UnrecordError<C::Error, Self>> {
238 unrecord::unrecord(self, channel, changes, hash, salt, touched)
239 }
240
241 fn touch_inodes<W: working_copy::WorkingCopy>(
242 &mut self,
243 working_copy: &W,
244 touched_inodes: &unrecord::TouchedInodes,
245 ) -> Result<(), unrecord::TouchError<W::Error, Self>> {
246 unrecord::touch_inodes(self, working_copy, touched_inodes)
247 }
248
249 fn add_file(&mut self, path: &str, salt: u64) -> Result<Inode, fs::FsError<Self>> {
253 fs::add_inode(self, None, path, false, salt)
254 }
255
256 fn add_dir(&mut self, path: &str, salt: u64) -> Result<Inode, fs::FsError<Self>> {
261 fs::add_inode(self, None, path, true, salt)
262 }
263
264 fn add(&mut self, path: &str, is_dir: bool, salt: u64) -> Result<Inode, fs::FsError<Self>> {
268 fs::add_inode(self, None, path, is_dir, salt)
269 }
270
271 fn move_file(&mut self, a: &str, b: &str, salt: u64) -> Result<(), fs::FsError<Self>> {
272 fs::move_file(self, a, b, salt)
273 }
274
275 fn remove_file(&mut self, a: &str) -> Result<(), fs::FsError<Self>> {
276 fs::remove_file(self, a)
277 }
278}
279
280pub trait TxnTExt: pristine::TxnT {
281 fn is_directory(&self, inode: pristine::Inode) -> Result<bool, Self::TreeError> {
282 fs::is_directory(self, inode).map_err(|e| e.0)
283 }
284
285 fn is_tracked(&self, path: &str) -> Result<bool, Self::TreeError> {
286 fs::is_tracked(self, path).map_err(|e| e.0)
287 }
288
289 fn iter_working_copy<'a>(&'a self) -> WorkingCopyIterator<'a, Self> {
290 fs::iter_working_copy(self, pristine::Inode::ROOT)
291 }
292
293 fn iter_graph_children<'txn, 'changes, P>(
294 &'txn self,
295 changes: &'changes P,
296 channel: &'txn Self::Channel,
297 key: pristine::Position<ChangeId>,
298 ) -> Result<fs::GraphChildren<'txn, 'changes, Self, P>, Self::GraphError>
299 where
300 P: changestore::ChangeStore,
301 {
302 fs::iter_graph_children(self, changes, self.graph(channel), key)
303 }
304
305 fn has_change(
306 &self,
307 channel: &pristine::ChannelRef<Self>,
308 hash: &pristine::Hash,
309 ) -> Result<Option<u64>, Self::GraphError> {
310 if let Some(cid) = pristine::GraphTxnT::get_internal(self, &hash.into()).map_err(|e| e.0)? {
311 self.get_changeset(self.changes(&channel.read()), cid)
312 .map_err(|e| e.0)
313 .map(|x| x.map(|x| u64::from_le(x.0)))
314 } else {
315 Ok(None)
316 }
317 }
318
319 fn is_alive(
320 &self,
321 channel: &Self::Channel,
322 a: &pristine::Vertex<pristine::ChangeId>,
323 ) -> Result<bool, Self::GraphError> {
324 pristine::is_alive(self, self.graph(channel), a).map_err(|e| e.0)
325 }
326
327 fn current_state(&self, channel: &Self::Channel) -> Result<pristine::Merkle, Self::GraphError> {
328 pristine::current_state(self, channel).map_err(|e| e.0)
329 }
330
331 fn log<'txn>(
332 &'txn self,
333 channel: &Self::Channel,
334 from: u64,
335 ) -> Result<Log<'txn, Self>, Self::GraphError> {
336 Ok(Log {
337 txn: self,
338 iter: pristine::changeid_log(self, channel, pristine::L64(from.to_le()))
339 .map_err(|e| e.0)?,
340 })
341 }
342
343 fn log_for_path<'channel, 'txn>(
344 &'txn self,
345 channel: &'channel Self::Channel,
346 pos: pristine::Position<pristine::ChangeId>,
347 from: u64,
348 ) -> Result<pristine::PathChangeset<'channel, 'txn, Self>, Self::GraphError> {
349 pristine::log_for_path(self, channel, pos, from).map_err(|e| e.0)
350 }
351
352 fn rev_log_for_path<'channel, 'txn>(
353 &'txn self,
354 channel: &'channel Self::Channel,
355 pos: pristine::Position<pristine::ChangeId>,
356 from: u64,
357 ) -> Result<pristine::RevPathChangeset<'channel, 'txn, Self>, Self::DepsError> {
358 pristine::rev_log_for_path(self, channel, pos, from).map_err(|e| e.0)
359 }
360
361 fn reverse_log<'txn>(
362 &'txn self,
363 channel: &Self::Channel,
364 from: Option<u64>,
365 ) -> Result<RevLog<'txn, Self>, Self::GraphError> {
366 Ok(RevLog {
367 txn: self,
368 iter: pristine::changeid_rev_log(self, channel, from.map(|x| pristine::L64(x.to_le())))
369 .map_err(|e| e.0)?,
370 })
371 }
372
373 #[allow(clippy::type_complexity)]
374 fn changeid_reverse_log<'txn>(
375 &'txn self,
376 channel: &Self::Channel,
377 from: Option<pristine::L64>,
378 ) -> Result<
379 pristine::RevCursor<
380 Self,
381 &'txn Self,
382 Self::RevchangesetCursor,
383 pristine::L64,
384 pristine::Pair<pristine::ChangeId, pristine::SerializedMerkle>,
385 >,
386 Self::GraphError,
387 > {
388 pristine::changeid_rev_log(self, channel, from).map_err(|e| e.0)
389 }
390
391 fn get_changes(
392 &self,
393 channel: &pristine::ChannelRef<Self>,
394 n: u64,
395 ) -> Result<Option<(pristine::Hash, pristine::Merkle)>, Self::GraphError> {
396 match self
397 .get_revchangeset(self.rev_changes(&channel.read()), &pristine::L64(n.to_le()))
398 .map_err(|e| e.0)?
399 {
400 Some(p) => Ok(Some((
401 self.get_external(&p.a)
402 .optional()
403 .map_err(|e| e.0)?
404 .unwrap()
405 .into(),
406 (&p.b).into(),
407 ))),
408 _ => Ok(None),
409 }
410 }
411
412 fn get_revchanges(
413 &self,
414 channel: &pristine::ChannelRef<Self>,
415 h: &pristine::Hash,
416 ) -> Result<Option<u64>, Self::GraphError> {
417 if let Some(h) = pristine::GraphTxnT::get_internal(self, &h.into()).map_err(|e| e.0)? {
418 self.get_changeset(self.changes(&channel.read()), h)
419 .map_err(|e| e.0)
420 .map(|x| x.map(|x| u64::from_le(x.0)))
421 } else {
422 Ok(None)
423 }
424 }
425
426 fn touched_files<'a>(
427 &'a self,
428 h: &pristine::Hash,
429 ) -> Result<Option<Touched<'a, Self>>, Self::DepsError> {
430 if let Some(id) = pristine::GraphTxnT::get_internal(self, &h.into()).map_err(|e| e.0)? {
431 Ok(Some(Touched {
432 txn: self,
433 iter: self.iter_rev_touched_files(id, None).map_err(|e| e.0)?,
434 id: *id,
435 }))
436 } else {
437 Ok(None)
438 }
439 }
440
441 #[allow(clippy::type_complexity)]
442 fn find_oldest_path<C: changestore::ChangeStore>(
443 &self,
444 changes: &C,
445 channel: &pristine::ChannelRef<Self>,
446 position: &pristine::Position<pristine::Hash>,
447 ) -> Result<Option<(String, bool)>, output::FileError<C::Error, Self>> {
448 let position = pristine::Position {
449 change: *pristine::GraphTxnT::get_internal(self, &position.change.into())?.unwrap(),
450 pos: position.pos,
451 };
452 match fs::find_path(changes, self, &channel.read(), false, position)? {
453 Some(a) => Ok(Some((a.path.join("/"), a.all_alive))),
454 _ => Ok(None),
455 }
456 }
457
458 #[allow(clippy::type_complexity)]
459 fn find_youngest_path<C: changestore::ChangeStore>(
460 &self,
461 changes: &C,
462 channel: &pristine::ChannelRef<Self>,
463 position: pristine::Position<pristine::Hash>,
464 ) -> Result<Option<(String, bool)>, output::FileError<C::Error, Self>> {
465 let position = pristine::Position {
466 change: *pristine::GraphTxnT::get_internal(self, &position.change.into())?.unwrap(),
467 pos: position.pos,
468 };
469 match fs::find_path(changes, self, &channel.read(), true, position)? {
470 Some(a) => Ok(Some((a.path.join("/"), a.all_alive))),
471 _ => Ok(None),
472 }
473 }
474
475 fn follow_oldest_path<C: changestore::ChangeStore>(
476 &self,
477 changes: &C,
478 channel: &pristine::ChannelRef<Self>,
479 path: &str,
480 ) -> Result<(pristine::Position<pristine::ChangeId>, bool), fs::FsErrorC<C::Error, Self>> {
481 fs::follow_oldest_path(changes, self, &channel.read(), path)
482 }
483
484 fn iter_adjacent<'txn>(
485 &'txn self,
486 graph: &'txn Self::Channel,
487 key: Vertex<pristine::ChangeId>,
488 min_flag: pristine::EdgeFlags,
489 max_flag: pristine::EdgeFlags,
490 ) -> Result<pristine::AdjacentIterator<'txn, Self>, pristine::TxnErr<Self::GraphError>> {
491 pristine::iter_adjacent(self, self.graph(graph), key, min_flag, max_flag)
492 }
493}
494
495impl<T: ChannelTxnT + TreeTxnT + DepsTxnT<DepsError = <T as GraphTxnT>::GraphError>> ArcTxn<T> {
496 #[allow(clippy::type_complexity)]
497 pub fn archive<C: changestore::ChangeStore, A: Archive>(
498 &self,
499 changes: &C,
500 channel: &pristine::ChannelRef<T>,
501 arch: &mut A,
502 ) -> Result<Vec<output::Conflict>, output::ArchiveError<C::Error, T, A::Error>> {
503 output::archive(changes, self, channel, &mut std::iter::empty(), arch)
504 }
505
506 pub fn archive_prefix<
507 'a,
508 C: changestore::ChangeStore,
509 I: Iterator<Item = &'a str>,
510 A: Archive,
511 >(
512 &self,
513 changes: &C,
514 channel: &pristine::ChannelRef<T>,
515 prefix: &mut I,
516 arch: &mut A,
517 ) -> Result<Vec<output::Conflict>, output::ArchiveError<C::Error, T, A::Error>> {
518 output::archive(changes, self, channel, prefix, arch)
519 }
520}
521
522impl<T: MutTxnT> ArcTxn<T> {
523 #[allow(clippy::type_complexity)]
524 pub fn archive_with_state<P: changestore::ChangeStore, A: Archive>(
525 &self,
526 changes: &P,
527 channel: &pristine::ChannelRef<T>,
528 state: &pristine::Merkle,
529 extra: &[pristine::Hash],
530 arch: &mut A,
531 salt: u64,
532 ) -> Result<Vec<output::Conflict>, output::ArchiveError<P::Error, T, A::Error>> {
533 self.archive_prefix_with_state(
534 changes,
535 channel,
536 state,
537 extra,
538 &mut std::iter::empty(),
539 arch,
540 salt,
541 )
542 }
543
544 #[allow(clippy::too_many_arguments)]
548 pub fn archive_prefix_with_state<
549 'a,
550 P: changestore::ChangeStore,
551 A: Archive,
552 I: Iterator<Item = &'a str>,
553 >(
554 &self,
555 changes: &P,
556 channel: &pristine::ChannelRef<T>,
557 state: &pristine::Merkle,
558 extra: &[pristine::Hash],
559 prefix: &mut I,
560 arch: &mut A,
561 salt: u64,
562 ) -> Result<Vec<output::Conflict>, output::ArchiveError<P::Error, T, A::Error>> {
563 let mut unrecord = Vec::new();
564 let mut found = false;
565 let mut txn = self.write();
566 for x in pristine::changeid_rev_log(&*txn, &channel.read(), None)? {
567 let (_, p) = x?;
568 let m: Merkle = (&p.b).into();
569 if &m == state {
570 found = true;
571 break;
572 } else {
573 unrecord.push(p.a)
574 }
575 }
576 debug!("unrecord = {:?}", unrecord);
577 if found {
578 for h in unrecord.iter() {
579 let h = txn.get_external(h).optional()?.unwrap().into();
580 unrecord::unrecord(&mut *txn, channel, changes, &h, salt, &mut HashSet::new())?;
581 }
582 {
583 let mut channel_ = channel.write();
584 for app in extra.iter() {
585 crate::apply::apply_change_rec(changes, &mut *txn, &mut channel_, app, false)?
586 }
587 }
588 std::mem::drop(txn);
589 output::archive(changes, self, channel, prefix, arch)
590 } else {
591 Err(output::ArchiveError::StateNotFound {
592 state: Box::new(*state),
593 })
594 }
595 }
596}
597
598pub struct Log<'txn, T: pristine::ChannelTxnT> {
599 txn: &'txn T,
600 iter: pristine::Cursor<
601 T,
602 &'txn T,
603 T::RevchangesetCursor,
604 pristine::L64,
605 pristine::Pair<pristine::ChangeId, pristine::SerializedMerkle>,
606 >,
607}
608
609impl<'txn, T: pristine::ChannelTxnT> Iterator for Log<'txn, T> {
610 type Item = Result<
611 (
612 u64,
613 (
614 &'txn pristine::SerializedHash,
615 &'txn pristine::SerializedMerkle,
616 ),
617 ),
618 T::GraphError,
619 >;
620 fn next(&mut self) -> Option<Self::Item> {
621 match self.iter.next() {
622 Some(Ok((n, p))) => {
623 let ext = match self.txn.get_external(&p.a).optional() {
624 Err(pristine::TxnErr(e)) => return Some(Err(e)),
625 Ok(None) => panic!("Unknown change {:?}", p),
626 Ok(Some(ext)) => ext,
627 };
628 Some(Ok((u64::from_le(n.0), (ext, &p.b))))
629 }
630 None => None,
631 Some(Err(e)) => Some(Err(e.0)),
632 }
633 }
634}
635
636pub struct RevLog<'txn, T: pristine::ChannelTxnT> {
637 txn: &'txn T,
638 iter: pristine::RevCursor<
639 T,
640 &'txn T,
641 T::RevchangesetCursor,
642 pristine::L64,
643 pristine::Pair<pristine::ChangeId, pristine::SerializedMerkle>,
644 >,
645}
646
647impl<'txn, T: pristine::ChannelTxnT> Iterator for RevLog<'txn, T> {
648 type Item = Result<
649 (
650 u64,
651 (
652 &'txn pristine::SerializedHash,
653 &'txn pristine::SerializedMerkle,
654 ),
655 ),
656 T::GraphError,
657 >;
658 fn next(&mut self) -> Option<Self::Item> {
659 match self.iter.next() {
660 Some(Ok((n, p))) => match self.txn.get_external(&p.a).optional() {
661 Ok(Some(ext)) => Some(Ok((u64::from_le(n.0), (ext, &p.b)))),
662 Err(e) => Some(Err(e.0)),
663 Ok(None) => panic!("Unknown change {:?}", p),
664 },
665 None => None,
666 Some(Err(e)) => Some(Err(e.0)),
667 }
668 }
669}
670
671pub struct Touched<'txn, T: pristine::DepsTxnT> {
672 txn: &'txn T,
673 iter: pristine::Cursor<
674 T,
675 &'txn T,
676 T::Rev_touched_filesCursor,
677 pristine::ChangeId,
678 pristine::Position<pristine::ChangeId>,
679 >,
680 id: pristine::ChangeId,
681}
682
683impl<
684 'txn,
685 T: pristine::DepsTxnT + pristine::GraphTxnT<GraphError = <T as pristine::DepsTxnT>::DepsError>,
686> Iterator for Touched<'txn, T>
687{
688 type Item = Result<pristine::Position<pristine::Hash>, T::DepsError>;
689 fn next(&mut self) -> Option<Self::Item> {
690 for x in self.iter.by_ref() {
691 let (cid, file) = match x {
692 Ok(x) => x,
693 Err(e) => return Some(Err(e.0)),
694 };
695 if *cid > self.id {
696 return None;
697 } else if *cid == self.id {
698 let change = match self.txn.get_external(&file.change).optional() {
699 Ok(Some(ext)) => ext,
700 Ok(None) => panic!("Unknown change {:?}", file.change),
701 Err(e) => return Some(Err(e.0)),
702 };
703 return Some(Ok(pristine::Position {
704 change: change.into(),
705 pos: file.pos,
706 }));
707 }
708 }
709 None
710 }
711}
712
713#[doc(hidden)]
714#[derive(Debug, Default, Clone)]
715pub struct Timers {
716 pub alive_output: std::time::Duration,
717 pub alive_graph: std::time::Duration,
718 pub alive_retrieve: std::time::Duration,
719 pub alive_contents: std::time::Duration,
720 pub alive_write: std::time::Duration,
721 pub record: std::time::Duration,
722 pub apply: std::time::Duration,
723 pub repair_context: std::time::Duration,
724 pub check_cyclic_paths: std::time::Duration,
725 pub find_alive: std::time::Duration,
726}
727
728pub static TIMERS: LazyLock<Mutex<Timers>> = LazyLock::new(|| {
729 Mutex::new(Timers {
730 alive_output: std::time::Duration::from_secs(0),
731 alive_graph: std::time::Duration::from_secs(0),
732 alive_retrieve: std::time::Duration::from_secs(0),
733 alive_contents: std::time::Duration::from_secs(0),
734 alive_write: std::time::Duration::from_secs(0),
735 record: std::time::Duration::from_secs(0),
736 apply: std::time::Duration::from_secs(0),
737 repair_context: std::time::Duration::from_secs(0),
738 check_cyclic_paths: std::time::Duration::from_secs(0),
739 find_alive: std::time::Duration::from_secs(0),
740 })
741});
742
743#[doc(hidden)]
744pub fn reset_timers() {
745 *TIMERS.lock().unwrap() = Timers::default();
746}
747#[doc(hidden)]
748pub fn get_timers() -> Timers {
749 TIMERS.lock().unwrap().clone()
750}
751
752pub(crate) fn get_valid_encoding(
753 enc: &chardetng::EncodingDetector,
754 tld: Option<&[u8]>,
755 allow_utf8: bool,
756 buffer: &[u8],
757) -> Option<&'static encoding_rs::Encoding> {
758 if let (encoding, true) = enc.guess_assess(tld, allow_utf8)
759 && let (s, e, false) = encoding.decode(buffer)
760 {
761 let reencoded = encoding.encode(&s).0;
762
763 if reencoded == buffer {
766 return Some(e);
767 } else if encoding == encoding_rs::UTF_8
768 && buffer.starts_with(b"\xef\xbb\xbf")
769 && buffer[3..] == *reencoded
770 {
771 return Some(e);
774 }
775 }
776 None
777}